From 7cc0499240c9a8c697e9828f7543625d64eb1f34 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Jan 2025 18:49:22 +0100 Subject: [PATCH] =?UTF-8?q?[Odie]=C2=A0Give=20Wapuu=20a=20chance=20(#98419?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Odie] Give Wapuu a chance * Fix thumbs down issue * Rename variable from removeDislike to removeDislikeStatus * Fix autoscroll when using the experiment * HasUserEverEscalated was lost during merge/conflicts * Fix TSC error * Separate logic for show extra contact options and show direct escalation link --- .../components/message/message-content.tsx | 8 +++++- .../components/message/messages-container.tsx | 12 ++++++--- .../src/components/message/user-message.tsx | 27 +++++++++++++++---- packages/odie-client/src/context/index.tsx | 17 ++++++++++++ .../src/data/use-send-odie-message.ts | 13 ++++++--- .../odie-client/src/hooks/use-auto-scroll.ts | 6 ++++- packages/odie-client/src/types.ts | 10 ++++++- 7 files changed, 79 insertions(+), 14 deletions(-) diff --git a/packages/odie-client/src/components/message/message-content.tsx b/packages/odie-client/src/components/message/message-content.tsx index 781444323eea4..567731d4b7593 100644 --- a/packages/odie-client/src/components/message/message-content.tsx +++ b/packages/odie-client/src/components/message/message-content.tsx @@ -1,6 +1,7 @@ import { useI18n } from '@wordpress/react-i18n'; import clsx from 'clsx'; import Markdown from 'react-markdown'; +import { useOdieAssistantContext } from '../../context'; import { zendeskMessageConverter } from '../../utils'; import ChatWithSupportLabel from '../chat-with-support'; import CustomALink from './custom-a-link'; @@ -24,6 +25,7 @@ export const MessageContent = ( { displayChatWithSupportLabel?: boolean; } ) => { const { __ } = useI18n(); + const { experimentVariationName } = useOdieAssistantContext(); const messageClasses = clsx( 'odie-chatbox-message', `odie-chatbox-message-${ message.role }`, @@ -35,6 +37,9 @@ export const MessageContent = ( { isNextMessageFromSameSender && 'next-chat-message-same-sender' ); + const stopConflatingNegativeRatingWithContactSupport = + experimentVariationName === 'give_wapuu_a_chance'; + const isMessageWithOnlyText = message.context?.flags?.hide_disclaimer_content || message.context?.question_tags?.inquiry_type === 'user-is-greeting'; @@ -97,7 +102,8 @@ export const MessageContent = ( {

) } - { message.type === 'dislike-feedback' && } + { ! stopConflatingNegativeRatingWithContactSupport && + message.type === 'dislike-feedback' && } { displayChatWithSupportLabel && } diff --git a/packages/odie-client/src/components/message/messages-container.tsx b/packages/odie-client/src/components/message/messages-container.tsx index 3d09e309b63df..7b26f7682e1b8 100644 --- a/packages/odie-client/src/components/message/messages-container.tsx +++ b/packages/odie-client/src/components/message/messages-container.tsx @@ -45,7 +45,7 @@ interface ChatMessagesProps { } export const MessagesContainer = ( { currentUser }: ChatMessagesProps ) => { - const { chat, botNameSlug, isChatLoaded } = useOdieAssistantContext(); + const { chat, botNameSlug, experimentVariationName, isChatLoaded } = useOdieAssistantContext(); const createZendeskConversation = useCreateZendeskConversation(); const resetSupportInteraction = useResetSupportInteraction(); const [ searchParams, setSearchParams ] = useSearchParams(); @@ -104,6 +104,12 @@ export const MessagesContainer = ( { currentUser }: ChatMessagesProps ) => { return currentMessage === nextMessage; }; + const removeDislikeStatus = experimentVariationName === 'give_wapuu_a_chance'; + + const availableStatusWithFeedback = removeDislikeStatus + ? [ 'sending', 'transfer' ] + : [ 'sending', 'dislike', 'transfer' ]; + return ( <>
@@ -134,8 +140,8 @@ export const MessagesContainer = ( { currentUser }: ChatMessagesProps ) => { /> ) ) } - { chat.status === 'dislike' && } - { [ 'sending', 'dislike', 'transfer' ].includes( chat.status ) && ( + { chat.status === 'dislike' && ! removeDislikeStatus && } + { availableStatusWithFeedback.includes( chat.status ) && (
{ chat.status === 'sending' && } { chat.status === 'dislike' && } diff --git a/packages/odie-client/src/components/message/user-message.tsx b/packages/odie-client/src/components/message/user-message.tsx index 53eea4ceee8ab..13c6a29366ca4 100644 --- a/packages/odie-client/src/components/message/user-message.tsx +++ b/packages/odie-client/src/components/message/user-message.tsx @@ -22,17 +22,34 @@ export const UserMessage = ( { message: Message; isMessageWithoutEscalationOption?: boolean; } ) => { - const { isUserEligibleForPaidSupport, trackEvent, chat } = useOdieAssistantContext(); + const { + isUserEligibleForPaidSupport, + hasUserEverEscalatedToHumanSupport, + trackEvent, + chat, + experimentVariationName, + } = useOdieAssistantContext(); const hasCannedResponse = message.context?.flags?.canned_response; - const isRequestingHumanSupport = message.context?.flags?.forward_to_human_support; + const isRequestingHumanSupport = message.context?.flags?.forward_to_human_support ?? false; const hasFeedback = !! message?.rating_value; const isBot = message.role === 'bot'; const isConnectedToZendesk = chat?.provider === 'zendesk'; const isPositiveFeedback = hasFeedback && message && message.rating_value && +message.rating_value === 1; - const showExtraContactOptions = - ( hasFeedback && ! isPositiveFeedback ) || isRequestingHumanSupport; + + const isExperimentGiveWapuuAChance = experimentVariationName === 'give_wapuu_a_chance'; + + let showExtraContactOptions = false; + if ( isExperimentGiveWapuuAChance ) { + showExtraContactOptions = isRequestingHumanSupport; + } else { + showExtraContactOptions = ( hasFeedback && ! isPositiveFeedback ) || isRequestingHumanSupport; + } + + const showDirectEscalationLink = isExperimentGiveWapuuAChance + ? hasUserEverEscalatedToHumanSupport + : ! ( hasFeedback && ! isPositiveFeedback ) || isRequestingHumanSupport; const forwardMessage = isUserEligibleForPaidSupport ? ODIE_FORWARD_TO_ZENDESK_MESSAGE @@ -83,7 +100,7 @@ export const UserMessage = ( { } ) }
- { ! showExtraContactOptions && } + { showDirectEscalationLink && } { ! isConnectedToZendesk && ( ) } diff --git a/packages/odie-client/src/context/index.tsx b/packages/odie-client/src/context/index.tsx index 17112afaabcb3..ed05db38321ea 100644 --- a/packages/odie-client/src/context/index.tsx +++ b/packages/odie-client/src/context/index.tsx @@ -37,12 +37,15 @@ export const OdieAssistantContext = createContext< OdieAssistantContextInterface canConnectToZendesk: false, clearChat: noop, currentUser: { display_name: 'Me' }, + experimentVariationName: null, + hasUserEverEscalatedToHumanSupport: false, isChatLoaded: false, isMinimized: false, isUserEligibleForPaidSupport: false, odieBroadcastClientId: '', setChat: noop, setChatStatus: noop, + setExperimentVariationName: noop, setMessageLikedStatus: noop, setWaitAnswerToFirstMessageFromHumanSupport: noop, trackEvent: noop, @@ -84,12 +87,23 @@ export const OdieAssistantProvider: React.FC< OdieAssistantProviderProps > = ( { }; }, [] ); + const [ experimentVariationName, setExperimentVariationName ] = useState< + string | null | undefined + >( null ); + /** * The main chat thread. * This is where we manage the state of the chat. */ const { mainChatState, setMainChatState } = useGetCombinedChat( canConnectToZendesk ); + /** + * Has the user ever escalated to get human support? + */ + const hasUserEverEscalatedToHumanSupport = mainChatState?.messages.some( + ( message ) => message.context?.flags?.forward_to_human_support + ); + /** * Tracking event. * Handler to make sure all requests are the same. @@ -176,13 +190,16 @@ export const OdieAssistantProvider: React.FC< OdieAssistantProviderProps > = ( { extraContactOptions, isChatLoaded, isMinimized, + experimentVariationName, isUserEligibleForPaidSupport, canConnectToZendesk, + hasUserEverEscalatedToHumanSupport, odieBroadcastClientId, selectedSiteId, selectedSiteURL, userFieldMessage, setChatStatus, + setExperimentVariationName, setMessageLikedStatus, setWaitAnswerToFirstMessageFromHumanSupport, trackEvent, diff --git a/packages/odie-client/src/data/use-send-odie-message.ts b/packages/odie-client/src/data/use-send-odie-message.ts index a40ae3aca9842..2978a42589bd0 100644 --- a/packages/odie-client/src/data/use-send-odie-message.ts +++ b/packages/odie-client/src/data/use-send-odie-message.ts @@ -34,8 +34,15 @@ export const useSendOdieMessage = () => { const internal_message_id = generateUUID(); const queryClient = useQueryClient(); - const { botNameSlug, selectedSiteId, version, setChat, odieBroadcastClientId, setChatStatus } = - useOdieAssistantContext(); + const { + botNameSlug, + selectedSiteId, + version, + setChat, + odieBroadcastClientId, + setChatStatus, + setExperimentVariationName, + } = useOdieAssistantContext(); const addMessage = ( message: Message | Message[], props?: Partial< Chat > ) => { setChat( ( prevChat ) => ( { @@ -111,7 +118,7 @@ export const useSendOdieMessage = () => { type: 'message', context: returnedChat.messages[ 0 ].context, }; - + setExperimentVariationName( returnedChat.experiment_name ); addMessage( botMessage, { odieId: returnedChat.chat_id } ); broadcastOdieMessage( botMessage, odieBroadcastClientId ); }, diff --git a/packages/odie-client/src/hooks/use-auto-scroll.ts b/packages/odie-client/src/hooks/use-auto-scroll.ts index 54cfc8c1561ae..aada915ad4487 100644 --- a/packages/odie-client/src/hooks/use-auto-scroll.ts +++ b/packages/odie-client/src/hooks/use-auto-scroll.ts @@ -2,7 +2,7 @@ import { RefObject, useEffect, useRef } from 'react'; import { useOdieAssistantContext } from '../context'; export const useAutoScroll = ( messagesContainerRef: RefObject< HTMLDivElement > ) => { - const { chat } = useOdieAssistantContext(); + const { chat, experimentVariationName } = useOdieAssistantContext(); const debounceTimeoutRef = useRef< number >( 500 ); const debounceTimeoutIdRef = useRef< number | null >( null ); const lastChatStatus = useRef< string | null >( null ); @@ -12,6 +12,10 @@ export const useAutoScroll = ( messagesContainerRef: RefObject< HTMLDivElement > return; } + if ( experimentVariationName === 'give_wapuu_a_chance' && chat.status === 'dislike' ) { + return; + } + if ( debounceTimeoutIdRef.current ) { clearTimeout( debounceTimeoutIdRef.current ); } diff --git a/packages/odie-client/src/types.ts b/packages/odie-client/src/types.ts index 8827fabe92c37..fef9a56502197 100644 --- a/packages/odie-client/src/types.ts +++ b/packages/odie-client/src/types.ts @@ -10,6 +10,8 @@ export type OdieAssistantContextInterface = { chat: Chat; clearChat: () => void; currentUser: CurrentUser; + experimentVariationName: string | undefined | null; + hasUserEverEscalatedToHumanSupport: boolean; isMinimized?: boolean; isUserEligibleForPaidSupport: boolean; extraContactOptions?: ReactNode; @@ -18,6 +20,7 @@ export type OdieAssistantContextInterface = { selectedSiteURL?: string | null; userFieldMessage?: string | null; waitAnswerToFirstMessageFromHumanSupport: boolean; + setExperimentVariationName: ( variationName: string | null | undefined ) => void; setMessageLikedStatus: ( message: Message, liked: boolean ) => void; setChat: ( chat: Chat | SetStateAction< Chat > ) => void; setChatStatus: ( status: ChatStatus ) => void; @@ -156,7 +159,12 @@ export type Message = { export type ChatStatus = 'loading' | 'loaded' | 'sending' | 'dislike' | 'transfer' | 'closed'; -export type ReturnedChat = { chat_id: number; messages: Message[]; wpcom_user_id: number }; +export type ReturnedChat = { + chat_id: number; + messages: Message[]; + wpcom_user_id: number; + experiment_name: string | undefined | null; +}; export type OdieChat = { messages: Message[];