Skip to content

Commit d1ece2f

Browse files
authored
Merge pull request Expensify#81807 from Krishna2323/krishna2323/issue/81587
Hide the chat history when Concierge is opened up in the right hand pane
2 parents 88df7ab + 202c8d8 commit d1ece2f

21 files changed

Lines changed: 531 additions & 49 deletions

src/CONST/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,6 +2413,7 @@ const CONST = {
24132413
},
24142414

24152415
CONCIERGE_DISPLAY_NAME: 'Concierge',
2416+
CONCIERGE_GREETING_ACTION_ID: 'concierge-side-panel-greeting',
24162417

24172418
INTEGRATION_ENTITY_MAP_TYPES: {
24182419
DEFAULT: 'DEFAULT',

src/components/SidePanel/SidePanelContextProvider.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
88
import useSidePanelDisplayStatus from '@hooks/useSidePanelDisplayStatus';
99
import useWindowDimensions from '@hooks/useWindowDimensions';
1010
import SidePanelActions from '@libs/actions/SidePanel';
11+
import DateUtils from '@libs/DateUtils';
1112
import focusComposerWithDelay from '@libs/focusComposerWithDelay';
1213
import {isPolicyAdmin, shouldShowPolicy} from '@libs/PolicyUtils';
1314
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
@@ -28,6 +29,7 @@ type SidePanelStateContextProps = {
2829
sidePanelTranslateX: RefObject<Animated.Value>;
2930
sidePanelNVP?: SidePanel;
3031
reportID?: string;
32+
sessionStartTime: string | null;
3133
};
3234

3335
type SidePanelActionsContextProps = {
@@ -44,6 +46,7 @@ const SidePanelStateContext = createContext<SidePanelStateContextProps>({
4446
shouldHideToolTip: false,
4547
sidePanelOffset: {current: new Animated.Value(0)},
4648
sidePanelTranslateX: {current: new Animated.Value(0)},
49+
sessionStartTime: null,
4750
});
4851

4952
const SidePanelActionsContext = createContext<SidePanelActionsContextProps>({
@@ -83,6 +86,16 @@ function SidePanelContextProvider({children}: PropsWithChildren) {
8386

8487
const reportID = isRHPAdminsRoom && isUserAdmin && isPolicyActive && adminsChatReportID ? adminsChatReportID : conciergeReportID;
8588

89+
const [sessionStartTime, setSessionStartTime] = useState<string | null>(null);
90+
const [prevShouldHideSidePanel, setPrevShouldHideSidePanel] = useState(shouldHideSidePanel);
91+
92+
if (prevShouldHideSidePanel !== shouldHideSidePanel) {
93+
setPrevShouldHideSidePanel(shouldHideSidePanel);
94+
if (!shouldHideSidePanel) {
95+
setSessionStartTime(DateUtils.getDBTime());
96+
}
97+
}
98+
8699
useEffect(() => {
87100
sidePanelWidthRef.current = sidePanelWidth;
88101
}, [sidePanelWidth]);
@@ -133,6 +146,7 @@ function SidePanelContextProvider({children}: PropsWithChildren) {
133146
sidePanelTranslateX,
134147
sidePanelNVP,
135148
reportID,
149+
sessionStartTime,
136150
};
137151

138152
// Because of the React Compiler we don't need to memoize it manually
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import {useCallback, useMemo, useState} from 'react';
2+
import DateUtils from '@libs/DateUtils';
3+
import {isCreatedAction} from '@libs/ReportActionsUtils';
4+
import {buildConciergeGreetingReportAction} from '@libs/ReportUtils';
5+
import type * as OnyxTypes from '@src/types/onyx';
6+
7+
type UseConciergeSidePanelReportActionsParams = {
8+
report: OnyxTypes.Report;
9+
reportActions: OnyxTypes.ReportAction[];
10+
visibleReportActions: OnyxTypes.ReportAction[];
11+
isConciergeSidePanel: boolean;
12+
hasUserSentMessage: boolean;
13+
hasOlderActions: boolean;
14+
sessionStartTime: string | null;
15+
currentUserAccountID: number;
16+
greetingText: string;
17+
loadOlderChats: (force?: boolean) => void;
18+
};
19+
20+
function useConciergeSidePanelReportActions({
21+
report,
22+
reportActions,
23+
visibleReportActions,
24+
isConciergeSidePanel,
25+
hasUserSentMessage,
26+
hasOlderActions,
27+
sessionStartTime,
28+
currentUserAccountID,
29+
greetingText,
30+
loadOlderChats,
31+
}: UseConciergeSidePanelReportActionsParams) {
32+
const [showFullHistory, setShowFullHistory] = useState(false);
33+
const [prevSessionStartTime, setPrevSessionStartTime] = useState(sessionStartTime);
34+
const [prevHasUserSentMessage, setPrevHasUserSentMessage] = useState(hasUserSentMessage);
35+
36+
if (prevSessionStartTime !== sessionStartTime) {
37+
setPrevSessionStartTime(sessionStartTime);
38+
setShowFullHistory(false);
39+
} else if (prevHasUserSentMessage && !hasUserSentMessage) {
40+
setPrevHasUserSentMessage(hasUserSentMessage);
41+
setShowFullHistory(false);
42+
} else if (prevHasUserSentMessage !== hasUserSentMessage) {
43+
setPrevHasUserSentMessage(hasUserSentMessage);
44+
}
45+
46+
// Check if the user had sent any message BEFORE this session started.
47+
// Uses sessionStartTime as the boundary — any user message created before the
48+
// panel opened is a pre-session message, regardless of when it was loaded.
49+
//
50+
// When no user message is found in the loaded set, hasOlderActions indicates
51+
// whether there is unloaded history. On a new account all onboarding messages
52+
// fit in a single page (hasOlderActions=false). On an existing account with
53+
// prior interactions the history spans multiple pages (hasOlderActions=true).
54+
//
55+
const hadUserMessageAtSessionStart = useMemo(() => {
56+
if (!isConciergeSidePanel || !sessionStartTime) {
57+
return false;
58+
}
59+
const hasUserMessageInLoadedSet = visibleReportActions.some(
60+
(action) => !isCreatedAction(action) && action.actorAccountID === currentUserAccountID && action.created < sessionStartTime,
61+
);
62+
return hasUserMessageInLoadedSet || hasOlderActions;
63+
}, [isConciergeSidePanel, visibleReportActions, currentUserAccountID, sessionStartTime, hasOlderActions]);
64+
65+
const hasPreviousMessages = useMemo(() => {
66+
if (!isConciergeSidePanel || !hadUserMessageAtSessionStart || !sessionStartTime) {
67+
return false;
68+
}
69+
return visibleReportActions.some((action) => !isCreatedAction(action) && action.created < sessionStartTime);
70+
}, [isConciergeSidePanel, visibleReportActions, sessionStartTime, hadUserMessageAtSessionStart]);
71+
72+
const showConciergeSidePanelWelcome = isConciergeSidePanel && hadUserMessageAtSessionStart && !hasUserSentMessage && !showFullHistory;
73+
const showConciergeGreeting = isConciergeSidePanel && hadUserMessageAtSessionStart && !showFullHistory;
74+
75+
const conciergeGreetingAction = useMemo(() => {
76+
if (!showConciergeGreeting) {
77+
return undefined;
78+
}
79+
return buildConciergeGreetingReportAction(report.reportID, greetingText, report.lastReadTime ?? DateUtils.getDBTime());
80+
}, [showConciergeGreeting, report.reportID, report.lastReadTime, greetingText]);
81+
82+
const firstUserMessageCreated = useMemo(() => {
83+
if (showConciergeSidePanelWelcome || !isConciergeSidePanel || !hasUserSentMessage || !sessionStartTime) {
84+
return undefined;
85+
}
86+
return reportActions.reduce<string | undefined>((earliest, action) => {
87+
if (isCreatedAction(action) || action.created < sessionStartTime || action.actorAccountID !== currentUserAccountID) {
88+
return earliest;
89+
}
90+
return !earliest || action.created < earliest ? action.created : earliest;
91+
}, undefined);
92+
}, [showConciergeSidePanelWelcome, isConciergeSidePanel, hasUserSentMessage, sessionStartTime, reportActions, currentUserAccountID]);
93+
94+
const isCurrentSessionAction = useCallback(
95+
(action: OnyxTypes.ReportAction): boolean => {
96+
if (!firstUserMessageCreated || !sessionStartTime) {
97+
return false;
98+
}
99+
return isCreatedAction(action) || (action.created >= sessionStartTime && action.created >= firstUserMessageCreated);
100+
},
101+
[firstUserMessageCreated, sessionStartTime],
102+
);
103+
104+
const filterActions = useCallback(
105+
(actions: OnyxTypes.ReportAction[]): OnyxTypes.ReportAction[] => {
106+
if (showConciergeSidePanelWelcome && conciergeGreetingAction) {
107+
const createdAction = actions.find(isCreatedAction);
108+
return createdAction ? [conciergeGreetingAction, createdAction] : [conciergeGreetingAction];
109+
}
110+
if (!isConciergeSidePanel || showFullHistory) {
111+
return actions;
112+
}
113+
if (!sessionStartTime) {
114+
return actions.filter(isCreatedAction);
115+
}
116+
if (!hadUserMessageAtSessionStart) {
117+
return actions;
118+
}
119+
const filtered = actions.filter(isCurrentSessionAction);
120+
if (filtered.length === 0) {
121+
return actions;
122+
}
123+
if (conciergeGreetingAction) {
124+
const createdIndex = filtered.findIndex(isCreatedAction);
125+
filtered.splice(createdIndex === -1 ? filtered.length : createdIndex, 0, conciergeGreetingAction);
126+
}
127+
return filtered;
128+
},
129+
[showConciergeSidePanelWelcome, conciergeGreetingAction, isConciergeSidePanel, showFullHistory, sessionStartTime, isCurrentSessionAction, hadUserMessageAtSessionStart],
130+
);
131+
132+
const filteredVisibleActions = useMemo(() => filterActions(visibleReportActions), [filterActions, visibleReportActions]);
133+
const filteredReportActions = useMemo(() => filterActions(reportActions), [filterActions, reportActions]);
134+
135+
const handleShowPreviousMessages = useCallback(() => {
136+
setShowFullHistory(true);
137+
loadOlderChats(true);
138+
}, [loadOlderChats]);
139+
140+
return {
141+
filteredVisibleActions,
142+
filteredReportActions,
143+
showConciergeSidePanelWelcome,
144+
showFullHistory,
145+
hasPreviousMessages,
146+
handleShowPreviousMessages,
147+
};
148+
}
149+
150+
export default useConciergeSidePanelReportActions;

src/languages/de.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@ const translations: TranslationDeepObject<typeof en> = {
545545
quarter: 'Quartal',
546546
vacationDelegate: 'Urlaubsvertretung',
547547
expensifyLogo: 'Expensify-Logo',
548+
concierge: {sidePanelGreeting: 'Hallo, wie kann ich helfen?', showHistory: 'Verlauf anzeigen'},
548549
duplicateReport: 'Duplizierten Bericht',
549550
approver: 'Genehmiger',
550551
},

src/languages/en.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,10 @@ const translations = {
563563
week: 'Week',
564564
year: 'Year',
565565
quarter: 'Quarter',
566+
concierge: {
567+
sidePanelGreeting: 'Hi there, how can I help?',
568+
showHistory: 'Show history',
569+
},
566570
vacationDelegate: 'Vacation delegate',
567571
expensifyLogo: 'Expensify logo',
568572
approver: 'Approver',

src/languages/es.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,10 @@ const translations: TranslationDeepObject<typeof en> = {
416416
week: 'Semana',
417417
year: 'Año',
418418
quarter: 'Trimestre',
419+
concierge: {
420+
sidePanelGreeting: 'Hola, ¿en qué puedo ayudarte?',
421+
showHistory: 'Mostrar historial',
422+
},
419423
vacationDelegate: 'Delegado de vacaciones',
420424
expensifyLogo: 'Logo de Expensify',
421425
approver: 'Aprobador',

src/languages/fr.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@ const translations: TranslationDeepObject<typeof en> = {
545545
quarter: 'Trimestre',
546546
vacationDelegate: 'Délégué de vacances',
547547
expensifyLogo: 'Logo Expensify',
548+
concierge: {sidePanelGreeting: 'Bonjour, comment puis-je vous aider ?', showHistory: 'Afficher l’historique'},
548549
duplicateReport: 'Note de frais en double',
549550
approver: 'Approbateur',
550551
},

src/languages/it.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@ const translations: TranslationDeepObject<typeof en> = {
545545
quarter: 'Trimestre',
546546
vacationDelegate: 'Delega ferie',
547547
expensifyLogo: 'Logo Expensify',
548+
concierge: {sidePanelGreeting: 'Ciao, come posso aiutarti?', showHistory: 'Mostra cronologia'},
548549
duplicateReport: 'Report duplicato',
549550
approver: 'Approvante',
550551
},

src/languages/ja.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ const translations: TranslationDeepObject<typeof en> = {
544544
quarter: '四半期',
545545
vacationDelegate: '休暇代理人',
546546
expensifyLogo: 'Expensifyロゴ',
547+
concierge: {sidePanelGreeting: 'こんにちは、どのようにお手伝いできますか?', showHistory: '履歴を表示'},
547548
duplicateReport: 'レポートを複製',
548549
approver: '承認者',
549550
},

src/languages/nl.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ const translations: TranslationDeepObject<typeof en> = {
544544
quarter: 'Kwartaal',
545545
vacationDelegate: 'Vertegenwoordiger tijdens vakantie',
546546
expensifyLogo: 'Expensify-logo',
547+
concierge: {sidePanelGreeting: 'Hoi, waarmee kan ik je helpen?', showHistory: 'Geschiedenis weergeven'},
547548
duplicateReport: 'Dubbel rapport',
548549
approver: 'Fiatteur',
549550
},

0 commit comments

Comments
 (0)