diff --git a/Mobile-Expensify b/Mobile-Expensify
index d62323fd8d53..d04040c9f51f 160000
--- a/Mobile-Expensify
+++ b/Mobile-Expensify
@@ -1 +1 @@
-Subproject commit d62323fd8d5308fa9b49d510f4b4d8383efcc978
+Subproject commit d04040c9f51f31226fb0d48bc1513dd7b15ea526
diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts
index 74e4f1d94169..ff79ebaa9adf 100755
--- a/src/ONYXKEYS.ts
+++ b/src/ONYXKEYS.ts
@@ -568,9 +568,6 @@ const ONYXKEYS = {
/** Stores the information if mobile selection mode is active */
RAM_ONLY_MOBILE_SELECTION_MODE: 'mobileSelectionMode',
- /** Session-scoped flag: user dismissed the "enable notifications" banner in the Concierge chat */
- RAM_ONLY_HAS_DISMISSED_CONCIERGE_NOTIFICATION_BANNER: 'hasDismissedConciergeNotificationBanner',
-
NVP_PRIVATE_CANCELLATION_DETAILS: 'nvp_private_cancellationDetails',
/** Stores the information about duplicated workspace */
@@ -1587,7 +1584,6 @@ type OnyxValuesMapping = {
[ONYXKEYS.ADD_NEW_PERSONAL_CARD]: OnyxTypes.AddNewPersonalCard;
[ONYXKEYS.ASSIGN_CARD]: OnyxTypes.AssignCard;
[ONYXKEYS.RAM_ONLY_MOBILE_SELECTION_MODE]: boolean;
- [ONYXKEYS.RAM_ONLY_HAS_DISMISSED_CONCIERGE_NOTIFICATION_BANNER]: boolean;
[ONYXKEYS.DUPLICATE_WORKSPACE]: OnyxTypes.DuplicateWorkspace;
[ONYXKEYS.COPY_POLICY_SETTINGS]: OnyxTypes.CopyPolicySettings;
[ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL]: string;
diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx
index ad2ca1595e65..f652653f910f 100644
--- a/src/components/Banner.tsx
+++ b/src/components/Banner.tsx
@@ -53,9 +53,6 @@ type BannerProps = {
/** Callback called when pressing the button */
onButtonPress?: () => void;
-
- /** Custom action content rendered in the right side of the banner. Overrides the configured `shouldShowButton` when provided. */
- children?: React.ReactNode;
};
function Banner({
@@ -67,7 +64,6 @@ function Banner({
onButtonPress,
containerStyles,
textStyles,
- children,
shouldRenderHTML = false,
shouldShowIcon = false,
shouldShowCloseButton = false,
@@ -122,15 +118,14 @@ function Banner({
))}
- {children ??
- (shouldShowButton && (
-
- ))}
+ {shouldShowButton && (
+
+ )}
{shouldShowCloseButton && !!onClose && (
= {
concierge: {
collapseReasoning: 'Begründung einklappen',
expandReasoning: 'Begründung erweitern',
- enableNotifications: {prompt: 'Möchten Sie benachrichtigt werden, wenn Concierge antwortet?', cta: 'Benachrichtigen'},
},
supportalNoAccess: {
title: 'Nicht so schnell',
diff --git a/src/languages/en.ts b/src/languages/en.ts
index 0f96b0e1b1f2..47a1ad80e0bb 100644
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -528,10 +528,6 @@ const translations = {
concierge: {
collapseReasoning: 'Collapse reasoning',
expandReasoning: 'Expand reasoning',
- enableNotifications: {
- prompt: 'Want to be notified when Concierge responds?',
- cta: 'Notify',
- },
},
supportalNoAccess: {
title: 'Not so fast',
diff --git a/src/languages/es.ts b/src/languages/es.ts
index a8717417154a..aab502920cea 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -460,10 +460,6 @@ const translations: TranslationDeepObject = {
concierge: {
collapseReasoning: 'Contraer razonamiento',
expandReasoning: 'Expandir razonamiento',
- enableNotifications: {
- prompt: '¿Quieres que te avisemos cuando Concierge responda?',
- cta: 'Notificar',
- },
},
supportalNoAccess: {
title: 'No tan rápido',
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index 69a77bc5c1c3..db93202c4e1b 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -508,7 +508,6 @@ const translations: TranslationDeepObject = {
concierge: {
collapseReasoning: 'Réduire le raisonnement',
expandReasoning: 'Développer le raisonnement',
- enableNotifications: {prompt: 'Vous souhaitez être averti lorsque Concierge répond ?', cta: 'Notifier'},
},
supportalNoAccess: {
title: 'Pas si vite',
diff --git a/src/languages/it.ts b/src/languages/it.ts
index 15960393907b..a3c1cf801e1a 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -508,7 +508,6 @@ const translations: TranslationDeepObject = {
concierge: {
collapseReasoning: 'Comprimi ragionamento',
expandReasoning: 'Espandi ragionamento',
- enableNotifications: {prompt: 'Vuoi ricevere una notifica quando Concierge risponde?', cta: 'Notifica'},
},
supportalNoAccess: {
title: 'Non così in fretta',
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index f58b76337ec1..266697019ad6 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -507,10 +507,6 @@ const translations: TranslationDeepObject = {
concierge: {
collapseReasoning: '推論を折りたたむ',
expandReasoning: '推論を展開',
- enableNotifications: {
- prompt: 'Conciergeから返信があったときに通知を受け取りますか?',
- cta: '通知',
- },
},
supportalNoAccess: {
title: 'ちょっと待ってください',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index 2aeba3345eb0..1ff2288a907e 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -507,7 +507,6 @@ const translations: TranslationDeepObject = {
concierge: {
collapseReasoning: 'Redenering inklappen',
expandReasoning: 'Redenering uitklappen',
- enableNotifications: {prompt: 'Wil je een melding krijgen wanneer Concierge reageert?', cta: 'Melden'},
},
supportalNoAccess: {
title: 'Niet zo snel',
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index be09ee06a69d..5789e6bfce28 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -507,7 +507,6 @@ const translations: TranslationDeepObject = {
concierge: {
collapseReasoning: 'Zwiń rozumowanie',
expandReasoning: 'Rozwiń rozumowanie',
- enableNotifications: {prompt: 'Chcesz otrzymywać powiadomienia, gdy Concierge odpowie?', cta: 'Powiadom'},
},
supportalNoAccess: {
title: 'Nie tak szybko',
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index e72ee72ceb94..e04a7d14df0c 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -506,7 +506,6 @@ const translations: TranslationDeepObject = {
concierge: {
collapseReasoning: 'Recolher raciocínio',
expandReasoning: 'Expandir raciocínio',
- enableNotifications: {prompt: 'Quer ser avisado quando o Concierge responder?', cta: 'Notificar'},
},
supportalNoAccess: {
title: 'Calma aí',
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 8c721598f08f..a4f7fd7e73a1 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -503,10 +503,6 @@ const translations: TranslationDeepObject = {
concierge: {
collapseReasoning: '收起推理',
expandReasoning: '展开推理',
- enableNotifications: {
- prompt: '希望在Concierge回复时收到通知吗?',
- cta: '通知',
- },
},
supportalNoAccess: {
title: '先别急',
diff --git a/src/libs/Notification/LocalNotification/BrowserNotifications.ts b/src/libs/Notification/LocalNotification/BrowserNotifications.ts
index c07ae3785754..5379bdd6acc5 100644
--- a/src/libs/Notification/LocalNotification/BrowserNotifications.ts
+++ b/src/libs/Notification/LocalNotification/BrowserNotifications.ts
@@ -5,7 +5,6 @@ import EXPENSIFY_ICON_URL from '@assets/images/expensify-logo-round-clearspace.p
import * as AppUpdate from '@libs/actions/AppUpdate';
import {translateLocal} from '@libs/Localize';
import {getForReportAction} from '@libs/ModifiedExpenseMessage';
-import NotificationPermission from '@libs/Notification/notificationPermission';
import {getTextFromHtml} from '@libs/ReportActionsUtils';
import {getReportName} from '@libs/ReportNameUtils';
import * as ReportUtils from '@libs/ReportUtils';
@@ -17,18 +16,28 @@ import type {LocalNotificationClickHandler, LocalNotificationData, LocalNotifica
const notificationCache: Record = {};
/**
- * Checks if the user has granted permission to show browser notifications, prompting them
- * if they have not yet decided.
+ * Checks if the user has granted permission to show browser notifications
*/
function canUseBrowserNotifications(): Promise {
- return NotificationPermission.getStatus().then((status) => {
- if (status === 'granted') {
- return true;
+ return new Promise((resolve) => {
+ // They have no browser notifications so we can't use this feature
+ if (!window.Notification) {
+ resolve(false);
+ return;
}
- if (status === 'denied') {
- return false;
+
+ // Check if they previously granted or denied us access to send a notification
+ const permissionGranted = Notification.permission === 'granted';
+
+ if (permissionGranted || Notification.permission === 'denied') {
+ resolve(permissionGranted);
+ return;
}
- return NotificationPermission.request().then((requested) => requested === 'granted');
+
+ // Check their global preferences for browser notifications and ask permission if they have none
+ Notification.requestPermission().then((status) => {
+ resolve(status === 'granted');
+ });
});
}
diff --git a/src/libs/Notification/notificationPermission/index.native.ts b/src/libs/Notification/notificationPermission/index.native.ts
deleted file mode 100644
index 29123ad37089..000000000000
--- a/src/libs/Notification/notificationPermission/index.native.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import type NotificationPermissionModule from './types';
-
-const NotificationPermissionNative: NotificationPermissionModule = {
- getStatus: () => Promise.resolve('denied'),
- request: () => Promise.resolve('denied'),
-};
-
-export default NotificationPermissionNative;
diff --git a/src/libs/Notification/notificationPermission/index.ts b/src/libs/Notification/notificationPermission/index.ts
deleted file mode 100644
index d9a5fb7a4367..000000000000
--- a/src/libs/Notification/notificationPermission/index.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import Log from '@libs/Log';
-import type NotificationPermissionModule from './types';
-import type {NotificationPermissionStatus} from './types';
-
-function toStatus(permission: NotificationPermission): NotificationPermissionStatus {
- if (permission === 'granted' || permission === 'denied') {
- return permission;
- }
- return 'default';
-}
-
-const NotificationPermission: NotificationPermissionModule = {
- getStatus(): Promise {
- if (typeof window === 'undefined' || !window.Notification) {
- return Promise.resolve('denied');
- }
- return Promise.resolve(toStatus(Notification.permission));
- },
-
- request(): Promise {
- if (typeof window === 'undefined' || !window.Notification) {
- return Promise.resolve('denied');
- }
- return Notification.requestPermission()
- .then(toStatus)
- .catch((error: unknown) => {
- Log.warn('[NotificationPermission] request failed', {error: String(error)});
- return 'denied';
- });
- },
-};
-
-export default NotificationPermission;
diff --git a/src/libs/Notification/notificationPermission/types.ts b/src/libs/Notification/notificationPermission/types.ts
deleted file mode 100644
index 9ffca38417ae..000000000000
--- a/src/libs/Notification/notificationPermission/types.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-type NotificationPermissionStatus = 'granted' | 'denied' | 'default';
-
-type NotificationPermissionModule = {
- getStatus: () => Promise;
- request: () => Promise;
-};
-
-export default NotificationPermissionModule;
-export type {NotificationPermissionStatus};
diff --git a/src/libs/actions/ConciergeNotificationBanner.ts b/src/libs/actions/ConciergeNotificationBanner.ts
deleted file mode 100644
index cd12ebe86389..000000000000
--- a/src/libs/actions/ConciergeNotificationBanner.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import Onyx from 'react-native-onyx';
-import ONYXKEYS from '@src/ONYXKEYS';
-
-function dismissForSession() {
- Onyx.set(ONYXKEYS.RAM_ONLY_HAS_DISMISSED_CONCIERGE_NOTIFICATION_BANNER, true);
-}
-
-export {
- // eslint-disable-next-line import/prefer-default-export -- Action files in src/libs/actions/ use named exports by convention so callers stay consistent as more actions are added
- dismissForSession,
-};
diff --git a/src/pages/inbox/ReportScreen.tsx b/src/pages/inbox/ReportScreen.tsx
index 0fe5cab9d7fb..684f69b66284 100644
--- a/src/pages/inbox/ReportScreen.tsx
+++ b/src/pages/inbox/ReportScreen.tsx
@@ -28,7 +28,7 @@ import useDeferNonEssentials from './hooks/useDeferNonEssentials';
import useFlushDeferredWriteOnFocus from './hooks/useFlushDeferredWriteOnFocus';
import LinkedActionNotFoundGuard from './LinkedActionNotFoundGuard';
import ReactionListWrapper from './ReactionListWrapper';
-import ReportActionCompose from './report/ReportActionCompose/ReportActionCompose';
+import ReportActionComposePlaceholder from './report/ReportActionCompose/ReportActionComposePlaceholder';
import {ReportActionEditMessageContextProvider, ReportScreenEditMessageProviderWithTransactionThread} from './report/ReportActionEditMessageContext';
import ReportFooter from './report/ReportFooter';
import useClearReportActionDraftsOnReportChange from './report/useClearReportActionDraftsOnReportChange';
@@ -148,7 +148,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
testID="report-actions-view-wrapper"
>
- {shouldDeferNonEssentials ? : }
+ {shouldDeferNonEssentials ? : }
diff --git a/src/pages/inbox/report/EnableNotificationsBanner.tsx b/src/pages/inbox/report/EnableNotificationsBanner.tsx
deleted file mode 100644
index 6d41a965f0a5..000000000000
--- a/src/pages/inbox/report/EnableNotificationsBanner.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import React from 'react';
-import {View} from 'react-native';
-import Banner from '@components/Banner';
-import Button from '@components/Button';
-import useLocalize from '@hooks/useLocalize';
-import useThemeStyles from '@hooks/useThemeStyles';
-import {dismissForSession} from '@libs/actions/ConciergeNotificationBanner';
-import NotificationPermission from '@libs/Notification/notificationPermission';
-import variables from '@styles/variables';
-import CONST from '@src/CONST';
-
-// Visual breathing room hidden behind the composer in addition to the corner-radius arc.
-const BANNER_EXTRA_OVERLAP_PX = 8;
-// Composer overlaps this banner; the overlap must be ≥ the composer's corner radius.
-const BANNER_COMPOSER_OVERLAP_PX = variables.componentBorderRadiusRounded + BANNER_EXTRA_OVERLAP_PX;
-const BANNER_VISIBLE_BOTTOM_GAP_PX = 12;
-
-const containerOverrideStyle = {
- paddingBottom: BANNER_VISIBLE_BOTTOM_GAP_PX + BANNER_COMPOSER_OVERLAP_PX,
- borderRadius: 0,
- borderTopLeftRadius: variables.componentBorderRadiusLarge,
- borderTopRightRadius: variables.componentBorderRadiusLarge,
-};
-
-function requestAndDismiss() {
- NotificationPermission.request().then(dismissForSession);
-}
-
-function EnableNotificationsBanner() {
- const styles = useThemeStyles();
- const {translate} = useLocalize();
-
- return (
-
-
-
-
-
-
- );
-}
-
-EnableNotificationsBanner.displayName = 'EnableNotificationsBanner';
-
-export default EnableNotificationsBanner;
-export {BANNER_COMPOSER_OVERLAP_PX};
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerTypingIndicator.tsx b/src/pages/inbox/report/ReportActionCompose/AgentZeroAwareTypingIndicator.tsx
similarity index 69%
rename from src/pages/inbox/report/ReportActionCompose/ComposerTypingIndicator.tsx
rename to src/pages/inbox/report/ReportActionCompose/AgentZeroAwareTypingIndicator.tsx
index 7ced0f7cc9c7..835cc1323b4f 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerTypingIndicator.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/AgentZeroAwareTypingIndicator.tsx
@@ -1,10 +1,8 @@
import React from 'react';
import useShouldSuppressConciergeIndicators from '@hooks/useShouldSuppressConciergeIndicators';
import ReportTypingIndicator from '@pages/inbox/report/ReportTypingIndicator';
-import {useComposerState} from './ComposerContext';
-function ComposerTypingIndicator() {
- const {reportID} = useComposerState();
+function AgentZeroAwareTypingIndicator({reportID}: {reportID: string}) {
const shouldSuppress = useShouldSuppressConciergeIndicators(reportID);
if (shouldSuppress) {
return null;
@@ -12,4 +10,4 @@ function ComposerTypingIndicator() {
return ;
}
-export default ComposerTypingIndicator;
+export default AgentZeroAwareTypingIndicator;
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerActionButton.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerActionButton.tsx
deleted file mode 100644
index e9147bd9afd7..000000000000
--- a/src/pages/inbox/report/ReportActionCompose/ComposerActionButton.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react';
-import ComposerActionMenu from './ComposerActionMenu';
-import {useComposerEditState} from './ComposerContext';
-import ComposerEditingButtons from './ComposerEditingButtons';
-
-function ComposerActionButton() {
- const {isEditingInComposer} = useComposerEditState();
-
- if (isEditingInComposer) {
- return ;
- }
- return ;
-}
-
-export default ComposerActionButton;
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerActionMenu.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerActionMenu.tsx
index b7e8f4e14569..c84eda56f95f 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerActionMenu.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ComposerActionMenu.tsx
@@ -11,8 +11,11 @@ import AttachmentPickerWithMenuItems from './AttachmentPickerWithMenuItems';
import {useComposerActions, useComposerEditState, useComposerMeta, useComposerSendState, useComposerState} from './ComposerContext';
import useAttachmentPicker from './useAttachmentPicker';
-function ComposerActionMenu() {
- const {reportID} = useComposerState();
+type ComposerActionMenuProps = {
+ reportID: string;
+};
+
+function ComposerActionMenu({reportID}: ComposerActionMenuProps) {
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const {isMenuVisible, isFullComposerAvailable} = useComposerState();
const {draftComment} = useComposerEditState();
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerBox.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerBox.tsx
index 0c1414788420..568c90b2a619 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerBox.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ComposerBox.tsx
@@ -1,13 +1,16 @@
import React from 'react';
-import type {PropsWithChildren} from 'react';
import {View} from 'react-native';
import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';
import ONYXKEYS from '@src/ONYXKEYS';
import {useComposerMeta, useComposerSendState, useComposerState} from './ComposerContext';
-function ComposerBox({children}: PropsWithChildren) {
- const {reportID} = useComposerState();
+type ComposerBoxProps = {
+ reportID: string;
+ children: React.ReactNode;
+};
+
+function ComposerBox({reportID, children}: ComposerBoxProps) {
const styles = useThemeStyles();
const {isFocused} = useComposerState();
const {isExceedingMaxLength, isBlockedFromConcierge} = useComposerSendState();
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerContainer.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerContainer.tsx
deleted file mode 100644
index 7a11d44e5f90..000000000000
--- a/src/pages/inbox/report/ReportActionCompose/ComposerContainer.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react';
-import type {PropsWithChildren} from 'react';
-import OfflineWithFeedback from '@components/OfflineWithFeedback';
-import useOnyx from '@hooks/useOnyx';
-import useThemeStyles from '@hooks/useThemeStyles';
-import {getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils';
-import ONYXKEYS from '@src/ONYXKEYS';
-import {useComposerState} from './ComposerContext';
-
-function ComposerContainer({children}: PropsWithChildren) {
- const {reportID} = useComposerState();
- const styles = useThemeStyles();
- const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
- const [isComposerFullSize = false] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`);
- const {reportPendingAction: pendingAction} = getReportOfflinePendingActionAndErrors(report);
-
- return (
-
- {children}
-
- );
-}
-
-export default ComposerContainer;
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerContext.ts b/src/pages/inbox/report/ReportActionCompose/ComposerContext.ts
index 64c32912a0a3..ec9b33de1433 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerContext.ts
+++ b/src/pages/inbox/report/ReportActionCompose/ComposerContext.ts
@@ -26,7 +26,6 @@ type ComposerText = string;
// Warm — changes on interaction
type ComposerState = {
- reportID: string;
isFocused: boolean;
isMenuVisible: boolean;
isFullComposerAvailable: boolean;
@@ -91,7 +90,6 @@ const noop = () => {};
const ComposerTextContext = createContext('');
const defaultState: ComposerState = {
- reportID: '',
isFocused: false,
isMenuVisible: false,
isFullComposerAvailable: false,
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerDefaultFooter.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerDefaultFooter.tsx
deleted file mode 100644
index 0c83e2b890ef..000000000000
--- a/src/pages/inbox/report/ReportActionCompose/ComposerDefaultFooter.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react';
-import OfflineIndicator from '@components/OfflineIndicator';
-import useResponsiveLayout from '@hooks/useResponsiveLayout';
-import useThemeStyles from '@hooks/useThemeStyles';
-import ComposerExceededLength from './ComposerExceededLength';
-import ComposerFooter from './ComposerFooter';
-import ComposerTypingIndicator from './ComposerTypingIndicator';
-
-function ComposerDefaultFooter() {
- const styles = useThemeStyles();
- const {shouldUseNarrowLayout} = useResponsiveLayout();
-
- return (
-
- {!shouldUseNarrowLayout && }
-
-
-
- );
-}
-
-export default ComposerDefaultFooter;
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerDropZone.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerDropZone.tsx
index 0db4107e2e22..344a2316925c 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerDropZone.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ComposerDropZone.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-import type {PropsWithChildren} from 'react';
import DragAndDropConsumer from '@components/DragAndDrop/Consumer';
import DropZoneUI from '@components/DropZone/DropZoneUI';
import DualDropZone from '@components/DropZone/DualDropZone';
@@ -15,11 +14,15 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
import {getParentReport, isChatRoom, isGroupChat, isInvoiceReport, isReportApproved, isSettled, temporary_getMoneyRequestOptions} from '@libs/ReportUtils';
import {hasReceipt as hasReceiptTransactionUtils} from '@libs/TransactionUtils';
import ONYXKEYS from '@src/ONYXKEYS';
-import {useComposerState} from './ComposerContext';
import useAttachmentPicker from './useAttachmentPicker';
import useReceiptDrop from './useReceiptDrop';
import useShouldAddOrReplaceReceipt from './useShouldAddOrReplaceReceipt';
+type ComposerDropZoneProps = {
+ reportID: string;
+ children: React.ReactNode;
+};
+
type RichDropZoneProps = {
reportID: string;
shouldAddOrReplaceReceipt: boolean;
@@ -107,8 +110,7 @@ function RichDropZone({reportID, shouldAddOrReplaceReceipt, transactionID, onAtt
);
}
-function ComposerDropZone({children}: PropsWithChildren) {
- const {reportID} = useComposerState();
+function ComposerDropZone({reportID, children}: ComposerDropZoneProps) {
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
const {shouldAddOrReplaceReceipt, transactionID} = useShouldAddOrReplaceReceipt(reportID);
const {pickAttachments, PDFValidationComponent: AttachmentPDFValidation, ErrorModal: AttachmentErrorModal} = useAttachmentPicker(reportID);
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerEditingButtons.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerEditingButtons.tsx
index 4bf7e056ad13..77f8f58dfae0 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerEditingButtons.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ComposerEditingButtons.tsx
@@ -2,12 +2,16 @@ import React from 'react';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
-import {useComposerEditActions, useComposerState} from './ComposerContext';
+import {useComposerEditActions} from './ComposerContext';
import ComposerExpandCollapseButton from './ComposerExpandCollapseButton';
import MessageEditCancelButton from './MessageEditCancelButton';
-function ComposerEditingButtons() {
- const {reportID} = useComposerState();
+type ComposerEditingButtonsProps = {
+ /** The report ID */
+ reportID: string;
+};
+
+function ComposerEditingButtons({reportID}: ComposerEditingButtonsProps) {
const styles = useThemeStyles();
const {deleteDraft} = useComposerEditActions();
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerEmojiPicker.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerEmojiPicker.tsx
index 43f4ff1c8c47..99c26549cd59 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerEmojiPicker.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ComposerEmojiPicker.tsx
@@ -6,10 +6,13 @@ import {canUseTouchScreen} from '@libs/DeviceCapabilities';
import DomUtils from '@libs/DomUtils';
import {hideEmojiPicker, isActive as isActiveEmojiPickerAction} from '@userActions/EmojiPickerAction';
import CONST from '@src/CONST';
-import {useComposerMeta, useComposerSendState, useComposerState} from './ComposerContext';
+import {useComposerMeta, useComposerSendState} from './ComposerContext';
-function ComposerEmojiPicker() {
- const {reportID} = useComposerState();
+type ComposerEmojiPickerProps = {
+ reportID: string;
+};
+
+function ComposerEmojiPicker({reportID}: ComposerEmojiPickerProps) {
const styles = useThemeStyles();
const {isMediumScreenWidth} = useResponsiveLayout();
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerFooter.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerFooter.tsx
index 4bcfe5289c4c..c34ca8e8e9de 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerFooter.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ComposerFooter.tsx
@@ -1,11 +1,14 @@
import React from 'react';
-import type {PropsWithChildren} from 'react';
import {View} from 'react-native';
import useNetwork from '@hooks/useNetwork';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
-function ComposerFooter({children}: PropsWithChildren) {
+type ComposerFooterProps = {
+ children: React.ReactNode;
+};
+
+function ComposerFooter({children}: ComposerFooterProps) {
const styles = useThemeStyles();
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
const {isSmallScreenWidth} = useResponsiveLayout();
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerInput.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerInput.tsx
index c8425c634336..1b01a369687f 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerInput.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ComposerInput.tsx
@@ -23,6 +23,10 @@ import ComposerWithSuggestions from './ComposerWithSuggestions';
import useAttachmentPicker from './useAttachmentPicker';
import useComposerSubmit from './useComposerSubmit';
+type ComposerInputProps = {
+ reportID: string;
+};
+
const AI_PLACEHOLDER_KEYS = ['reportActionCompose.askConciergeToUpdate', 'reportActionCompose.askConciergeToCorrect', 'reportActionCompose.askConciergeForHelp'] as const;
function getRandomPlaceholder(translate: LocalizedTranslate): string {
@@ -30,8 +34,7 @@ function getRandomPlaceholder(translate: LocalizedTranslate): string {
return translate(AI_PLACEHOLDER_KEYS[randomIndex]);
}
-function ComposerInput() {
- const {reportID} = useComposerState();
+function ComposerInput({reportID}: ComposerInputProps) {
const {translate, preferredLocale} = useLocalize();
const {isMenuVisible} = useComposerState();
const {isBlockedFromConcierge, debouncedCommentMaxLengthValidation} = useComposerSendState();
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerInputArea.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerInputArea.tsx
deleted file mode 100644
index 2b59b2f8db2a..000000000000
--- a/src/pages/inbox/report/ReportActionCompose/ComposerInputArea.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import React from 'react';
-import {View} from 'react-native';
-import useOnyx from '@hooks/useOnyx';
-import useThemeStyles from '@hooks/useThemeStyles';
-import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
-import ComposerActionButton from './ComposerActionButton';
-import ComposerBox from './ComposerBox';
-import ComposerContainer from './ComposerContainer';
-import {useComposerState} from './ComposerContext';
-import ComposerDropZone from './ComposerDropZone';
-import ComposerEmojiPicker from './ComposerEmojiPicker';
-import ComposerImportedState from './ComposerImportedState';
-import ComposerInput from './ComposerInput';
-import ComposerLocalTime from './ComposerLocalTime';
-import ComposerSendButton from './ComposerSendButton';
-
-function ComposerInputArea() {
- const {reportID} = useComposerState();
- const styles = useThemeStyles();
- const [isComposerFullSize = false] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`);
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-export default ComposerInputArea;
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerLocalTime.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerLocalTime.tsx
index 2eca6ac0d47c..98d14016f4ce 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerLocalTime.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ComposerLocalTime.tsx
@@ -9,10 +9,12 @@ import {isAgentEmail} from '@libs/SessionUtils';
import ParticipantLocalTime from '@pages/inbox/report/ParticipantLocalTime';
import ONYXKEYS from '@src/ONYXKEYS';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
-import {useComposerState} from './ComposerContext';
-function ComposerLocalTime() {
- const {reportID} = useComposerState();
+type ComposerLocalTimeProps = {
+ reportID: string;
+};
+
+function ComposerLocalTime({reportID}: ComposerLocalTimeProps) {
const [isComposerFullSize = false] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`);
const {accountID: currentUserAccountID} = useCurrentUserPersonalDetails();
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx
index 3435222bd241..7c3b41392182 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ComposerProvider.tsx
@@ -124,7 +124,6 @@ function ComposerProvider({children, reportID}: ComposerProviderProps) {
};
const composerState = {
- reportID,
isFocused,
isMenuVisible,
isFullComposerAvailable,
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerSendButton.tsx b/src/pages/inbox/report/ReportActionCompose/ComposerSendButton.tsx
index 361c4828cccb..79aab7007d88 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerSendButton.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ComposerSendButton.tsx
@@ -6,12 +6,11 @@ import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
-import {useComposerEditState, useComposerSendState, useComposerState} from './ComposerContext';
+import {useComposerEditState, useComposerSendState} from './ComposerContext';
import SubmitDraftButton from './SubmitDraftButton';
import useComposerSubmit from './useComposerSubmit';
-function ComposerSendButton() {
- const {reportID} = useComposerState();
+function ComposerSendButton({reportID}: {reportID: string}) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const icons = useMemoizedLazyExpensifyIcons(['Send', 'Checkmark']);
diff --git a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx
index 984b242f8c9d..b81935a33ada 100644
--- a/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ReportActionCompose.tsx
@@ -1,9 +1,18 @@
import React from 'react';
+import {View} from 'react-native';
+import OfflineIndicator from '@components/OfflineIndicator';
+import OfflineWithFeedback from '@components/OfflineWithFeedback';
+import useOnyx from '@hooks/useOnyx';
+import useResponsiveLayout from '@hooks/useResponsiveLayout';
+import useThemeStyles from '@hooks/useThemeStyles';
+import {getReportOfflinePendingActionAndErrors} from '@libs/ReportUtils';
+import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
+import AgentZeroAwareTypingIndicator from './AgentZeroAwareTypingIndicator';
import ComposerActionMenu from './ComposerActionMenu';
import ComposerBox from './ComposerBox';
-import ComposerContainer from './ComposerContainer';
+import {useComposerEditState} from './ComposerContext';
import type {SuggestionsRef} from './ComposerContext';
-import ComposerDefaultFooter from './ComposerDefaultFooter';
import ComposerDropZone from './ComposerDropZone';
import ComposerEditingButtons from './ComposerEditingButtons';
import ComposerEmojiPicker from './ComposerEmojiPicker';
@@ -11,53 +20,84 @@ import ComposerExceededLength from './ComposerExceededLength';
import ComposerFooter from './ComposerFooter';
import ComposerImportedState from './ComposerImportedState';
import ComposerInput from './ComposerInput';
-import ComposerInputArea from './ComposerInputArea';
import ComposerLocalTime from './ComposerLocalTime';
-import ComposerPlaceholder from './ComposerPlaceholder';
import ComposerProvider from './ComposerProvider';
import ComposerSendButton from './ComposerSendButton';
-import ComposerTypingIndicator from './ComposerTypingIndicator';
type ReportActionComposeProps = {
/** Report ID */
reportID: string;
+
+ /** Whether the composer is edit only */
+ isEditOnly?: boolean;
};
-function ReportActionCompose({reportID}: ReportActionComposeProps) {
+function ComposerInner({reportID, isEditOnly = false}: ReportActionComposeProps) {
+ const styles = useThemeStyles();
+ const {shouldUseNarrowLayout} = useResponsiveLayout();
+ const {isEditingInComposer} = useComposerEditState();
+ const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
+ const [isComposerFullSize = false] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`);
+ const {reportPendingAction: pendingAction} = getReportOfflinePendingActionAndErrors(report);
+
return (
-
-
-
-
+
+
+
+
+
+
+ {isEditingInComposer ? : }
+
+
+
+
+
+ {!isEditOnly && (
+
+ {!shouldUseNarrowLayout && }
+
+
+
+ )}
+
+
+
+
);
}
-function EditOnlyReportActionCompose({reportID}: ReportActionComposeProps) {
+function Composer({reportID, ...restProps}: ReportActionComposeProps) {
return (
-
+
);
}
-ReportActionCompose.LocalTime = ComposerLocalTime;
-ReportActionCompose.Container = ComposerContainer;
-ReportActionCompose.ImportedState = ComposerImportedState;
-ReportActionCompose.DropZone = ComposerDropZone;
-ReportActionCompose.Box = ComposerBox;
-ReportActionCompose.ActionMenu = ComposerActionMenu;
-ReportActionCompose.Input = ComposerInput;
-ReportActionCompose.EmojiPicker = ComposerEmojiPicker;
-ReportActionCompose.SendButton = ComposerSendButton;
-ReportActionCompose.EditingButtons = ComposerEditingButtons;
-ReportActionCompose.Footer = ComposerFooter;
-ReportActionCompose.TypingIndicator = ComposerTypingIndicator;
-ReportActionCompose.ExceededLength = ComposerExceededLength;
-ReportActionCompose.Layout = ComposerInputArea;
-ReportActionCompose.Placeholder = ComposerPlaceholder;
-ReportActionCompose.InputArea = ComposerInputArea;
-ReportActionCompose.DefaultFooter = ComposerDefaultFooter;
-ReportActionCompose.EditOnly = EditOnlyReportActionCompose;
+Composer.LocalTime = ComposerLocalTime;
+Composer.DropZone = ComposerDropZone;
+Composer.Box = ComposerBox;
+Composer.ActionMenu = ComposerActionMenu;
+Composer.Input = ComposerInput;
+Composer.EmojiPicker = ComposerEmojiPicker;
+Composer.SendButton = ComposerSendButton;
+Composer.EditingButtons = ComposerEditingButtons;
+Composer.Footer = ComposerFooter;
+Composer.TypingIndicator = AgentZeroAwareTypingIndicator;
+Composer.ExceededLength = ComposerExceededLength;
+Composer.ImportedState = ComposerImportedState;
-export default ReportActionCompose;
+export default Composer;
export type {SuggestionsRef, ReportActionComposeProps};
diff --git a/src/pages/inbox/report/ReportActionCompose/ComposerPlaceholder.tsx b/src/pages/inbox/report/ReportActionCompose/ReportActionComposePlaceholder.tsx
similarity index 97%
rename from src/pages/inbox/report/ReportActionCompose/ComposerPlaceholder.tsx
rename to src/pages/inbox/report/ReportActionCompose/ReportActionComposePlaceholder.tsx
index f3a997f5357a..7a90fad4a08c 100644
--- a/src/pages/inbox/report/ReportActionCompose/ComposerPlaceholder.tsx
+++ b/src/pages/inbox/report/ReportActionCompose/ReportActionComposePlaceholder.tsx
@@ -22,7 +22,7 @@ import CONST from '@src/CONST';
* chatItemComposeSecondaryRow, and icons [Plus, Emoji, Send].
* If the real composer changes its layout or icon set, update this placeholder to match.
*/
-function ComposerPlaceholder() {
+function ReportActionComposePlaceholder() {
const theme = useTheme();
const styles = useThemeStyles();
const {translate} = useLocalize();
@@ -86,4 +86,4 @@ function ComposerPlaceholder() {
);
}
-export default ComposerPlaceholder;
+export default ReportActionComposePlaceholder;
diff --git a/src/pages/inbox/report/ReportFooter.tsx b/src/pages/inbox/report/ReportFooter.tsx
index ab26c755e5f2..08897c6bb2d3 100644
--- a/src/pages/inbox/report/ReportFooter.tsx
+++ b/src/pages/inbox/report/ReportFooter.tsx
@@ -31,16 +31,12 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {isLoadingInitialReportActionsSelector} from '@src/selectors/ReportMetaData';
import type * as OnyxTypes from '@src/types/onyx';
-import EnableNotificationsBanner, {BANNER_COMPOSER_OVERLAP_PX} from './EnableNotificationsBanner';
import ReportActionCompose from './ReportActionCompose/ReportActionCompose';
import SystemChatReportFooterMessage from './SystemChatReportFooterMessage';
import useShouldShowComposerForActiveEditDraft from './useShouldShowComposerForActiveEditDraft';
-import useShouldShowEnableNotificationsBanner from './useShouldShowEnableNotificationsBanner';
const policyRoleSelector = (policy: OnyxEntry) => policy?.role;
-const composerOverlapStyle = {marginTop: -BANNER_COMPOSER_OVERLAP_PX};
-
/**
* Footer component that decides between the composer and
* archived/anonymous/blocked/system chat/admins-only footer.
@@ -75,7 +71,6 @@ function ReportFooter() {
const isUserPolicyAdmin = policyRole === CONST.POLICY.ROLE.ADMIN;
const isArchivedRoom = isArchivedNonExpenseReport(report, isReportArchived);
- const shouldShowEnableNotificationsBanner = useShouldShowEnableNotificationsBanner(reportIDFromRoute);
const shouldShowComposerOptimistically = !isAnonymousUser && isPublicRoom(report) && !!isLoadingInitialReportActions;
const canPerformWriteAction = canUserPerformWriteAction(report, isReportArchived) ?? shouldShowComposerOptimistically;
@@ -95,12 +90,7 @@ function ReportFooter() {
if (!shouldHideComposer) {
return (
- {shouldShowEnableNotificationsBanner && }
-
-
-
-
-
+
);
}
@@ -169,9 +159,10 @@ function ReportFooter() {
{isEditingWithComposer && (
-
-
-
+
)}
+
+
+ );
+}
+
export default ReportFooter;
diff --git a/src/pages/inbox/report/useShouldShowEnableNotificationsBanner.ts b/src/pages/inbox/report/useShouldShowEnableNotificationsBanner.ts
deleted file mode 100644
index 69ec25126fb8..000000000000
--- a/src/pages/inbox/report/useShouldShowEnableNotificationsBanner.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import {useEffect, useState} from 'react';
-import useOnyx from '@hooks/useOnyx';
-import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
-import NotificationPermission from '@libs/Notification/notificationPermission';
-import type {NotificationPermissionStatus} from '@libs/Notification/notificationPermission/types';
-import {isConciergeChatReport} from '@libs/ReportUtils';
-import ONYXKEYS from '@src/ONYXKEYS';
-
-function useShouldShowEnableNotificationsBanner(reportID: string | undefined): boolean {
- const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(reportID)}`);
- const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
- const [hasDismissed] = useOnyx(ONYXKEYS.RAM_ONLY_HAS_DISMISSED_CONCIERGE_NOTIFICATION_BANNER);
- const [permissionStatus, setPermissionStatus] = useState();
-
- const isConcierge = !!reportID && isConciergeChatReport(report, conciergeReportID);
-
- useEffect(() => {
- if (!isConcierge || hasDismissed) {
- return;
- }
- let isMounted = true;
- NotificationPermission.getStatus().then((status) => {
- if (!isMounted) {
- return;
- }
- setPermissionStatus(status);
- });
- return () => {
- isMounted = false;
- };
- }, [isConcierge, hasDismissed]);
-
- return isConcierge && !hasDismissed && permissionStatus === 'default';
-}
-
-export default useShouldShowEnableNotificationsBanner;
diff --git a/src/setup/index.ts b/src/setup/index.ts
index a912a25fbddd..455c516cc730 100644
--- a/src/setup/index.ts
+++ b/src/setup/index.ts
@@ -70,7 +70,6 @@ export default function () {
ONYXKEYS.RAM_ONLY_PLAID_LINK_TOKEN,
ONYXKEYS.RAM_ONLY_MERGE_HR_LINK_TOKEN,
ONYXKEYS.COLLECTION.RAM_ONLY_ISSUE_NEW_EXPENSIFY_CARD,
- ONYXKEYS.RAM_ONLY_HAS_DISMISSED_CONCIERGE_NOTIFICATION_BANNER,
ONYXKEYS.RAM_ONLY_DOMAIN_MEMBERS_SELECTED_FOR_MOVE,
],
});
diff --git a/tests/actions/ConciergeNotificationBannerTest.ts b/tests/actions/ConciergeNotificationBannerTest.ts
deleted file mode 100644
index 41902f3e15d7..000000000000
--- a/tests/actions/ConciergeNotificationBannerTest.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import Onyx from 'react-native-onyx';
-import {dismissForSession} from '@libs/actions/ConciergeNotificationBanner';
-import ONYXKEYS from '@src/ONYXKEYS';
-import getOnyxValue from '../utils/getOnyxValue';
-import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
-
-describe('ConciergeNotificationBanner', () => {
- beforeAll(() => {
- Onyx.init({keys: ONYXKEYS});
- });
-
- beforeEach(async () => {
- await Onyx.clear();
- });
-
- describe('dismissForSession', () => {
- it('writes true to the RAM-only dismissal flag', async () => {
- dismissForSession();
- await waitForBatchedUpdates();
- const value = await getOnyxValue(ONYXKEYS.RAM_ONLY_HAS_DISMISSED_CONCIERGE_NOTIFICATION_BANNER);
- expect(value).toBe(true);
- });
- });
-});
diff --git a/tests/unit/ComposerLocalTimeTest.tsx b/tests/unit/ComposerLocalTimeTest.tsx
index 7bf6f13c107e..0fdb4ca2e4f8 100644
--- a/tests/unit/ComposerLocalTimeTest.tsx
+++ b/tests/unit/ComposerLocalTimeTest.tsx
@@ -5,7 +5,6 @@ import ComposeProviders from '@components/ComposeProviders';
import {LocaleContextProvider} from '@components/LocaleContextProvider';
import OnyxListItemProvider from '@components/OnyxListItemProvider';
import ComposerLocalTime from '@pages/inbox/report/ReportActionCompose/ComposerLocalTime';
-import ComposerProvider from '@pages/inbox/report/ReportActionCompose/ComposerProvider';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {PersonalDetailsList, Report} from '@src/types/onyx';
@@ -38,12 +37,8 @@ function buildReport(participantAccountIDs: number[]): Report {
} as Report;
}
-function renderWithProviders(component: React.ReactElement, reportID: string) {
- return render(
-
- {component}
- ,
- );
+function renderWithProviders(component: React.ReactElement) {
+ return render({component});
}
describe('ComposerLocalTime', () => {
@@ -72,7 +67,7 @@ describe('ComposerLocalTime', () => {
await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, personalDetails);
await waitForBatchedUpdates();
- renderWithProviders(, REPORT_ID);
+ renderWithProviders();
await waitForBatchedUpdates();
@@ -98,7 +93,7 @@ describe('ComposerLocalTime', () => {
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${REPORT_ID}`, true);
await waitForBatchedUpdates();
- const {toJSON} = renderWithProviders(, REPORT_ID);
+ const {toJSON} = renderWithProviders();
await waitForBatchedUpdates();
@@ -122,7 +117,7 @@ describe('ComposerLocalTime', () => {
await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, personalDetails);
await waitForBatchedUpdates();
- const {toJSON} = renderWithProviders(, REPORT_ID);
+ const {toJSON} = renderWithProviders();
await waitForBatchedUpdates();
diff --git a/tests/unit/EnableNotificationsBannerTest.tsx b/tests/unit/EnableNotificationsBannerTest.tsx
deleted file mode 100644
index dfc3000c07f9..000000000000
--- a/tests/unit/EnableNotificationsBannerTest.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import {fireEvent, render, screen} from '@testing-library/react-native';
-import React from 'react';
-import Onyx from 'react-native-onyx';
-import {LocaleContextProvider} from '@components/LocaleContextProvider';
-import OnyxListItemProvider from '@components/OnyxListItemProvider';
-import {dismissForSession} from '@libs/actions/ConciergeNotificationBanner';
-import type {NotificationPermissionStatus} from '@libs/Notification/notificationPermission/types';
-import EnableNotificationsBanner from '@pages/inbox/report/EnableNotificationsBanner';
-import ONYXKEYS from '@src/ONYXKEYS';
-import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
-
-let mockRequestResult: NotificationPermissionStatus = 'default';
-const mockRequest = jest.fn(() => Promise.resolve(mockRequestResult));
-
-jest.mock('@libs/Notification/notificationPermission', () => ({
- __esModule: true,
- default: {
- getStatus: () => Promise.resolve('default' as NotificationPermissionStatus),
- request: () => mockRequest(),
- },
-}));
-
-jest.mock('@libs/actions/ConciergeNotificationBanner', () => ({
- dismissForSession: jest.fn(),
-}));
-
-function renderBanner() {
- return render(
-
-
-
-
- ,
- );
-}
-
-describe('EnableNotificationsBanner', () => {
- beforeAll(() => {
- Onyx.init({keys: ONYXKEYS});
- });
-
- beforeEach(async () => {
- jest.clearAllMocks();
- mockRequestResult = 'default';
- await Onyx.clear();
- });
-
- it('renders the prompt text and both action buttons', async () => {
- renderBanner();
- await waitForBatchedUpdatesWithAct();
- expect(screen.getByText('Want to be notified when Concierge responds?')).toBeOnTheScreen();
- expect(screen.getByText('Notify')).toBeOnTheScreen();
- expect(screen.getByText('Not now')).toBeOnTheScreen();
- });
-
- it('calls dismissForSession when "Not now" is pressed', async () => {
- renderBanner();
- await waitForBatchedUpdatesWithAct();
- fireEvent.press(screen.getByText('Not now'));
- expect(dismissForSession).toHaveBeenCalledTimes(1);
- });
-
- it('requests permission when "Notify" is pressed and dismisses on grant', async () => {
- mockRequestResult = 'granted';
- renderBanner();
- await waitForBatchedUpdatesWithAct();
- fireEvent.press(screen.getByText('Notify'));
- await waitForBatchedUpdatesWithAct();
- expect(mockRequest).toHaveBeenCalledTimes(1);
- expect(dismissForSession).toHaveBeenCalledTimes(1);
- });
-
- it('dismisses after request even when permission is denied so the banner does not get stuck', async () => {
- mockRequestResult = 'denied';
- renderBanner();
- await waitForBatchedUpdatesWithAct();
- fireEvent.press(screen.getByText('Notify'));
- await waitForBatchedUpdatesWithAct();
- expect(mockRequest).toHaveBeenCalledTimes(1);
- expect(dismissForSession).toHaveBeenCalledTimes(1);
- });
-});
diff --git a/tests/unit/hooks/useShouldShowEnableNotificationsBanner.test.ts b/tests/unit/hooks/useShouldShowEnableNotificationsBanner.test.ts
deleted file mode 100644
index d11b045fcc5c..000000000000
--- a/tests/unit/hooks/useShouldShowEnableNotificationsBanner.test.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import {renderHook, waitFor} from '@testing-library/react-native';
-import Onyx from 'react-native-onyx';
-import type {NotificationPermissionStatus} from '@libs/Notification/notificationPermission/types';
-import useShouldShowEnableNotificationsBanner from '@pages/inbox/report/useShouldShowEnableNotificationsBanner';
-import ONYXKEYS from '@src/ONYXKEYS';
-
-const CONCIERGE_REPORT_ID = '1';
-const OTHER_REPORT_ID = '2';
-
-let mockCurrentPermission: NotificationPermissionStatus = 'default';
-
-jest.mock('@libs/Notification/notificationPermission', () => ({
- __esModule: true,
- default: {
- getStatus: () => Promise.resolve(mockCurrentPermission),
- request: () => Promise.resolve(mockCurrentPermission),
- },
-}));
-
-describe('useShouldShowEnableNotificationsBanner', () => {
- beforeAll(() => {
- Onyx.init({keys: ONYXKEYS});
- });
-
- beforeEach(async () => {
- mockCurrentPermission = 'default';
- await Onyx.clear();
- await Onyx.merge(ONYXKEYS.CONCIERGE_REPORT_ID, CONCIERGE_REPORT_ID);
- await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${CONCIERGE_REPORT_ID}`, {reportID: CONCIERGE_REPORT_ID});
- await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${OTHER_REPORT_ID}`, {reportID: OTHER_REPORT_ID});
- });
-
- it('returns false when reportID is undefined', async () => {
- const {result} = renderHook(() => useShouldShowEnableNotificationsBanner(undefined));
- await waitFor(() => expect(result.current).toBe(false));
- });
-
- it('returns false in a non-Concierge report', async () => {
- const {result} = renderHook(() => useShouldShowEnableNotificationsBanner(OTHER_REPORT_ID));
- await waitFor(() => expect(result.current).toBe(false));
- });
-
- it('returns false when the user has already dismissed the banner this session', async () => {
- await Onyx.set(ONYXKEYS.RAM_ONLY_HAS_DISMISSED_CONCIERGE_NOTIFICATION_BANNER, true);
- const {result} = renderHook(() => useShouldShowEnableNotificationsBanner(CONCIERGE_REPORT_ID));
- await waitFor(() => expect(result.current).toBe(false));
- });
-
- it('returns false when notifications are already granted', async () => {
- mockCurrentPermission = 'granted';
- const {result} = renderHook(() => useShouldShowEnableNotificationsBanner(CONCIERGE_REPORT_ID));
- // Give the async probe a tick to resolve, then assert it stays false.
- await waitFor(() => expect(result.current).toBe(false));
- });
-
- it('returns false when notifications are denied', async () => {
- mockCurrentPermission = 'denied';
- const {result} = renderHook(() => useShouldShowEnableNotificationsBanner(CONCIERGE_REPORT_ID));
- await waitFor(() => expect(result.current).toBe(false));
- });
-
- it('returns true in the Concierge report when permission is default and not dismissed', async () => {
- const {result} = renderHook(() => useShouldShowEnableNotificationsBanner(CONCIERGE_REPORT_ID));
- await waitFor(() => expect(result.current).toBe(true));
- });
-});