From 60ba12aeab8b2e6e0b2e950388b721d967c788a9 Mon Sep 17 00:00:00 2001 From: gijoe0295 <153004152+gijoe0295@users.noreply.github.com> Date: Fri, 29 May 2026 00:55:50 +0700 Subject: [PATCH 01/44] feat: ai feature promo modal navigation, basic component and guards --- src/CONST/index.ts | 8 + src/NAVIGATORS.ts | 1 + src/ROUTES.ts | 5 + src/SCREENS.ts | 4 + src/components/AIFeaturesPromoModal/index.tsx | 34 ++++ .../Navigation/AppNavigator/AuthScreens.tsx | 6 + .../AIFeaturesPromoModalNavigator.tsx | 26 +++ .../Navigation/guards/AIFeaturesPromoGuard.ts | 174 ++++++++++++++++++ src/libs/Navigation/guards/index.ts | 4 + src/libs/Navigation/linkingConfig/config.ts | 9 + src/libs/Navigation/types.ts | 6 + src/types/onyx/DismissedProductTraining.ts | 5 + 12 files changed, 282 insertions(+) create mode 100644 src/components/AIFeaturesPromoModal/index.tsx create mode 100644 src/libs/Navigation/AppNavigator/Navigators/AIFeaturesPromoModalNavigator.tsx create mode 100644 src/libs/Navigation/guards/AIFeaturesPromoGuard.ts diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 78b741d7aec0..12eecd80e920 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -9000,6 +9000,14 @@ const CONST = { MIGRATED_USER_WELCOME_MODAL: 'migratedUserWelcomeModal', + AI_FEATURES_PROMO_MODAL: 'aiFeaturesPromoModal', + + AI_FEATURES_PROMO_LEARN_MORE_URLS: { + SPEND_ANALYSIS: 'https://help.expensify.com/articles/new-expensify/concierge-ai/How-Concierge-Analyzes-Spend', + EXPENSE_ASSISTANT: 'https://help.expensify.com/articles/new-expensify/concierge-ai/Expense-Assistant', + BUILD_AGENTS: 'https://help.expensify.com/articles/new-expensify/concierge-ai/Expense-Assistant', + }, + BASE_LIST_ITEM_TEST_ID: 'base-list-item-', SELECTION_BUTTON_TEST_ID: 'selection-button-', PRODUCT_TRAINING_TOOLTIP_NAMES: { diff --git a/src/NAVIGATORS.ts b/src/NAVIGATORS.ts index 23f0755290cc..6e93648f621a 100644 --- a/src/NAVIGATORS.ts +++ b/src/NAVIGATORS.ts @@ -9,6 +9,7 @@ export default { FEATURE_TRAINING_MODAL_NAVIGATOR: 'FeatureTrainingModalNavigator', EXPLANATION_MODAL_NAVIGATOR: 'ExplanationModalNavigator', MIGRATED_USER_MODAL_NAVIGATOR: 'MigratedUserModalNavigator', + AI_FEATURES_PROMO_MODAL_NAVIGATOR: 'AIFeaturesPromoModalNavigator', TEST_DRIVE_MODAL_NAVIGATOR: 'TestDriveModalNavigator', TEST_DRIVE_DEMO_NAVIGATOR: 'TestDriveDemoNavigator', REPORTS_SPLIT_NAVIGATOR: 'ReportsSplitNavigator', diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 3966b0b9e5a8..0d51153b9588 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -3328,6 +3328,11 @@ const ROUTES = { getRoute: (backTo?: string) => getUrlWithBackToParam('onboarding/migrated-user-welcome', backTo, false), }, + AI_FEATURES_PROMO_MODAL: { + route: 'ai-features-promo', + + getRoute: (backTo?: string) => getUrlWithBackToParam('ai-features-promo', backTo, false), + }, TRANSACTION_RECEIPT: { route: 'r/:reportID/transaction/:transactionID/receipt/:action?/:iouType?', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 8eaebf73b0d1..0db9cd64ef75 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -960,6 +960,10 @@ const SCREENS = { ROOT: 'MigratedUserWelcomeModal_Root', }, + AI_FEATURES_PROMO_MODAL: { + ROOT: 'AIFeaturesPromoModal_Root', + }, + TEST_DRIVE_MODAL: { ROOT: 'TestDrive_Modal_Root', }, diff --git a/src/components/AIFeaturesPromoModal/index.tsx b/src/components/AIFeaturesPromoModal/index.tsx new file mode 100644 index 000000000000..1dda5ecb0ca6 --- /dev/null +++ b/src/components/AIFeaturesPromoModal/index.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import FeatureTrainingModal from '@components/FeatureTrainingModal'; +import LottieAnimations from '@components/LottieAnimations'; +import useLocalize from '@hooks/useLocalize'; +import {dismissProductTraining} from '@libs/actions/Welcome'; +import Log from '@libs/Log'; +import CONST from '@src/CONST'; + +function AIFeaturesPromoModal() { + const {translate} = useLocalize(); + + const onClose = () => { + Log.hmmm('[AIFeaturesPromoModal] onClose called, dismissing product training'); + dismissProductTraining(CONST.AI_FEATURES_PROMO_MODAL, true); + }; + + const onConfirm = () => { + Log.hmmm('[AIFeaturesPromoModal] onConfirm called, dismissing product training'); + dismissProductTraining(CONST.AI_FEATURES_PROMO_MODAL); + }; + + return ( + + ); +} + +export default AIFeaturesPromoModal; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 721f8814fc15..072b627776fd 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -55,6 +55,7 @@ import DelegatorConnectGuard from './DelegatorConnectGate'; import hideKeyboardOnSwipe from './hideKeyboardOnSwipe'; import KeyboardShortcutsHandler from './KeyboardShortcutsHandler'; import {ShareModalStackNavigator} from './ModalStackNavigators'; +import AIFeaturesPromoModalNavigator from './Navigators/AIFeaturesPromoModalNavigator'; import ExplanationModalNavigator from './Navigators/ExplanationModalNavigator'; import FeatureTrainingModalNavigator from './Navigators/FeatureTrainingModalNavigator'; import MigratedUserWelcomeModalNavigator from './Navigators/MigratedUserWelcomeModalNavigator'; @@ -309,6 +310,11 @@ function AuthScreens() { options={rootNavigatorScreenOptions.basicModalNavigator} component={MigratedUserWelcomeModalNavigator} /> + (); + +function AIFeaturesPromoModalNavigator() { + return ( + + + + + + + + ); +} + +export default AIFeaturesPromoModalNavigator; diff --git a/src/libs/Navigation/guards/AIFeaturesPromoGuard.ts b/src/libs/Navigation/guards/AIFeaturesPromoGuard.ts new file mode 100644 index 000000000000..192b89324c0e --- /dev/null +++ b/src/libs/Navigation/guards/AIFeaturesPromoGuard.ts @@ -0,0 +1,174 @@ +import type {NavigationAction, NavigationState} from '@react-navigation/native'; +import {findFocusedRoute} from '@react-navigation/native'; +import {hasCompletedGuidedSetupFlowSelector, tryNewDotOnyxSelector} from '@selectors/Onboarding'; +import Onyx from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import Log from '@libs/Log'; +import Navigation from '@libs/Navigation/Navigation'; +import isProductTrainingElementDismissed from '@libs/TooltipUtils'; +import CONST from '@src/CONST'; +import NAVIGATORS from '@src/NAVIGATORS'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; +import type {DismissedProductTraining, Onboarding, Session} from '@src/types/onyx'; +import type {GuardResult, NavigationGuard} from './types'; + +let session: OnyxEntry; +let isLoadingApp = true; + +let dismissedProductTraining: OnyxEntry; +let isDismissedProductTrainingLoaded = false; + +let hasBeenAddedToNudgeMigration = false; +let isTryNewDotLoaded = false; + +let onboarding: OnyxEntry; +let isOnboardingLoaded = false; + +let hasRedirectedToAIFeaturesPromoModal = false; + +/** + * Same-session protection. + * + * Per the issue requirements, the AI features promo modal must not appear in the same + * session as the migration welcome modal or the onboarding flow. These flags trip when + * we observe either of those modals being "active" (pending dismissal / not yet + * completed) at any point during this process lifetime, and suppress the AI promo for + * the rest of the session. + */ +let observedActiveMigrationModalThisSession = false; +let observedActiveOnboardingThisSession = false; + +function resetSessionFlag() { + hasRedirectedToAIFeaturesPromoModal = false; + observedActiveMigrationModalThisSession = false; + observedActiveOnboardingThisSession = false; +} + +/** + * Proactively navigate to the AI features promo modal when all conditions are met. + * Waits for the gating NVPs to load to avoid racing with the migration / onboarding guards. + */ +function navigateToAIFeaturesPromoModalIfReady() { + if ( + !session?.authToken || + isLoadingApp || + hasRedirectedToAIFeaturesPromoModal || + !isDismissedProductTrainingLoaded || + !isTryNewDotLoaded || + !isOnboardingLoaded || + isProductTrainingElementDismissed(CONST.AI_FEATURES_PROMO_MODAL, dismissedProductTraining) || + observedActiveMigrationModalThisSession || + observedActiveOnboardingThisSession + ) { + return; + } + + Log.info('[AIFeaturesPromoGuard] Proactively navigating to AI features promo modal'); + hasRedirectedToAIFeaturesPromoModal = true; + Navigation.navigate(ROUTES.AI_FEATURES_PROMO_MODAL.getRoute()); +} + +/** + * Called by guards/index.ts when session or loading app state changes. + * Reuses the shared Onyx subscriptions from guards/index.ts to avoid duplicate connections. + */ +function onSessionOrLoadingAppChanged(sessionValue: OnyxEntry, isLoadingAppValue: boolean) { + session = sessionValue; + isLoadingApp = isLoadingAppValue; + navigateToAIFeaturesPromoModalIfReady(); +} + +Onyx.connectWithoutView({ + key: ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, + callback: (value) => { + dismissedProductTraining = value; + isDismissedProductTrainingLoaded = true; + if (isProductTrainingElementDismissed(CONST.AI_FEATURES_PROMO_MODAL, value)) { + hasRedirectedToAIFeaturesPromoModal = false; + } + // If the migration welcome modal is currently still pending, suppress AI promo this session. + if (hasBeenAddedToNudgeMigration && !isProductTrainingElementDismissed(CONST.MIGRATED_USER_WELCOME_MODAL, value)) { + observedActiveMigrationModalThisSession = true; + } + navigateToAIFeaturesPromoModalIfReady(); + }, +}); + +Onyx.connectWithoutView({ + key: ONYXKEYS.NVP_TRY_NEW_DOT, + callback: (value) => { + const result = value ? tryNewDotOnyxSelector(value) : undefined; + hasBeenAddedToNudgeMigration = result?.hasBeenAddedToNudgeMigration ?? false; + isTryNewDotLoaded = true; + if (hasBeenAddedToNudgeMigration && !isProductTrainingElementDismissed(CONST.MIGRATED_USER_WELCOME_MODAL, dismissedProductTraining)) { + observedActiveMigrationModalThisSession = true; + } + navigateToAIFeaturesPromoModalIfReady(); + }, +}); + +Onyx.connectWithoutView({ + key: ONYXKEYS.NVP_ONBOARDING, + callback: (value) => { + onboarding = value; + isOnboardingLoaded = true; + if (!hasCompletedGuidedSetupFlowSelector(onboarding)) { + observedActiveOnboardingThisSession = true; + } + navigateToAIFeaturesPromoModalIfReady(); + }, +}); + +/** + * Block navigation while the AI features promo modal is active (on top of the stack). + * Mirrors the pattern from MigratedUserWelcomeModalGuard. + */ +function shouldBlockWhileModalActive(state: NavigationState, action: NavigationAction): boolean { + const isAllowedAction = action.type === CONST.NAVIGATION.ACTION_TYPE.DISMISS_MODAL || action.type === CONST.NAVIGATION.ACTION_TYPE.GO_BACK; + return ( + hasRedirectedToAIFeaturesPromoModal && + !isProductTrainingElementDismissed(CONST.AI_FEATURES_PROMO_MODAL, dismissedProductTraining) && + state.routes.at(-1)?.name === NAVIGATORS.AI_FEATURES_PROMO_MODAL_NAVIGATOR && + !isAllowedAction + ); +} + +/** Prevents redirect loops by detecting when we're already on or resetting to the modal. */ +function isNavigatingToAIFeaturesPromoModal(state: NavigationState, action: NavigationAction): boolean { + const isOnModal = findFocusedRoute(state)?.name === SCREENS.AI_FEATURES_PROMO_MODAL.ROOT; + const isResettingToModal = action.type === 'RESET' && !!action.payload && findFocusedRoute(action.payload as NavigationState)?.name === SCREENS.AI_FEATURES_PROMO_MODAL.ROOT; + + return isOnModal || isResettingToModal; +} + +/** + * AIFeaturesPromoGuard surfaces the one-time AI features promo modal. + * + * This guard relies on the proactive Onyx-driven path (navigateToAIFeaturesPromoModalIfReady) + * rather than redirecting from evaluate(), because it needs to wait for higher-priority guards + * (Onboarding, MigratedUserWelcomeModal) to settle before deciding whether to fire. + */ +const AIFeaturesPromoGuard: NavigationGuard = { + name: 'AIFeaturesPromoGuard', + + evaluate: (state: NavigationState, action: NavigationAction, context): GuardResult => { + if (context.isLoading) { + return {type: 'ALLOW'}; + } + + if (shouldBlockWhileModalActive(state, action)) { + return {type: 'BLOCK', reason: '[AIFeaturesPromoGuard] Blocking navigation while AI features promo modal is active'}; + } + + if (isNavigatingToAIFeaturesPromoModal(state, action) || hasRedirectedToAIFeaturesPromoModal) { + return {type: 'ALLOW'}; + } + + return {type: 'ALLOW'}; + }, +}; + +export default AIFeaturesPromoGuard; +export {resetSessionFlag, onSessionOrLoadingAppChanged}; diff --git a/src/libs/Navigation/guards/index.ts b/src/libs/Navigation/guards/index.ts index fdc43fd6440a..e60ba997d016 100644 --- a/src/libs/Navigation/guards/index.ts +++ b/src/libs/Navigation/guards/index.ts @@ -4,6 +4,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import getCurrentUrl from '@libs/Navigation/currentUrl'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Session} from '@src/types/onyx'; +import AIFeaturesPromoGuard, {onSessionOrLoadingAppChanged as onAIFeaturesPromoSessionOrLoadingAppChanged} from './AIFeaturesPromoGuard'; import MigratedUserWelcomeModalGuard, {onSessionOrLoadingAppChanged} from './MigratedUserWelcomeModalGuard'; import OnboardingGuard from './OnboardingGuard'; import type {GuardContext, GuardResult, NavigationGuard} from './types'; @@ -20,6 +21,7 @@ Onyx.connectWithoutView({ callback: (value) => { session = value; onSessionOrLoadingAppChanged(session, isLoadingApp); + onAIFeaturesPromoSessionOrLoadingAppChanged(session, isLoadingApp); }, }); @@ -28,6 +30,7 @@ Onyx.connectWithoutView({ callback: (value) => { isLoadingApp = value ?? true; onSessionOrLoadingAppChanged(session, isLoadingApp); + onAIFeaturesPromoSessionOrLoadingAppChanged(session, isLoadingApp); }, }); @@ -103,5 +106,6 @@ function clearGuards(): void { registerGuard(OnboardingGuard); registerGuard(MigratedUserWelcomeModalGuard); +registerGuard(AIFeaturesPromoGuard); export {registerGuard, createGuardContext, evaluateGuards, getRegisteredGuards, clearGuards}; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index f1a5498123d7..6ec8c9a3f6f6 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -67,6 +67,15 @@ const config: LinkingOptions['config'] = { }, }, + [NAVIGATORS.AI_FEATURES_PROMO_MODAL_NAVIGATOR]: { + screens: { + [SCREENS.AI_FEATURES_PROMO_MODAL.ROOT]: { + path: ROUTES.AI_FEATURES_PROMO_MODAL.route, + exact: true, + }, + }, + }, + [NAVIGATORS.TEST_DRIVE_MODAL_NAVIGATOR]: { screens: { [SCREENS.TEST_DRIVE_MODAL.ROOT]: { diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 675cff9a5059..12be8b30c30f 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -2916,6 +2916,10 @@ type MigratedUserModalNavigatorParamList = { [SCREENS.MIGRATED_USER_WELCOME_MODAL.ROOT]: undefined; }; +type AIFeaturesPromoModalNavigatorParamList = { + [SCREENS.AI_FEATURES_PROMO_MODAL.ROOT]: undefined; +}; + type TestDriveModalNavigatorParamList = { [SCREENS.TEST_DRIVE_MODAL.ROOT]: { bossEmail?: string; @@ -3092,6 +3096,7 @@ type AuthScreensParamList = SharedScreensParamList & [NAVIGATORS.FEATURE_TRAINING_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.EXPLANATION_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.MIGRATED_USER_MODAL_NAVIGATOR]: NavigatorScreenParams; + [NAVIGATORS.AI_FEATURES_PROMO_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.TEST_DRIVE_MODAL_NAVIGATOR]: NavigatorScreenParams; [NAVIGATORS.TEST_DRIVE_DEMO_NAVIGATOR]: NavigatorScreenParams; [SCREENS.CONNECTION_COMPLETE]: undefined; @@ -3368,6 +3373,7 @@ export type { WorkspaceSplitNavigatorParamList, WorkspaceNavigatorParamList, MigratedUserModalNavigatorParamList, + AIFeaturesPromoModalNavigatorParamList, WorkspaceConfirmationNavigatorParamList, WorkspaceDuplicateNavigatorParamList, PolicyCopySettingsNavigatorParamList, diff --git a/src/types/onyx/DismissedProductTraining.ts b/src/types/onyx/DismissedProductTraining.ts index 7cfb6f999011..17c13e6c9112 100644 --- a/src/types/onyx/DismissedProductTraining.ts +++ b/src/types/onyx/DismissedProductTraining.ts @@ -22,6 +22,11 @@ type DismissedProductTraining = { */ [CONST.MIGRATED_USER_WELCOME_MODAL]: DismissedProductTrainingElement; + /** + * When user dismisses the AI features promo modal, we store the timestamp here. + */ + [CONST.AI_FEATURES_PROMO_MODAL]: DismissedProductTrainingElement; + // TODO: CONCIERGE_LHN_GBR tooltip will be replaced by a tooltip in the #admins room // https://github.com/Expensify/App/issues/57045#issuecomment-2701455668 /** From 3b61e1e0ed926ba0dff7361c5c20f4d1d5d3c6d6 Mon Sep 17 00:00:00 2001 From: gijoe0295 <153004152+gijoe0295@users.noreply.github.com> Date: Fri, 29 May 2026 00:58:32 +0700 Subject: [PATCH 02/44] add English terms --- src/languages/en.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index 2c964abfbbee..f10457e74c74 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -9231,6 +9231,28 @@ const translations = { chat: 'Chat on any expense to resolve questions quickly', }, }, + aiFeaturesPromoModal: { + title: 'Meet Concierge AI', + description: 'Your new AI-powered Expensify assistant.', + letsGo: "Let's go", + learnMore: 'Learn more', + next: 'Next', + back: 'Back', + screens: { + spendAnalysis: { + title: 'Interactive spend analysis', + description: 'Ask Concierge to break down your spending however you want — by category, person, project, or time.', + }, + expenseAssistant: { + title: 'Meet your new expense assistant', + description: 'Concierge can categorize expenses, fill in details, and follow up on missing receipts for you.', + }, + buildAgents: { + title: 'Build your own agents', + description: 'Create custom AI agents to automate the workflows that matter most to your team.', + }, + }, + }, productTrainingTooltip: { // TODO: CONCIERGE_LHN_GBR tooltip will be replaced by a tooltip in the #admins room // https://github.com/Expensify/App/issues/57045#issuecomment-2701455668 From 847a4fbfe6265c51319b9724d6bb3e54d3b96556 Mon Sep 17 00:00:00 2001 From: gijoe0295 <153004152+gijoe0295@users.noreply.github.com> Date: Tue, 2 Jun 2026 00:14:30 +0700 Subject: [PATCH 03/44] run translation script --- src/languages/de.ts | 22 ++++++++++++++++++++++ src/languages/es.ts | 16 ++++++++++++++++ src/languages/fr.ts | 19 +++++++++++++++++++ src/languages/it.ts | 19 +++++++++++++++++++ src/languages/ja.ts | 16 ++++++++++++++++ src/languages/nl.ts | 19 +++++++++++++++++++ src/languages/pl.ts | 19 +++++++++++++++++++ src/languages/pt-BR.ts | 16 ++++++++++++++++ src/languages/zh-hans.ts | 13 +++++++++++++ 9 files changed, 159 insertions(+) diff --git a/src/languages/de.ts b/src/languages/de.ts index 57b6dfb756eb..0efacdba3fbc 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -9594,5 +9594,27 @@ Hier ist ein *Testbeleg*, um dir zu zeigen, wie es funktioniert:`, negativeButton: 'Nicht wirklich', }, monthPickerPage: {month: 'Monat', selectMonth: 'Bitte wählen Sie einen Monat aus'}, + aiFeaturesPromoModal: { + title: 'Lernen Sie Concierge AI kennen', + description: 'Ihr neuer KI-gestützter Expensify-Assistent.', + letsGo: "Los geht's", + learnMore: 'Mehr erfahren', + next: 'Weiter', + back: 'Zurück', + screens: { + spendAnalysis: { + title: 'Interaktive Ausgabenanalyse', + description: 'Bitten Sie Concierge, Ihre Ausgaben ganz nach Ihren Wünschen aufzuschlüsseln – nach Kategorie, Person, Projekt oder Zeitraum.', + }, + expenseAssistant: { + title: 'Lernen Sie Ihre neue Spesenassistenz kennen', + description: 'Concierge kann Ausgaben kategorisieren, Details ausfüllen und fehlenden Belegen für Sie nachgehen.', + }, + buildAgents: { + title: 'Erstellen Sie Ihre eigenen Agents', + description: 'Erstellen Sie benutzerdefinierte KI-Agenten, um die Workflows zu automatisieren, die für Ihr Team am wichtigsten sind.', + }, + }, + }, }; export default translations; diff --git a/src/languages/es.ts b/src/languages/es.ts index f94ab5bdd7e5..20b26c068662 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -9839,5 +9839,21 @@ ${amount} para ${merchant} - ${date}`, lockScreenTrackingText: 'Siguiendo...', }, }, + aiFeaturesPromoModal: { + title: 'Conoce Concierge AI', + description: 'Tu nuevo asistente de Expensify potenciado por IA.', + letsGo: 'Vamos allá', + learnMore: 'Más información', + next: 'Siguiente', + back: 'Atrás', + screens: { + spendAnalysis: {title: 'Análisis interactivo del gasto', description: 'Pídele a Concierge que desglosE tus gastos como quieras: por categoría, persona, proyecto o periodo.'}, + expenseAssistant: { + title: 'Conoce a tu nuevo asistente de gastos', + description: 'Concierge puede categorizar gastos, completar detalles y hacer seguimiento de los recibos que falten por ti.', + }, + buildAgents: {title: 'Crea tus propios agentes', description: 'Crea agentes de IA personalizados para automatizar los flujos de trabajo que más le importan a tu equipo.'}, + }, + }, }; export default translations; diff --git a/src/languages/fr.ts b/src/languages/fr.ts index ffe8fe432ab7..e26aab6249bf 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -9629,5 +9629,24 @@ Voici un *reçu test* pour vous montrer comment ça fonctionne :`, negativeButton: 'Pas vraiment', }, monthPickerPage: {month: 'Mois', selectMonth: 'Veuillez sélectionner un mois'}, + aiFeaturesPromoModal: { + title: 'Découvrez Concierge IA', + description: 'Votre nouvel assistant Expensify, optimisé par l’IA.', + letsGo: 'Allons-y', + learnMore: 'En savoir plus', + next: 'Suivant', + back: 'Retour', + screens: { + spendAnalysis: { + title: 'Analyse interactive des dépenses', + description: 'Demandez à Concierge de ventiler vos dépenses comme vous le souhaitez — par catégorie, personne, projet ou période.', + }, + expenseAssistant: { + title: 'Découvrez votre nouvel assistant de dépenses', + description: 'Concierge peut catégoriser les dépenses, renseigner les détails et faire le suivi des reçus manquants pour vous.', + }, + buildAgents: {title: 'Créez vos propres agents', description: 'Créez des agents IA personnalisés pour automatiser les workflows qui comptent le plus pour votre équipe.'}, + }, + }, }; export default translations; diff --git a/src/languages/it.ts b/src/languages/it.ts index 14a51b1a27a3..55b42c0d0b53 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -9583,5 +9583,24 @@ Ecco una *ricevuta di prova* per mostrarti come funziona:`, negativeButton: 'Non proprio', }, monthPickerPage: {month: 'Mese', selectMonth: 'Seleziona un mese'}, + aiFeaturesPromoModal: { + title: 'Incontra Concierge AI', + description: 'Il tuo nuovo assistente Expensify con tecnologia AI.', + letsGo: 'Andiamo', + learnMore: 'Scopri di più', + next: 'Avanti', + back: 'Indietro', + screens: { + spendAnalysis: { + title: 'Analisi interattiva delle spese', + description: 'Chiedi a Concierge di suddividere le tue spese come vuoi tu — per categoria, persona, progetto o periodo.', + }, + expenseAssistant: { + title: 'Scopri il tuo nuovo assistente per le spese', + description: 'Concierge può categorizzare le spese, compilare i dettagli e sollecitare le ricevute mancanti per te.', + }, + buildAgents: {title: 'Crea i tuoi agenti', description: 'Crea agenti IA personalizzati per automatizzare i flussi di lavoro più importanti per il tuo team.'}, + }, + }, }; export default translations; diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 979a87f17b04..4118c5aa3e4c 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -9458,5 +9458,21 @@ ${reportName} negativeButton: 'そうでもありません', }, monthPickerPage: {month: '月', selectMonth: '月を選択してください'}, + aiFeaturesPromoModal: { + title: 'Concierge AI を紹介します', + description: '新しい AI 搭載の Expensify アシスタントです。', + letsGo: '始めましょう', + learnMore: '詳細はこちら', + next: '次へ', + back: '戻る', + screens: { + spendAnalysis: {title: 'インタラクティブな支出分析', description: 'カテゴリ、人、プロジェクト、期間など、必要な切り口で支出を分解するよう Concierge に依頼できます。'}, + expenseAssistant: { + title: '新しい経費アシスタントをご紹介します', + description: 'Concierge は、経費のカテゴリ分けや詳細の入力、不足しているレシートのフォローアップを代わりに行います。', + }, + buildAgents: {title: '独自のエージェントを作成する', description: 'チームにとって最も重要なワークフローを自動化するカスタム AI エージェントを作成しましょう。'}, + }, + }, }; export default translations; diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 8540c1d87bdb..a0ceaa0758ba 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -9549,5 +9549,24 @@ Hier is een *proefbon* om je te laten zien hoe het werkt:`, negativeButton: 'Niet echt', }, monthPickerPage: {month: 'Maand', selectMonth: 'Selecteer een maand'}, + aiFeaturesPromoModal: { + title: 'Maak kennis met Concierge AI', + description: 'Je nieuwe door AI aangestuurde Expensify-assistent.', + letsGo: 'Laten we gaan', + learnMore: 'Meer informatie', + next: 'Volgende', + back: 'Terug', + screens: { + spendAnalysis: { + title: 'Interactieve uitgavenanalyse', + description: 'Vraag Concierge om je uitgaven op elke gewenste manier op te splitsen — per categorie, persoon, project of tijd.', + }, + expenseAssistant: { + title: 'Maak kennis met je nieuwe onkostenassistent', + description: 'Concierge kan uitgaven categoriseren, details invullen en ontbrekende bonnen voor je opvolgen.', + }, + buildAgents: {title: 'Bouw je eigen agents', description: 'Maak aangepaste AI-agents om de workflows te automatiseren die het belangrijkst zijn voor je team.'}, + }, + }, }; export default translations; diff --git a/src/languages/pl.ts b/src/languages/pl.ts index bdbaf80e8efa..d0c90d123127 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -9528,5 +9528,24 @@ Oto *paragon testowy*, żeby pokazać Ci, jak to działa:`, negativeButton: 'Niekoniecznie', }, monthPickerPage: {month: 'Miesiąc', selectMonth: 'Wybierz miesiąc'}, + aiFeaturesPromoModal: { + title: 'Poznaj Concierge AI', + description: 'Twój nowy asystent Expensify zasilany AI.', + letsGo: 'Zaczynajmy', + learnMore: 'Dowiedz się więcej', + next: 'Dalej', + back: 'Wstecz', + screens: { + spendAnalysis: { + title: 'Interaktywna analiza wydatków', + description: 'Poproś Concierge o rozbicie swoich wydatków tak, jak chcesz — według kategorii, osoby, projektu lub czasu.', + }, + expenseAssistant: { + title: 'Poznaj swojego nowego asystenta wydatków', + description: 'Concierge może kategoryzować wydatki, uzupełniać szczegóły i śledzić brakujące paragony za ciebie.', + }, + buildAgents: {title: 'Zbuduj własne agentów', description: 'Twórz własne agentki AI, żeby automatyzować procesy, które są dla twojego zespołu najważniejsze.'}, + }, + }, }; export default translations; diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index cdfe462ac026..6713140189f4 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -9539,5 +9539,21 @@ Aqui está um *comprovante de teste* para mostrar como funciona:`, negativeButton: 'Na verdade, não', }, monthPickerPage: {month: 'Mês', selectMonth: 'Selecione um mês por favor'}, + aiFeaturesPromoModal: { + title: 'Conheça a Concierge AI', + description: 'Seu novo assistente Expensify com tecnologia de IA.', + letsGo: 'Vamos lá', + learnMore: 'Saiba mais', + next: 'Próximo', + back: 'Voltar', + screens: { + spendAnalysis: {title: 'Análise interativa de gastos', description: 'Peça ao Concierge para detalhar seus gastos como você quiser — por categoria, pessoa, projeto ou período.'}, + expenseAssistant: { + title: 'Conheça seu novo assistente de despesas', + description: 'O Concierge pode categorizar despesas, preencher detalhes e acompanhar recibos ausentes para você.', + }, + buildAgents: {title: 'Crie seus próprios agentes', description: 'Crie agentes de IA personalizados para automatizar os fluxos de trabalho que mais importam para a sua equipe.'}, + }, + }, }; export default translations; diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 95a993c47bf1..dbdfd8e0cce6 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -9277,5 +9277,18 @@ ${reportName} }, proactiveAppReview: {title: '喜欢全新的 Expensify 吗?', description: '请告诉我们,这样我们就能帮助您让报销体验变得更好。', positiveButton: '太棒了!', negativeButton: '不太是'}, monthPickerPage: {month: '月份', selectMonth: '请选择月份'}, + aiFeaturesPromoModal: { + title: '了解 Concierge AI', + description: '您全新的 AI 驱动 Expensify 助理。', + letsGo: '开始使用', + learnMore: '了解详情', + next: '下一步', + back: '返回', + screens: { + spendAnalysis: {title: '交互式支出分析', description: '让 Concierge 按你想要的方式细分支出——按类别、人员、项目或时间。'}, + expenseAssistant: {title: '认识你的新报销助手', description: 'Concierge 可以帮你分类报销、填写详情,并跟进缺失的收据。'}, + buildAgents: {title: '构建你自己的代理', description: '创建自定义 AI 代理,自动化对你的团队最重要的工作流程。'}, + }, + }, }; export default translations; From dc884962769efc7bd09137638b0f9c83ca649c48 Mon Sep 17 00:00:00 2001 From: gijoe0295 <153004152+gijoe0295@users.noreply.github.com> Date: Tue, 2 Jun 2026 23:39:34 +0700 Subject: [PATCH 04/44] remove deprecated getUrlWithBackToParam --- src/ROUTES.ts | 6 +----- src/libs/Navigation/guards/AIFeaturesPromoGuard.ts | 2 +- src/libs/Navigation/linkingConfig/config.ts | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index b58efd3b6367..4e6ece62015c 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -3375,11 +3375,7 @@ const ROUTES = { getRoute: (backTo?: string) => getUrlWithBackToParam('onboarding/migrated-user-welcome', backTo, false), }, - AI_FEATURES_PROMO_MODAL: { - route: 'ai-features-promo', - - getRoute: (backTo?: string) => getUrlWithBackToParam('ai-features-promo', backTo, false), - }, + AI_FEATURES_PROMO_MODAL: 'ai-features-promo', TRANSACTION_RECEIPT: { route: 'r/:reportID/transaction/:transactionID/receipt/:action?/:iouType?', diff --git a/src/libs/Navigation/guards/AIFeaturesPromoGuard.ts b/src/libs/Navigation/guards/AIFeaturesPromoGuard.ts index 192b89324c0e..3a8df06ead97 100644 --- a/src/libs/Navigation/guards/AIFeaturesPromoGuard.ts +++ b/src/libs/Navigation/guards/AIFeaturesPromoGuard.ts @@ -67,7 +67,7 @@ function navigateToAIFeaturesPromoModalIfReady() { Log.info('[AIFeaturesPromoGuard] Proactively navigating to AI features promo modal'); hasRedirectedToAIFeaturesPromoModal = true; - Navigation.navigate(ROUTES.AI_FEATURES_PROMO_MODAL.getRoute()); + Navigation.navigate(ROUTES.AI_FEATURES_PROMO_MODAL); } /** diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index d6c4cbd3b396..3079af6ec200 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -65,7 +65,7 @@ const config: LinkingOptions['config'] = { [NAVIGATORS.AI_FEATURES_PROMO_MODAL_NAVIGATOR]: { screens: { [SCREENS.AI_FEATURES_PROMO_MODAL.ROOT]: { - path: ROUTES.AI_FEATURES_PROMO_MODAL.route, + path: ROUTES.AI_FEATURES_PROMO_MODAL, exact: true, }, }, From e9738ef72c6486c50149e027c821b4f8b5fb12ef Mon Sep 17 00:00:00 2001 From: gijoe0295 <153004152+gijoe0295@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:21:06 +0700 Subject: [PATCH 05/44] improve navigation guard to account for explanation modal --- .../Navigation/guards/AIFeaturesPromoGuard.ts | 20 ++++++++++++++----- src/libs/Navigation/guards/index.ts | 6 +++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/libs/Navigation/guards/AIFeaturesPromoGuard.ts b/src/libs/Navigation/guards/AIFeaturesPromoGuard.ts index 3a8df06ead97..041234a4053c 100644 --- a/src/libs/Navigation/guards/AIFeaturesPromoGuard.ts +++ b/src/libs/Navigation/guards/AIFeaturesPromoGuard.ts @@ -6,6 +6,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import isProductTrainingElementDismissed from '@libs/TooltipUtils'; +import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import NAVIGATORS from '@src/NAVIGATORS'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -21,6 +22,7 @@ let dismissedProductTraining: OnyxEntry; let isDismissedProductTrainingLoaded = false; let hasBeenAddedToNudgeMigration = false; +let isHybridAppOnboardingCompleted: boolean | undefined; let isTryNewDotLoaded = false; let onboarding: OnyxEntry; @@ -32,18 +34,20 @@ let hasRedirectedToAIFeaturesPromoModal = false; * Same-session protection. * * Per the issue requirements, the AI features promo modal must not appear in the same - * session as the migration welcome modal or the onboarding flow. These flags trip when - * we observe either of those modals being "active" (pending dismissal / not yet - * completed) at any point during this process lifetime, and suppress the AI promo for - * the rest of the session. + * session as the migration welcome modal, the onboarding flow, or the HybridApp + * explanation modal. These flags trip when we observe any of those modals being + * "active" (pending dismissal / not yet completed) at any point during this process + * lifetime, and suppress the AI promo for the rest of the session. */ let observedActiveMigrationModalThisSession = false; let observedActiveOnboardingThisSession = false; +let observedActiveExplanationModalThisSession = false; function resetSessionFlag() { hasRedirectedToAIFeaturesPromoModal = false; observedActiveMigrationModalThisSession = false; observedActiveOnboardingThisSession = false; + observedActiveExplanationModalThisSession = false; } /** @@ -60,7 +64,8 @@ function navigateToAIFeaturesPromoModalIfReady() { !isOnboardingLoaded || isProductTrainingElementDismissed(CONST.AI_FEATURES_PROMO_MODAL, dismissedProductTraining) || observedActiveMigrationModalThisSession || - observedActiveOnboardingThisSession + observedActiveOnboardingThisSession || + observedActiveExplanationModalThisSession ) { return; } @@ -101,10 +106,15 @@ Onyx.connectWithoutView({ callback: (value) => { const result = value ? tryNewDotOnyxSelector(value) : undefined; hasBeenAddedToNudgeMigration = result?.hasBeenAddedToNudgeMigration ?? false; + isHybridAppOnboardingCompleted = result?.isHybridAppOnboardingCompleted; isTryNewDotLoaded = true; if (hasBeenAddedToNudgeMigration && !isProductTrainingElementDismissed(CONST.MIGRATED_USER_WELCOME_MODAL, dismissedProductTraining)) { observedActiveMigrationModalThisSession = true; } + // The HybridApp explanation modal shows when the user is transitioning from OldDot to NewDot. + if (CONFIG.IS_HYBRID_APP && isHybridAppOnboardingCompleted === false) { + observedActiveExplanationModalThisSession = true; + } navigateToAIFeaturesPromoModalIfReady(); }, }); diff --git a/src/libs/Navigation/guards/index.ts b/src/libs/Navigation/guards/index.ts index e60ba997d016..783b8be533b3 100644 --- a/src/libs/Navigation/guards/index.ts +++ b/src/libs/Navigation/guards/index.ts @@ -5,7 +5,7 @@ import getCurrentUrl from '@libs/Navigation/currentUrl'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Session} from '@src/types/onyx'; import AIFeaturesPromoGuard, {onSessionOrLoadingAppChanged as onAIFeaturesPromoSessionOrLoadingAppChanged} from './AIFeaturesPromoGuard'; -import MigratedUserWelcomeModalGuard, {onSessionOrLoadingAppChanged} from './MigratedUserWelcomeModalGuard'; +import MigratedUserWelcomeModalGuard, {onSessionOrLoadingAppChanged as onMigratedUserWelcomeModalSessionOrLoadingAppChanged} from './MigratedUserWelcomeModalGuard'; import OnboardingGuard from './OnboardingGuard'; import type {GuardContext, GuardResult, NavigationGuard} from './types'; @@ -20,7 +20,7 @@ Onyx.connectWithoutView({ key: ONYXKEYS.SESSION, callback: (value) => { session = value; - onSessionOrLoadingAppChanged(session, isLoadingApp); + onMigratedUserWelcomeModalSessionOrLoadingAppChanged(session, isLoadingApp); onAIFeaturesPromoSessionOrLoadingAppChanged(session, isLoadingApp); }, }); @@ -29,7 +29,7 @@ Onyx.connectWithoutView({ key: ONYXKEYS.IS_LOADING_APP, callback: (value) => { isLoadingApp = value ?? true; - onSessionOrLoadingAppChanged(session, isLoadingApp); + onMigratedUserWelcomeModalSessionOrLoadingAppChanged(session, isLoadingApp); onAIFeaturesPromoSessionOrLoadingAppChanged(session, isLoadingApp); }, }); From d78c768b2a85f891ec954deed77a87427d7a584b Mon Sep 17 00:00:00 2001 From: gijoe0295 <153004152+gijoe0295@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:21:43 +0700 Subject: [PATCH 06/44] add lottie files --- assets/animations/CustomAgents.lottie | Bin 0 -> 38277 bytes assets/animations/ExpenseAssistant.lottie | Bin 0 -> 28766 bytes assets/animations/SpendAnalysis.lottie | Bin 0 -> 20112 bytes src/components/LottieAnimations/index.tsx | 16 ++++++++++++++++ 4 files changed, 16 insertions(+) create mode 100644 assets/animations/CustomAgents.lottie create mode 100644 assets/animations/ExpenseAssistant.lottie create mode 100644 assets/animations/SpendAnalysis.lottie diff --git a/assets/animations/CustomAgents.lottie b/assets/animations/CustomAgents.lottie new file mode 100644 index 0000000000000000000000000000000000000000..8210c757e7a027bfb36841b1b60c0024149afc06 GIT binary patch literal 38277 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 literal 0 HcmV?d00001 diff --git a/assets/animations/ExpenseAssistant.lottie b/assets/animations/ExpenseAssistant.lottie new file mode 100644 index 0000000000000000000000000000000000000000..99e64a64cede3e17f2b38cc4c4285971d86cc8c2 GIT binary patch literal 28766 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'@assets/animations/SpendAnalysis.lottie'), + w: 440, + h: 334, + backgroundColor: colors.pink700, + }, + ExpenseAssistant: { + file: require('@assets/animations/ExpenseAssistant.lottie'), + w: 440, + h: 334, + }, + CustomAgents: { + file: require('@assets/animations/CustomAgents.lottie'), + w: 440, + h: 334, + }, } satisfies Record; export default DotLottieAnimations; From 07bdc5abdf91b5d2da4f08b5ae911f0875cc6413 Mon Sep 17 00:00:00 2001 From: gijoe0295 <153004152+gijoe0295@users.noreply.github.com> Date: Mon, 8 Jun 2026 13:23:25 +0700 Subject: [PATCH 07/44] implement AI features promo modal UI --- src/components/AIFeaturesPromoModal/index.tsx | 61 ++- .../FeatureTrainingModalBody.tsx | 282 ++++++++++++ .../index.tsx} | 403 ++++++++---------- src/styles/index.ts | 6 + src/styles/variables.ts | 1 + 5 files changed, 528 insertions(+), 225 deletions(-) create mode 100644 src/components/FeatureTrainingModal/FeatureTrainingModalBody.tsx rename src/components/{FeatureTrainingModal.tsx => FeatureTrainingModal/index.tsx} (56%) diff --git a/src/components/AIFeaturesPromoModal/index.tsx b/src/components/AIFeaturesPromoModal/index.tsx index 1dda5ecb0ca6..08bbdc3553c2 100644 --- a/src/components/AIFeaturesPromoModal/index.tsx +++ b/src/components/AIFeaturesPromoModal/index.tsx @@ -1,32 +1,75 @@ -import React from 'react'; +import React, {useMemo} from 'react'; import FeatureTrainingModal from '@components/FeatureTrainingModal'; +import type {FeatureTrainingModalPageProps} from '@components/FeatureTrainingModal'; import LottieAnimations from '@components/LottieAnimations'; import useLocalize from '@hooks/useLocalize'; +import usePermissions from '@hooks/usePermissions'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useThemeStyles from '@hooks/useThemeStyles'; import {dismissProductTraining} from '@libs/actions/Welcome'; import Log from '@libs/Log'; +import variables from '@styles/variables'; import CONST from '@src/CONST'; function AIFeaturesPromoModal() { const {translate} = useLocalize(); + const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const {isBetaEnabled} = usePermissions(); + const canUseCustomAgent = isBetaEnabled(CONST.BETAS.CUSTOM_AGENT); - const onClose = () => { - Log.hmmm('[AIFeaturesPromoModal] onClose called, dismissing product training'); - dismissProductTraining(CONST.AI_FEATURES_PROMO_MODAL, true); - }; + const pages = useMemo( + () => [ + { + animation: LottieAnimations.SpendAnalysis, + title: translate('aiFeaturesPromoModal.spendAnalysis.title'), + subtitle: translate('aiFeaturesPromoModal.subtitle'), + description: translate('aiFeaturesPromoModal.spendAnalysis.description'), + confirmText: translate('common.next'), + }, + { + animation: LottieAnimations.ExpenseAssistant, + title: translate('aiFeaturesPromoModal.expenseAssistant.title'), + subtitle: translate('aiFeaturesPromoModal.subtitle'), + description: translate('aiFeaturesPromoModal.expenseAssistant.description'), + confirmText: canUseCustomAgent ? translate('common.next') : translate('aiFeaturesPromoModal.confirmText'), + }, + ...(canUseCustomAgent + ? [ + { + animation: LottieAnimations.CustomAgents, + title: translate('aiFeaturesPromoModal.customAgents.title'), + subtitle: translate('aiFeaturesPromoModal.subtitle'), + description: translate('aiFeaturesPromoModal.customAgents.description'), + confirmText: translate('aiFeaturesPromoModal.confirmText'), + }, + ] + : []), + ], + [translate], + ); const onConfirm = () => { Log.hmmm('[AIFeaturesPromoModal] onConfirm called, dismissing product training'); dismissProductTraining(CONST.AI_FEATURES_PROMO_MODAL); }; + const onClose = () => { + Log.hmmm('[AIFeaturesPromoModal] onClose called, dismissing product training'); + dismissProductTraining(CONST.AI_FEATURES_PROMO_MODAL, true); + }; + return ( ); } diff --git a/src/components/FeatureTrainingModal/FeatureTrainingModalBody.tsx b/src/components/FeatureTrainingModal/FeatureTrainingModalBody.tsx new file mode 100644 index 000000000000..1055dcb2263e --- /dev/null +++ b/src/components/FeatureTrainingModal/FeatureTrainingModalBody.tsx @@ -0,0 +1,282 @@ +import type {SourceLoadEventPayload} from 'expo-video'; +import React, {useEffect, useState} from 'react'; +import {Image, View} from 'react-native'; +// eslint-disable-next-line no-restricted-imports +import type {ImageResizeMode, ImageSourcePropType} from 'react-native'; +import {GestureHandlerRootView} from 'react-native-gesture-handler'; +import Button from '@components/Button'; +import CheckboxWithLabel from '@components/CheckboxWithLabel'; +import type {BaseFeatureTrainingModalProps, FeatureTrainingModalPageProps} from '@components/FeatureTrainingModal'; +import FormAlertWithSubmitButton from '@components/FormAlertWithSubmitButton'; +import ImageSVG from '@components/ImageSVG'; +import Lottie from '@components/Lottie'; +import LottieAnimations from '@components/LottieAnimations'; +import OfflineIndicator from '@components/OfflineIndicator'; +import RenderHTML from '@components/RenderHTML'; +import Text from '@components/Text'; +import VideoPlayer from '@components/VideoPlayer'; +import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; +import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import Accessibility from '@libs/Accessibility'; +import isInLandscapeModeUtil from '@libs/isInLandscapeMode'; +import CONST from '@src/CONST'; + +// Aspect ratio and height of the video. +// Useful before video loads to reserve space. +const VIDEO_ASPECT_RATIO = 1280 / 960; + +type VideoStatus = 'video' | 'animation'; + +const LANDSCAPE_ILLUSTRATION_MAX_HEIGHT_TO_WINDOW_HEIGHT_RATIO = 0.7; + +type FeatureTrainingModalBodyProps = BaseFeatureTrainingModalProps & + FeatureTrainingModalPageProps & { + /** Padding for the modal */ + modalPadding: number; + + /** Whether the modal should be shown again */ + willShowAgain: boolean; + + /** A callback to call when the modal should be shown again */ + toggleWillShowAgain: () => void; + + /** A callback to call when we want to close the modal */ + closeModal: (didPressHelpButton?: boolean) => void; + + /** A callback to call when we want to close the modal and confirm */ + confirmModal: () => void; + + /** Whether to show the back button to navigate back to the previous page in carousel mode */ + shouldShowBackButton?: boolean; + + /** A callback to call when we want to navigate back to the previous page in carousel mode */ + onBack?: () => void; + }; + +function FeatureTrainingModalBody({ + animation, + animationStyle, + illustrationInnerContainerStyle, + illustrationOuterContainerStyle, + videoURL, + illustrationAspectRatio: illustrationAspectRatioProp, + image, + contentFitImage, + width, + title = '', + subtitle = '', + description = '', + secondaryDescription = '', + titleStyles, + shouldShowDismissModalOption = false, + confirmText = '', + helpText = '', + onHelp = () => {}, + children, + contentInnerContainerStyles, + contentOuterContainerStyles, + imageWidth, + imageHeight, + shouldRenderSVG = true, + shouldRenderHTMLDescription = false, + shouldShowConfirmationLoader = false, + canConfirmWhileOffline = true, + shouldCallOnHelpWhenModalHidden = false, + helpSentryLabel, + confirmSentryLabel, + modalPadding, + willShowAgain = true, + toggleWillShowAgain, + closeModal, + confirmModal, + shouldShowBackButton = false, + onBack, +}: FeatureTrainingModalBodyProps) { + const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const {translate} = useLocalize(); + const isReduceMotionEnabled = Accessibility.useReducedMotion(); + const illustrations = useMemoizedLazyIllustrations(['Hands']); + const {onboardingIsMediumOrLargerScreenWidth} = useResponsiveLayout(); + const {windowHeight, windowWidth} = useWindowDimensions(); + const [videoStatus, setVideoStatus] = useState('video'); + const [isVideoStatusLocked, setIsVideoStatusLocked] = useState(false); + const [illustrationAspectRatio, setIllustrationAspectRatio] = useState(illustrationAspectRatioProp ?? VIDEO_ASPECT_RATIO); + const {shouldUseNarrowLayout} = useResponsiveLayout(); + const {isOffline} = useNetwork(); + const isInLandscapeMode = isInLandscapeModeUtil(windowWidth, windowHeight); + + useEffect(() => { + if (isVideoStatusLocked) { + return; + } + + if (isOffline) { + setVideoStatus('animation'); + } else if (!isOffline) { + setVideoStatus('video'); + setIsVideoStatusLocked(true); + } + }, [isOffline, isVideoStatusLocked]); + + const setAspectRatio = (event: SourceLoadEventPayload) => { + const track = event.availableVideoTracks.at(0); + + if (!track) { + return; + } + + setIllustrationAspectRatio(track.size.width / track.size.height); + }; + + const renderIllustration = () => { + const aspectRatio = illustrationAspectRatio || VIDEO_ASPECT_RATIO; + + return ( + + {!!image && + (shouldRenderSVG ? ( + + ) : ( + + ))} + {!!videoURL && videoStatus === 'video' && ( + + + + )} + {((!videoURL && !image) || (!!videoURL && videoStatus === 'animation')) && ( + + {isReduceMotionEnabled && (animation ?? LottieAnimations.Hands) === LottieAnimations.Hands ? ( + + ) : ( + + )} + + )} + + ); + }; + + return ( + + + {renderIllustration()} + + + {!!title && !!description && ( + + {subtitle && {subtitle}} + {typeof title === 'string' ? {title} : title} + {shouldRenderHTMLDescription ? ( + + + + ) : ( + {description} + )} + {secondaryDescription.length > 0 && {secondaryDescription}} + {children} + + )} + {shouldShowDismissModalOption && ( + + )} + {!!helpText && ( +