diff --git a/assets/images/chart-pie.svg b/assets/images/chart-pie.svg
new file mode 100644
index 000000000000..97da1ef66cef
--- /dev/null
+++ b/assets/images/chart-pie.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/components/Icon/chunks/expensify-icons.chunk.ts b/src/components/Icon/chunks/expensify-icons.chunk.ts
index 4d388c980589..2b519f85fd99 100644
--- a/src/components/Icon/chunks/expensify-icons.chunk.ts
+++ b/src/components/Icon/chunks/expensify-icons.chunk.ts
@@ -44,6 +44,7 @@ import CarWithKey from '@assets/images/car-with-key.svg';
import Car from '@assets/images/car.svg';
import CaretUpDown from '@assets/images/caret-up-down.svg';
import Cash from '@assets/images/cash.svg';
+import ChartPie from '@assets/images/chart-pie.svg';
import ChatBubbleCounter from '@assets/images/chatbubble-counter.svg';
import ChatBubbleReply from '@assets/images/chatbubble-reply.svg';
import ChatBubbleUnread from '@assets/images/chatbubble-unread.svg';
@@ -334,6 +335,7 @@ const Expensicons = {
CarPlus,
Cash,
CertiniaSquare,
+ ChartPie,
ChatBubble,
ChatBubbles,
Checkbox,
diff --git a/src/hooks/useConciergeAskState.ts b/src/hooks/useConciergeAskState.ts
new file mode 100644
index 000000000000..9d41745a7caa
--- /dev/null
+++ b/src/hooks/useConciergeAskState.ts
@@ -0,0 +1,57 @@
+import {isCreatedAction, isCurrentUserPendingAddAction} from '@libs/ReportActionsUtils';
+
+import {useConciergeSessionState} from '@pages/inbox/ConciergeSessionContext';
+
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import type {ReportActions} from '@src/types/onyx/ReportAction';
+
+import type {OnyxEntry} from 'react-native-onyx';
+
+import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails';
+import useIsInSidePanel from './useIsInSidePanel';
+import useOnyx from './useOnyx';
+import usePermissions from './usePermissions';
+
+type ConciergeAskState = {
+ /** Whether to show the welcome empty state and the quick actions that go with it */
+ shouldShowWelcome: boolean;
+
+ /** Whether to label the composer as the start of a new question, which it is once earlier history is expanded */
+ shouldLabelComposerAsNewQuestion: boolean;
+};
+
+/**
+ * Drives the Ask Concierge screen in the main Concierge DM. The empty state lives in the report actions
+ * list and the composer label and quick actions live in the report footer, and those are siblings, so
+ * both derive their state here rather than one passing it to the other.
+ */
+function useConciergeAskState(reportID: string | undefined): ConciergeAskState {
+ const {isBetaEnabled} = usePermissions();
+ const isInSidePanel = useIsInSidePanel();
+ const {sessionStartTime, showFullHistory} = useConciergeSessionState();
+ const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails();
+ const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
+
+ // Derive the boolean inside the Onyx selector so the screen re-renders only when session activity
+ // flips, not on every report-action change in the Concierge chat.
+ const hasSessionActivitySelector = (actions: OnyxEntry) => {
+ if (!actions || !sessionStartTime) {
+ return false;
+ }
+ return Object.values(actions).some((action) => isCurrentUserPendingAddAction(action, currentUserAccountID) || (!isCreatedAction(action) && action.created >= sessionStartTime));
+ };
+ const [hasSessionActivity] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, {
+ selector: hasSessionActivitySelector,
+ });
+
+ // Waiting for the session boundary keeps an existing conversation from flashing the welcome screen on open.
+ const isAskConciergeChat = !!reportID && reportID === conciergeReportID && !isInSidePanel && !!sessionStartTime && isBetaEnabled(CONST.BETAS.CONCIERGE_RESPOND_IN_THREAD);
+
+ return {
+ shouldShowWelcome: isAskConciergeChat && !showFullHistory && !hasSessionActivity,
+ shouldLabelComposerAsNewQuestion: isAskConciergeChat && showFullHistory && !hasSessionActivity,
+ };
+}
+
+export default useConciergeAskState;
diff --git a/src/languages/de.ts b/src/languages/de.ts
index 670707b049f5..13c2c426226e 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -488,7 +488,26 @@ const translations: TranslationDeepObject = {
quarter: 'Quartal',
vacationDelegate: 'Urlaubsvertretung',
expensifyLogo: 'Expensify-Logo',
- concierge: {greeting: 'Hallo, wie kann ich helfen?', showHistory: 'Verlauf anzeigen'},
+ concierge: {
+ greeting: 'Hallo, wie kann ich helfen?',
+ subtitle: 'Dein persönlicher KI-Assistent',
+ welcomeDescription: 'Concierge kann Fragen beantworten, Ausgaben aktualisieren und mehr.',
+ viewChatHistory: 'Chatverlauf anzeigen',
+ hideChatHistory: 'Chatverlauf ausblenden',
+ askNewQuestion: 'Neue Frage stellen',
+ newChat: 'Neuer Chat',
+ composerPlaceholder: 'Wie viel habe ich letzten Monat ausgegeben?',
+ quickActions: {
+ createExpense: 'Ausgabe erstellen',
+ createExpensePrompt: 'Erstelle eine Ausgabe',
+ analyzeSpend: 'Ausgaben analysieren',
+ analyzeSpendPrompt: 'Wie viel habe ich letzten Monat ausgegeben?',
+ configureWorkspace: 'Arbeitsbereich einrichten',
+ configureWorkspacePrompt: 'Hilf mir, meinen Arbeitsbereich einzurichten',
+ getSupport: 'Support erhalten',
+ getSupportPrompt: 'Ich brauche bei etwas Hilfe',
+ },
+ },
duplicateReport: 'Duplizierten Bericht',
approver: 'Genehmiger',
goToConcierge: 'Zu Concierge',
diff --git a/src/languages/el.ts b/src/languages/el.ts
index 5345331a15a0..e303b5053c0e 100644
--- a/src/languages/el.ts
+++ b/src/languages/el.ts
@@ -492,7 +492,23 @@ const translations: TranslationDeepObject = {
tagGLCode: 'Ετικέτα κωδικού GL',
concierge: {
greeting: 'Γεια σας, πώς μπορώ να σας βοηθήσω;',
- showHistory: 'Εμφάνιση ιστορικού',
+ subtitle: 'Ο προσωπικός σας βοηθός AI',
+ welcomeDescription: 'Ο Concierge μπορεί να απαντήσει σε ερωτήσεις, να ενημερώσει δαπάνες και άλλα.',
+ viewChatHistory: 'Προβολή ιστορικού συνομιλιών',
+ hideChatHistory: 'Απόκρυψη ιστορικού συνομιλιών',
+ askNewQuestion: 'Κάντε μια νέα ερώτηση',
+ newChat: 'Νέα συνομιλία',
+ composerPlaceholder: 'Πόσα ξόδεψα τον προηγούμενο μήνα;',
+ quickActions: {
+ createExpense: 'Δημιουργία δαπάνης',
+ createExpensePrompt: 'Δημιούργησε μια δαπάνη',
+ analyzeSpend: 'Ανάλυση δαπανών',
+ analyzeSpendPrompt: 'Πόσα ξόδεψα τον προηγούμενο μήνα;',
+ configureWorkspace: 'Ρύθμιση χώρου εργασίας',
+ configureWorkspacePrompt: 'Βοήθησέ με να ρυθμίσω τον χώρο εργασίας μου',
+ getSupport: 'Λήψη υποστήριξης',
+ getSupportPrompt: 'Χρειάζομαι βοήθεια με κάτι',
+ },
},
vacationDelegate: 'Εκπρόσωπος διακοπών',
expensifyLogo: 'Λογότυπο Expensify',
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 7ebbdcde6fb3..97e3f6a9740c 100644
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -538,7 +538,23 @@ const translations = {
tagGLCode: 'Tag GL code',
concierge: {
greeting: 'Hi there, how can I help?',
- showHistory: 'Show history',
+ subtitle: 'Your personal AI assistant',
+ welcomeDescription: 'Concierge can answer questions, update expenses, and more.',
+ viewChatHistory: 'View chat history',
+ hideChatHistory: 'Hide chat history',
+ askNewQuestion: 'Ask a new question',
+ newChat: 'New chat',
+ composerPlaceholder: 'How much did I spend last month?',
+ quickActions: {
+ createExpense: 'Create expense',
+ createExpensePrompt: 'Create an expense',
+ analyzeSpend: 'Analyze spend',
+ analyzeSpendPrompt: 'How much did I spend last month?',
+ configureWorkspace: 'Configure workspace',
+ configureWorkspacePrompt: 'Help me configure my workspace',
+ getSupport: 'Get support',
+ getSupportPrompt: 'I need help with something',
+ },
},
vacationDelegate: 'Vacation delegate',
expensifyLogo: 'Expensify logo',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index 073dd24d79db..37baa13f0575 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -481,7 +481,23 @@ const translations: TranslationDeepObject = {
quarter: 'Trimestre',
concierge: {
greeting: 'Hola, ¿en qué puedo ayudarte?',
- showHistory: 'Mostrar historial',
+ subtitle: 'Tu asistente de IA personal',
+ welcomeDescription: 'Concierge puede responder preguntas, actualizar gastos y mucho más.',
+ viewChatHistory: 'Ver historial del chat',
+ hideChatHistory: 'Ocultar historial del chat',
+ askNewQuestion: 'Haz una nueva pregunta',
+ newChat: 'Nuevo chat',
+ composerPlaceholder: '¿Cuánto gasté el mes pasado?',
+ quickActions: {
+ createExpense: 'Crear gasto',
+ createExpensePrompt: 'Crea un gasto',
+ analyzeSpend: 'Analizar gastos',
+ analyzeSpendPrompt: '¿Cuánto gasté el mes pasado?',
+ configureWorkspace: 'Configurar espacio de trabajo',
+ configureWorkspacePrompt: 'Ayúdame a configurar mi espacio de trabajo',
+ getSupport: 'Obtener ayuda',
+ getSupportPrompt: 'Necesito ayuda con algo',
+ },
},
vacationDelegate: 'Delegado de vacaciones',
expensifyLogo: 'Logo de Expensify',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index e8ca853b7569..13c936d8614b 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -488,7 +488,26 @@ const translations: TranslationDeepObject = {
quarter: 'Trimestre',
vacationDelegate: 'Délégué de vacances',
expensifyLogo: 'Logo Expensify',
- concierge: {greeting: 'Bonjour, comment puis-je vous aider ?', showHistory: 'Afficher l’historique'},
+ concierge: {
+ greeting: 'Bonjour, comment puis-je vous aider ?',
+ subtitle: 'Votre assistant IA personnel',
+ welcomeDescription: 'Concierge peut répondre à vos questions, mettre à jour vos dépenses et plus encore.',
+ viewChatHistory: 'Afficher l’historique du chat',
+ hideChatHistory: 'Masquer l’historique du chat',
+ askNewQuestion: 'Poser une nouvelle question',
+ newChat: 'Nouveau chat',
+ composerPlaceholder: 'Combien ai-je dépensé le mois dernier ?',
+ quickActions: {
+ createExpense: 'Créer une dépense',
+ createExpensePrompt: 'Crée une dépense',
+ analyzeSpend: 'Analyser les dépenses',
+ analyzeSpendPrompt: 'Combien ai-je dépensé le mois dernier ?',
+ configureWorkspace: 'Configurer l’espace de travail',
+ configureWorkspacePrompt: 'Aide-moi à configurer mon espace de travail',
+ getSupport: 'Obtenir de l’aide',
+ getSupportPrompt: 'J’ai besoin d’aide pour quelque chose',
+ },
+ },
duplicateReport: 'Note de frais en double',
approver: 'Approbateur',
goToConcierge: 'Aller à Concierge',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 86ea6baca0bf..98861b81ea9e 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -488,7 +488,26 @@ const translations: TranslationDeepObject = {
quarter: 'Trimestre',
vacationDelegate: 'Delega ferie',
expensifyLogo: 'Logo Expensify',
- concierge: {greeting: 'Ciao, come posso aiutarti?', showHistory: 'Mostra cronologia'},
+ concierge: {
+ greeting: 'Ciao, come posso aiutarti?',
+ subtitle: 'Il tuo assistente AI personale',
+ welcomeDescription: 'Concierge può rispondere a domande, aggiornare spese e altro ancora.',
+ viewChatHistory: 'Mostra cronologia chat',
+ hideChatHistory: 'Nascondi cronologia chat',
+ askNewQuestion: 'Fai una nuova domanda',
+ newChat: 'Nuova chat',
+ composerPlaceholder: 'Quanto ho speso il mese scorso?',
+ quickActions: {
+ createExpense: 'Crea spesa',
+ createExpensePrompt: 'Crea una spesa',
+ analyzeSpend: 'Analizza le spese',
+ analyzeSpendPrompt: 'Quanto ho speso il mese scorso?',
+ configureWorkspace: 'Configura area di lavoro',
+ configureWorkspacePrompt: 'Aiutami a configurare la mia area di lavoro',
+ getSupport: 'Ottieni assistenza',
+ getSupportPrompt: 'Ho bisogno di aiuto con una cosa',
+ },
+ },
duplicateReport: 'Report duplicato',
approver: 'Approvante',
goToConcierge: 'Vai a Concierge',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index c9bd9d9c8960..a46732111a1b 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -487,7 +487,26 @@ const translations: TranslationDeepObject = {
quarter: '四半期',
vacationDelegate: '休暇代理人',
expensifyLogo: 'Expensifyロゴ',
- concierge: {greeting: 'こんにちは、どのようにお手伝いできますか?', showHistory: '履歴を表示'},
+ concierge: {
+ greeting: 'こんにちは、どのようにお手伝いできますか?',
+ subtitle: 'あなた専用のAIアシスタント',
+ welcomeDescription: 'Conciergeは質問への回答や経費の更新などを行えます。',
+ viewChatHistory: 'チャット履歴を表示',
+ hideChatHistory: 'チャット履歴を非表示',
+ askNewQuestion: '新しい質問をする',
+ newChat: '新しいチャット',
+ composerPlaceholder: '先月はいくら使いましたか?',
+ quickActions: {
+ createExpense: '経費を作成',
+ createExpensePrompt: '経費を作成して',
+ analyzeSpend: '支出を分析',
+ analyzeSpendPrompt: '先月はいくら使いましたか?',
+ configureWorkspace: 'ワークスペースを設定',
+ configureWorkspacePrompt: 'ワークスペースの設定を手伝って',
+ getSupport: 'サポートを受ける',
+ getSupportPrompt: '困っていることがあります',
+ },
+ },
duplicateReport: 'レポートを複製',
approver: '承認者',
goToConcierge: 'Conciergeへ移動',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 4031271b6da1..6035992e57d4 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -487,7 +487,26 @@ const translations: TranslationDeepObject = {
quarter: 'Kwartaal',
vacationDelegate: 'Vertegenwoordiger tijdens vakantie',
expensifyLogo: 'Expensify-logo',
- concierge: {greeting: 'Hoi, waarmee kan ik je helpen?', showHistory: 'Geschiedenis weergeven'},
+ concierge: {
+ greeting: 'Hoi, waarmee kan ik je helpen?',
+ subtitle: 'Je persoonlijke AI-assistent',
+ welcomeDescription: 'Concierge kan vragen beantwoorden, uitgaven bijwerken en meer.',
+ viewChatHistory: 'Chatgeschiedenis weergeven',
+ hideChatHistory: 'Chatgeschiedenis verbergen',
+ askNewQuestion: 'Stel een nieuwe vraag',
+ newChat: 'Nieuwe chat',
+ composerPlaceholder: 'Hoeveel heb ik vorige maand uitgegeven?',
+ quickActions: {
+ createExpense: 'Uitgave maken',
+ createExpensePrompt: 'Maak een uitgave',
+ analyzeSpend: 'Uitgaven analyseren',
+ analyzeSpendPrompt: 'Hoeveel heb ik vorige maand uitgegeven?',
+ configureWorkspace: 'Werkruimte instellen',
+ configureWorkspacePrompt: 'Help me mijn werkruimte in te stellen',
+ getSupport: 'Hulp krijgen',
+ getSupportPrompt: 'Ik heb ergens hulp bij nodig',
+ },
+ },
duplicateReport: 'Dubbel rapport',
approver: 'Fiatteur',
goToConcierge: 'Ga naar Concierge',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 50a014fdd300..22d411f70a90 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -491,7 +491,26 @@ const translations: TranslationDeepObject = {
quarter: 'Kwartał',
vacationDelegate: 'Zastępca urlopowy',
expensifyLogo: 'Logo Expensify',
- concierge: {greeting: 'Cześć, w czym mogę pomóc?', showHistory: 'Pokaż historię'},
+ concierge: {
+ greeting: 'Cześć, w czym mogę pomóc?',
+ subtitle: 'Twój osobisty asystent AI',
+ welcomeDescription: 'Concierge może odpowiadać na pytania, aktualizować wydatki i nie tylko.',
+ viewChatHistory: 'Pokaż historię czatu',
+ hideChatHistory: 'Ukryj historię czatu',
+ askNewQuestion: 'Zadaj nowe pytanie',
+ newChat: 'Nowy czat',
+ composerPlaceholder: 'Ile wydałem w zeszłym miesiącu?',
+ quickActions: {
+ createExpense: 'Utwórz wydatek',
+ createExpensePrompt: 'Utwórz wydatek',
+ analyzeSpend: 'Analizuj wydatki',
+ analyzeSpendPrompt: 'Ile wydałem w zeszłym miesiącu?',
+ configureWorkspace: 'Skonfiguruj przestrzeń roboczą',
+ configureWorkspacePrompt: 'Pomóż mi skonfigurować moją przestrzeń roboczą',
+ getSupport: 'Uzyskaj pomoc',
+ getSupportPrompt: 'Potrzebuję z czymś pomocy',
+ },
+ },
duplicateReport: 'Zduplikowany raport',
approver: 'Osoba zatwierdzająca',
goToConcierge: 'Przejdź do Concierge',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index d3f8b357efa2..4072041bee4f 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -486,7 +486,26 @@ const translations: TranslationDeepObject = {
quarter: 'Trimestre',
vacationDelegate: 'Delegado de férias',
expensifyLogo: 'Logo da Expensify',
- concierge: {greeting: 'Oi, como posso ajudar?', showHistory: 'Mostrar histórico'},
+ concierge: {
+ greeting: 'Oi, como posso ajudar?',
+ subtitle: 'Seu assistente de IA pessoal',
+ welcomeDescription: 'O Concierge pode responder perguntas, atualizar despesas e muito mais.',
+ viewChatHistory: 'Ver histórico do chat',
+ hideChatHistory: 'Ocultar histórico do chat',
+ askNewQuestion: 'Faça uma nova pergunta',
+ newChat: 'Novo chat',
+ composerPlaceholder: 'Quanto gastei no mês passado?',
+ quickActions: {
+ createExpense: 'Criar despesa',
+ createExpensePrompt: 'Crie uma despesa',
+ analyzeSpend: 'Analisar gastos',
+ analyzeSpendPrompt: 'Quanto gastei no mês passado?',
+ configureWorkspace: 'Configurar espaço de trabalho',
+ configureWorkspacePrompt: 'Ajude-me a configurar meu espaço de trabalho',
+ getSupport: 'Obter suporte',
+ getSupportPrompt: 'Preciso de ajuda com uma coisa',
+ },
+ },
duplicateReport: 'Duplicar relatório',
approver: 'Aprovador',
goToConcierge: 'Ir para o Concierge',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 0355e53ad566..229ac87f64e0 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -483,7 +483,26 @@ const translations: TranslationDeepObject = {
quarter: '季度',
vacationDelegate: '休假代理',
expensifyLogo: 'Expensify徽标',
- concierge: {greeting: '你好,我能帮你做什么?', showHistory: '显示历史'},
+ concierge: {
+ greeting: '你好,我能帮你做什么?',
+ subtitle: '你的专属 AI 助手',
+ welcomeDescription: 'Concierge 可以回答问题、更新支出等。',
+ viewChatHistory: '查看聊天记录',
+ hideChatHistory: '隐藏聊天记录',
+ askNewQuestion: '提出新问题',
+ newChat: '新聊天',
+ composerPlaceholder: '我上个月花了多少钱?',
+ quickActions: {
+ createExpense: '创建支出',
+ createExpensePrompt: '创建一笔支出',
+ analyzeSpend: '分析支出',
+ analyzeSpendPrompt: '我上个月花了多少钱?',
+ configureWorkspace: '配置工作区',
+ configureWorkspacePrompt: '帮我配置工作区',
+ getSupport: '获取支持',
+ getSupportPrompt: '我需要一些帮助',
+ },
+ },
duplicateReport: '重复报销单',
approver: '审批人',
goToConcierge: '前往 Concierge',
diff --git a/src/pages/inbox/ConciergeSessionContext.tsx b/src/pages/inbox/ConciergeSessionContext.tsx
index 6122908e12ff..51e60a7be3a5 100644
--- a/src/pages/inbox/ConciergeSessionContext.tsx
+++ b/src/pages/inbox/ConciergeSessionContext.tsx
@@ -17,6 +17,8 @@ type ConciergeSessionStateContextType = {
type ConciergeSessionActionsContextType = {
startSession: (unreadBoundary?: string | null) => void;
+ /** Begins a brand new session, so the Concierge DM drops back to its welcome state */
+ resetSession: () => void;
setShowFullHistory: (show: boolean) => void;
setHadMessagesAtSessionStart: (value: boolean) => void;
};
@@ -29,6 +31,7 @@ const ConciergeSessionStateContext = createContext({
startSession: () => {},
+ resetSession: () => {},
setShowFullHistory: () => {},
setHadMessagesAtSessionStart: () => {},
});
@@ -105,8 +108,15 @@ function ConciergeSessionProvider({children}: PropsWithChildren) {
}
}, []);
+ const resetSession = useCallback(() => {
+ sessionCreatedAtRef.current = Date.now();
+ setSessionStartTime(getServerAnchoredDBTime());
+ setShowFullHistory(false);
+ setHadMessagesAtSessionStart(false);
+ }, []);
+
const stateValue = useMemo(() => ({sessionStartTime, showFullHistory, hadMessagesAtSessionStart}), [sessionStartTime, showFullHistory, hadMessagesAtSessionStart]);
- const actionsValue = useMemo(() => ({startSession, setShowFullHistory, setHadMessagesAtSessionStart}), [startSession]);
+ const actionsValue = useMemo(() => ({startSession, resetSession, setShowFullHistory, setHadMessagesAtSessionStart}), [startSession, resetSession]);
return (
diff --git a/src/pages/inbox/HeaderView.tsx b/src/pages/inbox/HeaderView.tsx
index 3b822f72ab62..ff734d5a963c 100644
--- a/src/pages/inbox/HeaderView.tsx
+++ b/src/pages/inbox/HeaderView.tsx
@@ -25,6 +25,7 @@ import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useParentReportAction from '@hooks/useParentReportAction';
+import usePermissions from '@hooks/usePermissions';
import usePolicy from '@hooks/usePolicy';
import {useDerivedReportNamesByReportIDs} from '@hooks/useReportAttributes';
import useReportIsArchived from '@hooks/useReportIsArchived';
@@ -34,6 +35,7 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
+import Navigation from '@libs/Navigation/Navigation';
import Parser from '@libs/Parser';
import {getPersonalDetailByEmail, getPersonalDetailsForAccountIDs} from '@libs/PersonalDetailsUtils';
import {getHumanAgentAccountIDFromReportAction, getHumanAgentFirstName} from '@libs/ReportActionsUtils';
@@ -86,6 +88,7 @@ import {callFunctionIfActionIsAllowed} from '@userActions/Session';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
+import ROUTES from '@src/ROUTES';
import SCREENS from '@src/SCREENS';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
@@ -97,6 +100,8 @@ import {isPast} from 'date-fns';
import React, {useMemo} from 'react';
import {Keyboard, View} from 'react-native';
+import {useConciergeSessionActions} from './ConciergeSessionContext';
+
type HeaderViewProps = {
/** Toggles the navigationMenu open and closed */
onNavigationMenuButtonClicked: () => void;
@@ -109,7 +114,7 @@ function HeaderView({onNavigationMenuButtonClicked, reportID}: HeaderViewProps)
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
const parentReportAction = useParentReportAction(report);
- const icons = useMemoizedLazyExpensifyIcons(['BackArrow', 'Close', 'DotIndicator']);
+ const icons = useMemoizedLazyExpensifyIcons(['BackArrow', 'Close', 'DotIndicator', 'Plus']);
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
const {isSmallScreenWidth, shouldUseNarrowLayout, isInLandscapeMode} = useResponsiveLayout();
const isInSidePanel = useIsInSidePanel();
@@ -145,6 +150,10 @@ function HeaderView({onNavigationMenuButtonClicked, reportID}: HeaderViewProps)
const isSelfDM = isSelfDMReportUtils(report);
const isGroupChat = isGroupChatReportUtils(report) || isDeprecatedGroupDM(report, isReportArchived);
const isConciergeChat = isConciergeChatReport(report, conciergeReportID);
+ const {isBetaEnabled} = usePermissions();
+ const {resetSession: resetConciergeSession} = useConciergeSessionActions();
+ const isAskConciergeEnabled = isBetaEnabled(CONST.BETAS.CONCIERGE_RESPOND_IN_THREAD) && !isInSidePanel;
+ const isConciergeThread = !!conciergeReportID && report?.parentReportID === conciergeReportID;
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [onboarding] = useOnyx(ONYXKEYS.NVP_ONBOARDING);
const allParticipants = getParticipantsAccountIDsForDisplay(report, false, true, undefined, reportMetadata);
@@ -174,7 +183,8 @@ function HeaderView({onNavigationMenuButtonClicked, reportID}: HeaderViewProps)
const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails();
// Use sorted display names for the title for group chats on native small screen widths
const title = getReportName(reportHeaderData, derivedReportHeaderName);
- const subtitle = getChatRoomSubtitle(reportHeaderData, reportHeaderDataPolicy, conciergeReportID, translate, false, isReportHeaderDataArchived);
+ const chatRoomSubtitle = getChatRoomSubtitle(reportHeaderData, reportHeaderDataPolicy, conciergeReportID, translate, false, isReportHeaderDataArchived);
+ const subtitle = isAskConciergeEnabled && isConciergeChat ? translate('common.concierge.subtitle') : chatRoomSubtitle;
// This is used to get the status badge for invoice report subtitle.
const statusTextForInvoiceReport = isParentInvoiceAndIsChatThread
? getReportStatusTranslation({stateNum: reportHeaderData?.stateNum, statusNum: reportHeaderData?.statusNum, translate})
@@ -292,6 +302,21 @@ function HeaderView({onNavigationMenuButtonClicked, reportID}: HeaderViewProps)
);
+ const startNewConciergeChat = () => {
+ resetConciergeSession();
+ Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(conciergeReportID));
+ };
+
+ const newConciergeChatButton = (
+
+ );
+
const renderAdditionalText = () => {
if (shouldShowSubtitle() || isPersonalExpenseChat || !policyName || !isEmptyObject(parentNavigationSubtitleData) || isSelfDM) {
return null;
@@ -472,6 +497,7 @@ function HeaderView({onNavigationMenuButtonClicked, reportID}: HeaderViewProps)
)}
+ {isAskConciergeEnabled && isConciergeThread && newConciergeChatButton}
{shouldShowBookCall && !shouldStackBookCall && bookCallButton}
{shouldShowOnBoardingHelpDropdownButton && !shouldUseNarrowLayout && onboardingHelpDropdownButton}
{!shouldUseNarrowLayout && !shouldShowDiscount && isChatUsedForOnboarding && (
diff --git a/src/pages/inbox/report/ConciergeChatHistoryToggle.tsx b/src/pages/inbox/report/ConciergeChatHistoryToggle.tsx
new file mode 100644
index 000000000000..ee9f6469dbd6
--- /dev/null
+++ b/src/pages/inbox/report/ConciergeChatHistoryToggle.tsx
@@ -0,0 +1,67 @@
+import Button from '@components/ButtonComposed';
+
+import useConciergeAskState from '@hooks/useConciergeAskState';
+import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
+import useLocalize from '@hooks/useLocalize';
+import useThemeStyles from '@hooks/useThemeStyles';
+
+import {useConciergeSessionActions} from '@pages/inbox/ConciergeSessionContext';
+
+import CONST from '@src/CONST';
+
+import React from 'react';
+import {View} from 'react-native';
+
+type ConciergeChatHistoryToggleProps = {
+ /** The ID of the report being displayed */
+ reportID: string;
+
+ /** Whether there are messages hidden before the session start */
+ hasPreviousMessages: boolean;
+
+ /** Whether the earlier conversation is currently shown */
+ shouldShowFullHistory: boolean;
+
+ /** Callback to reveal the earlier conversation */
+ onShowPreviousMessages: () => void;
+};
+
+/**
+ * Expands and collapses the earlier Concierge conversation in the main Concierge DM. It renders as part
+ * of the inverted list's header so it sits at the bottom of the conversation, directly above the composer.
+ */
+function ConciergeChatHistoryToggle({reportID, hasPreviousMessages, shouldShowFullHistory, onShowPreviousMessages}: ConciergeChatHistoryToggleProps) {
+ const styles = useThemeStyles();
+ const {translate} = useLocalize();
+ const expensifyIcons = useMemoizedLazyExpensifyIcons(['UpArrow', 'DownArrow']);
+ const {shouldShowWelcome, shouldLabelComposerAsNewQuestion} = useConciergeAskState(reportID);
+ const {setShowFullHistory} = useConciergeSessionActions();
+
+ const hideChatHistory = () => setShowFullHistory(false);
+
+ // Both flags are false once the user asks something, which is when this control would otherwise
+ // land between the latest message and the composer.
+ if (!shouldShowWelcome && !shouldLabelComposerAsNewQuestion) {
+ return null;
+ }
+
+ if (!hasPreviousMessages) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+ );
+}
+
+export default ConciergeChatHistoryToggle;
diff --git a/src/pages/inbox/report/ConciergeQuickActions.tsx b/src/pages/inbox/report/ConciergeQuickActions.tsx
new file mode 100644
index 000000000000..0436d0704861
--- /dev/null
+++ b/src/pages/inbox/report/ConciergeQuickActions.tsx
@@ -0,0 +1,65 @@
+import Button from '@components/ButtonComposed';
+
+import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
+import useLocalize from '@hooks/useLocalize';
+import useTheme from '@hooks/useTheme';
+import useThemeStyles from '@hooks/useThemeStyles';
+
+import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
+
+import {saveReportDraftComment} from '@userActions/Report';
+
+import CONST from '@src/CONST';
+
+import React from 'react';
+import {View} from 'react-native';
+
+const QUICK_ACTIONS = [
+ {iconName: 'Plus', labelKey: 'common.concierge.quickActions.createExpense', promptKey: 'common.concierge.quickActions.createExpensePrompt'},
+ {iconName: 'ChartPie', labelKey: 'common.concierge.quickActions.analyzeSpend', promptKey: 'common.concierge.quickActions.analyzeSpendPrompt'},
+ {iconName: 'Wrench', labelKey: 'common.concierge.quickActions.configureWorkspace', promptKey: 'common.concierge.quickActions.configureWorkspacePrompt'},
+ {iconName: 'QuestionMark', labelKey: 'common.concierge.quickActions.getSupport', promptKey: 'common.concierge.quickActions.getSupportPrompt'},
+] as const;
+
+const QUICK_ACTION_ICON_NAMES = QUICK_ACTIONS.map((action) => action.iconName);
+
+type ConciergeQuickActionsProps = {
+ /** The ID of the Concierge report the prompt is written into */
+ reportID: string;
+};
+
+/**
+ * Conversation starters shown under the composer while the Concierge chat is still empty. Pressing one
+ * writes its prompt into the composer so the user can edit it before sending.
+ */
+function ConciergeQuickActions({reportID}: ConciergeQuickActionsProps) {
+ const styles = useThemeStyles();
+ const theme = useTheme();
+ const {translate} = useLocalize();
+ const expensifyIcons = useMemoizedLazyExpensifyIcons(QUICK_ACTION_ICON_NAMES);
+
+ return (
+
+ {QUICK_ACTIONS.map(({iconName, labelKey, promptKey}) => (
+
+ ))}
+
+ );
+}
+
+export default ConciergeQuickActions;
diff --git a/src/pages/inbox/report/ConciergeWelcome.tsx b/src/pages/inbox/report/ConciergeWelcome.tsx
new file mode 100644
index 000000000000..b89e85844cc4
--- /dev/null
+++ b/src/pages/inbox/report/ConciergeWelcome.tsx
@@ -0,0 +1,27 @@
+import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
+import useLocalize from '@hooks/useLocalize';
+
+import HomeSectionEmptyState from '@pages/home/HomeSectionEmptyState';
+
+import React from 'react';
+
+const ILLUSTRATION_NAMES = ['ConciergeBot'] as const;
+
+/**
+ * Empty state shown in the main Concierge DM before the user asks anything.
+ */
+function ConciergeWelcome() {
+ const {translate} = useLocalize();
+ const illustrations = useMemoizedLazyIllustrations(ILLUSTRATION_NAMES);
+
+ return (
+
+ );
+}
+
+export default ConciergeWelcome;
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerInput.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerInput.tsx
index 777265b2b8a2..b464af77d9f1 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerInput.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ComposerInput.tsx
@@ -3,6 +3,7 @@ import type {LocalizedTranslate} from '@components/LocaleContextProvider';
import useIsScrollLikelyLayoutTriggered from '@hooks/useIsScrollLikelyLayoutTriggered';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
+import usePermissions from '@hooks/usePermissions';
import useReportIsArchived from '@hooks/useReportIsArchived';
import {setIsComposerFullSize} from '@libs/actions/Report';
@@ -45,6 +46,7 @@ function ComposerInput() {
const {setIsFullComposerAvailable, onBlur, onFocus, setComposerRef} = useComposerActions();
const {containerRef, suggestionsRef, isNextModalWillOpenRef} = useComposerMeta();
const {isEditingInComposer, didResetComposerHeightWhileEditing} = useComposerEditState();
+ const {isBetaEnabled} = usePermissions();
const isSubmittingEdit = isEditingInComposer || didResetComposerHeightWhileEditing;
const {submitDraftAndClearComposer, validateAndSubmitDraft} = useComposerSubmit(reportID);
@@ -69,6 +71,8 @@ function ComposerInput() {
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
const isReportArchived = useReportIsArchived(report?.reportID);
+ const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
+ const isAskConciergeChat = !!reportID && reportID === conciergeReportID && isBetaEnabled(CONST.BETAS.CONCIERGE_RESPOND_IN_THREAD);
const includesConcierge = chatIncludesConcierge({participants: report?.participants});
const isGroupPolicyReport = !!report?.policyID && report.policyID !== CONST.POLICY.ID_FAKE;
@@ -79,6 +83,8 @@ function ComposerInput() {
let inputPlaceholder = translate('reportActionCompose.writeSomething');
if (includesConcierge && userBlockedFromConcierge) {
inputPlaceholder = translate('reportActionCompose.blockedFromConcierge');
+ } else if (isAskConciergeChat) {
+ inputPlaceholder = translate('common.concierge.composerPlaceholder');
} else if (isExpenseRelatedReport && canUserPerformWriteAction && isEnglishLocale) {
inputPlaceholder = getRandomPlaceholder(translate);
}
diff --git a/src/pages/inbox/report/ReportActionItemCreated.tsx b/src/pages/inbox/report/ReportActionItemCreated.tsx
index 42e6a02e8e43..98a1c2181eed 100644
--- a/src/pages/inbox/report/ReportActionItemCreated.tsx
+++ b/src/pages/inbox/report/ReportActionItemCreated.tsx
@@ -3,6 +3,7 @@ import OfflineWithFeedback from '@components/OfflineWithFeedback';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import ReportWelcomeText from '@components/ReportWelcomeText';
+import useConciergeAskState from '@hooks/useConciergeAskState';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useIsInSidePanel from '@hooks/useIsInSidePanel';
import useLocalize from '@hooks/useLocalize';
@@ -24,6 +25,7 @@ import React, {memo} from 'react';
import {View} from 'react-native';
import AnimatedEmptyStateBackground from './AnimatedEmptyStateBackground';
+import ConciergeWelcome from './ConciergeWelcome';
type ReportActionItemCreatedProps = {
/** The id of the report */
@@ -55,11 +57,16 @@ function ReportActionItemCreated({reportID, policyID}: ReportActionItemCreatedPr
.map(Number)
.find((id) => id !== currentUserAccountID) ?? CONST.DEFAULT_NUMBER_ID;
const [isParticipantOptimistic = true] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: isOptimisticPersonalDetailSelector(otherParticipantAccountID)});
+ const {shouldShowWelcome} = useConciergeAskState(reportID);
if (!isChatReport(report)) {
return null;
}
+ if (shouldShowWelcome) {
+ return ;
+ }
+
const shouldDisableDetailPage = shouldDisableDetailPageReportUtils(report, isParticipantOptimistic);
return (
diff --git a/src/pages/inbox/report/ReportActionsList.tsx b/src/pages/inbox/report/ReportActionsList.tsx
index a733aa6ed3d3..8c47616fd636 100644
--- a/src/pages/inbox/report/ReportActionsList.tsx
+++ b/src/pages/inbox/report/ReportActionsList.tsx
@@ -2,6 +2,7 @@ import {renderScrollComponent as renderActionSheetAwareScrollView} from '@compon
import InvertedFlashList from '@components/FlashList/InvertedFlashList';
import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView';
+import useConciergeAskState from '@hooks/useConciergeAskState';
import useEnvironment from '@hooks/useEnvironment';
import useLinkedMessageOfflineLoading from '@hooks/useLinkedMessageOfflineLoading';
import useLocalize from '@hooks/useLocalize';
@@ -63,6 +64,7 @@ import {useRoute} from '@react-navigation/native';
import {isTrackIntentUserSelector} from '@selectors/Onboarding';
import React, {useEffect, useRef, useState} from 'react';
+import ConciergeChatHistoryToggle from './ConciergeChatHistoryToggle';
import FloatingMessageCounter from './FloatingMessageCounter';
import ReportActionIndexContext from './ReportActionIndexContext';
import {useReportActionsListActions, useReportActionsListState} from './ReportActionsListContext';
@@ -135,6 +137,7 @@ function ReportActionsListContent({reportID, conciergeChat, onLayout}: ReportAct
const route = useRoute>();
const reportActionIDFromRoute = route?.params?.reportActionID;
const {sessionStartTime} = useConciergeSessionState();
+ const {shouldShowWelcome: shouldShowConciergeWelcome} = useConciergeAskState(reportID);
const didLayout = useRef(false);
@@ -407,10 +410,18 @@ function ReportActionsListContent({reportID, conciergeChat, onLayout}: ReportAct
];
const listHeaderComponent = (
-
+ <>
+
+
+ >
);
const shouldShowOfflineSkeleton = isOffline && !sortedVisibleReportActions.some((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED);
@@ -469,7 +480,7 @@ function ReportActionsListContent({reportID, conciergeChat, onLayout}: ReportAct
keyExtractor={keyExtractor}
drawDistance={1500}
renderScrollComponent={renderActionSheetAwareScrollView}
- contentContainerStyle={styles.chatContentScrollView}
+ contentContainerStyle={[styles.chatContentScrollView, shouldShowConciergeWelcome && styles.chatContentScrollViewCentered]}
onEndReached={loadOlderChatsOnEndReached}
onEndReachedThreshold={0.75}
onStartReached={loadNewerChatsAfterTransitions}
diff --git a/src/pages/inbox/report/ReportFooter.tsx b/src/pages/inbox/report/ReportFooter.tsx
index 7d230e1f9128..1731dc74cd8b 100644
--- a/src/pages/inbox/report/ReportFooter.tsx
+++ b/src/pages/inbox/report/ReportFooter.tsx
@@ -4,7 +4,9 @@ import Banner from '@components/Banner';
import BlockedReportFooter from '@components/BlockedReportFooter';
import OfflineIndicator from '@components/OfflineIndicator';
import SwipeableView from '@components/SwipeableView';
+import Text from '@components/Text';
+import useConciergeAskState from '@hooks/useConciergeAskState';
import {useIsReportLoadPending} from '@hooks/useInFlightRequests';
import useIsAnonymousUser from '@hooks/useIsAnonymousUser';
import useIsReportReadyToDisplay from '@hooks/useIsReportReadyToDisplay';
@@ -37,6 +39,7 @@ import {isBlockedFromChatSelector} from '@selectors/BlockedFromChat';
import React from 'react';
import {Keyboard, View} from 'react-native';
+import ConciergeQuickActions from './ConciergeQuickActions';
import EnableNotificationsBanner, {BANNER_COMPOSER_OVERLAP_PX} from './EnableNotificationsBanner';
import ReportActionCompose from './ReportActionCompose/ReportActionCompose';
import SystemChatReportFooterMessage from './SystemChatReportFooterMessage';
@@ -92,6 +95,7 @@ function ReportFooter() {
const isSystemChat = isSystemChatUtil(report);
const isAdminsOnlyPostingRoom = isAdminsOnlyPostingRoomUtil(report);
const shouldShowComposerForActiveEditDraft = useShouldShowComposerForActiveEditDraft();
+ const {shouldShowWelcome: shouldShowConciergeWelcome, shouldLabelComposerAsNewQuestion} = useConciergeAskState(reportIDFromRoute);
if (!isCurrentReportLoadedFromOnyx || !report || !reportIDFromRoute) {
return null;
@@ -108,6 +112,7 @@ function ReportFooter() {
);
return (
+ {shouldLabelComposerAsNewQuestion && {translate('common.concierge.askNewQuestion')}}
{shouldShowEnableNotificationsBanner ? (
<>
@@ -116,6 +121,7 @@ function ReportFooter() {
) : (
composer
)}
+ {shouldShowConciergeWelcome && }
);
}
diff --git a/src/pages/inbox/report/ShowPreviousMessagesButton.tsx b/src/pages/inbox/report/ShowPreviousMessagesButton.tsx
index a79be7182b07..ab587d701a8e 100644
--- a/src/pages/inbox/report/ShowPreviousMessagesButton.tsx
+++ b/src/pages/inbox/report/ShowPreviousMessagesButton.tsx
@@ -3,6 +3,7 @@ import Button from '@components/ButtonComposed';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
+import usePermissions from '@hooks/usePermissions';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
@@ -33,12 +34,17 @@ function ShowPreviousMessagesButton({reportID, actionType, hasPreviousMessages,
const styles = useThemeStyles();
const {translate} = useLocalize();
const expensifyIcons = useMemoizedLazyExpensifyIcons(['UpArrow']);
+ const {isBetaEnabled} = usePermissions();
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
const isConciergeChat = reportID === conciergeReportID;
if (!isConciergeChat) {
return null;
}
+ // ConciergeChatHistoryToggle takes over in the Ask Concierge design, where the control also collapses the history again.
+ if (isBetaEnabled(CONST.BETAS.CONCIERGE_RESPOND_IN_THREAD)) {
+ return null;
+ }
if (!onPress) {
return null;
}
@@ -60,7 +66,7 @@ function ShowPreviousMessagesButton({reportID, actionType, hasPreviousMessages,
size={CONST.BUTTON_SIZE.SMALL}
onPress={onPress}
>
- {translate('common.concierge.showHistory')}
+ {translate('common.concierge.viewChatHistory')}
diff --git a/src/styles/index.ts b/src/styles/index.ts
index 4af9fe456c0c..2b2ffa55bfe7 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -2242,6 +2242,11 @@ const staticStyles = (theme: ThemeColors) =>
...chatContentScrollViewPlatformStyles,
},
+ // The list is inverted, so centering here lifts a short welcome screen off the composer and into the middle of the chat.
+ chatContentScrollViewCentered: {
+ justifyContent: 'center',
+ },
+
// Chat Item
chatItem: {
display: 'flex',
diff --git a/tests/ui/ReportActionsListTest.tsx b/tests/ui/ReportActionsListTest.tsx
index 0b1aac4cf61e..c68e081a6307 100644
--- a/tests/ui/ReportActionsListTest.tsx
+++ b/tests/ui/ReportActionsListTest.tsx
@@ -300,7 +300,7 @@ describe('ReportActionsList (body)', () => {
revealDraftFromReportAction: jest.fn(),
});
mockUseConciergeSessionState.mockReturnValue({sessionStartTime: null, showFullHistory: false, hadMessagesAtSessionStart: false});
- mockUseConciergeSessionActions.mockReturnValue({startSession: jest.fn(), setShowFullHistory: jest.fn(), setHadMessagesAtSessionStart: jest.fn()});
+ mockUseConciergeSessionActions.mockReturnValue({startSession: jest.fn(), resetSession: jest.fn(), setShowFullHistory: jest.fn(), setHadMessagesAtSessionStart: jest.fn()});
mockUseOnyx.mockImplementation((key: string, options) => {
// useReportActionsListModel derives app-load state from the request queue via useIsAppLoadPending,
@@ -791,7 +791,7 @@ describe('ReportActionsList (body)', () => {
mockUseIsInSidePanel.mockReturnValue(false);
mockUseSidePanelState.mockReturnValue(defaultSidePanelState);
mockUseConciergeSessionState.mockReturnValue({sessionStartTime, showFullHistory, hadMessagesAtSessionStart: false});
- mockUseConciergeSessionActions.mockReturnValue({startSession: jest.fn(), setShowFullHistory: jest.fn(), setHadMessagesAtSessionStart: jest.fn()});
+ mockUseConciergeSessionActions.mockReturnValue({startSession: jest.fn(), resetSession: jest.fn(), setShowFullHistory: jest.fn(), setHadMessagesAtSessionStart: jest.fn()});
mockUseOnyx.mockImplementation((key: string, options) => {
if (key === ONYXKEYS.CONCIERGE_REPORT_ID) {
@@ -953,9 +953,9 @@ describe('ReportActionsList (body)', () => {
it('should call startSession on mount for main DM concierge', () => {
const mockStartSession = jest.fn();
- mockUseConciergeSessionActions.mockReturnValue({startSession: mockStartSession, setShowFullHistory: jest.fn(), setHadMessagesAtSessionStart: jest.fn()});
+ mockUseConciergeSessionActions.mockReturnValue({startSession: mockStartSession, resetSession: jest.fn(), setShowFullHistory: jest.fn(), setHadMessagesAtSessionStart: jest.fn()});
setupMainDMConciergeMocks();
- mockUseConciergeSessionActions.mockReturnValue({startSession: mockStartSession, setShowFullHistory: jest.fn(), setHadMessagesAtSessionStart: jest.fn()});
+ mockUseConciergeSessionActions.mockReturnValue({startSession: mockStartSession, resetSession: jest.fn(), setShowFullHistory: jest.fn(), setHadMessagesAtSessionStart: jest.fn()});
mockUsePaginatedReportActions.mockReturnValue({
...defaultPaginatedReportActionsResult,
diff --git a/tests/unit/ShowPreviousMessagesButtonTest.tsx b/tests/unit/ShowPreviousMessagesButtonTest.tsx
index 301da70b4d17..8203eee4caec 100644
--- a/tests/unit/ShowPreviousMessagesButtonTest.tsx
+++ b/tests/unit/ShowPreviousMessagesButtonTest.tsx
@@ -96,4 +96,15 @@ describe('ShowPreviousMessagesButton', () => {
renderButton({showFullHistory: true});
expect(screen.queryByRole('button')).toBeNull();
});
+
+ it('renders nothing when the Ask Concierge beta is enabled', async () => {
+ await Onyx.set(ONYXKEYS.BETAS, [CONST.BETAS.CONCIERGE_RESPOND_IN_THREAD]);
+ await waitForBatchedUpdates();
+
+ renderButton();
+ expect(screen.queryByRole('button')).toBeNull();
+
+ await Onyx.set(ONYXKEYS.BETAS, []);
+ await waitForBatchedUpdates();
+ });
});