From 4cc504a4cddd8a52187525600595d236714ebffd Mon Sep 17 00:00:00 2001 From: Shawn Borton Date: Fri, 17 Apr 2026 12:29:29 +0200 Subject: [PATCH 1/7] Replace Priority Mode with Inbox Tab Filters Replace the hidden Focus/Priority mode toggle in Account > Preferences with visible tab filters at the top of the Inbox: All, Unread, Expenses, and Direct messages. This gives users immediate, discoverable control over which chats they see. - Add CONST.INBOX_TAB and ONYXKEYS.NVP_INBOX_TAB for tab state - Add filterReportsForInboxTab() post-filter in SidebarUtils - Add InboxTabSelector component using TabSelectorBase with small size - Add 'small' size variant to TabSelector components (28px height, 11px font) - Remove isInFocusMode from the entire report pipeline - Remove PriorityModeHandler, PriorityModeController, FocusModeNotification - Remove PriorityModePage and its route/navigation config - Remove updateChatPriorityMode API command - Update all test files Co-Authored-By: Claude Opus 4.6 (1M context) --- src/CONST/index.ts | 9 +- src/Expensify.tsx | 2 - src/ONYXKEYS.ts | 10 +- src/PriorityModeHandler.tsx | 30 ----- src/ROUTES.ts | 1 - src/SCREENS.ts | 1 - src/components/FocusModeNotification.tsx | 46 -------- .../LHNOptionsList/OptionRowLHN.tsx | 23 ++-- src/components/PriorityModeController.tsx | 110 ------------------ .../ScrollOffsetContextProvider.tsx | 10 +- src/components/TabSelector/TabLabel.tsx | 11 +- .../TabSelector/TabSelectorBase.tsx | 4 +- .../TabSelector/TabSelectorItem.tsx | 3 + src/components/TabSelector/types.ts | 10 +- src/hooks/useSidebarOrderedReports.tsx | 70 +++++++---- src/languages/de.ts | 23 +--- src/languages/en.ts | 23 +--- src/languages/es.ts | 24 +--- src/languages/fr.ts | 23 +--- src/languages/it.ts | 23 +--- src/languages/ja.ts | 23 +--- src/languages/nl.ts | 23 +--- src/languages/pl.ts | 23 +--- src/languages/pt-BR.ts | 23 +--- src/languages/zh-hans.ts | 22 +--- .../UpdateChatPriorityModeParams.ts | 9 -- src/libs/API/parameters/index.ts | 1 - src/libs/API/types.ts | 2 - src/libs/DebugUtils.ts | 3 - .../Navigation/AppNavigator/AuthScreens.tsx | 2 - .../ModalStackNavigators/index.tsx | 1 - .../RELATIONS/SETTINGS_TO_RHP.ts | 7 +- .../linkingConfig/RELATIONS/SIDEBAR_TO_RHP.ts | 2 +- src/libs/Navigation/linkingConfig/config.ts | 4 - src/libs/Navigation/types.ts | 1 - src/libs/OptionsListUtils/index.ts | 1 - src/libs/ReportUtils.ts | 17 +-- src/libs/SidebarUtils.ts | 59 +++++++--- src/libs/UnreadIndicatorUpdater/index.ts | 1 - src/libs/actions/App.ts | 1 - src/libs/actions/Delegate.ts | 1 - src/libs/actions/QueuedOnyxUpdates.ts | 1 - src/libs/actions/Session/index.ts | 1 - src/libs/actions/User.ts | 36 +----- src/pages/Debug/Report/DebugReportPage.tsx | 4 +- src/pages/inbox/sidebar/BaseSidebarScreen.tsx | 2 + src/pages/inbox/sidebar/InboxTabSelector.tsx | 35 ++++++ src/pages/inbox/sidebar/SidebarLinks.tsx | 11 +- src/pages/inbox/sidebar/SidebarLinksData.tsx | 6 - .../settings/Preferences/PreferencesPage.tsx | 10 -- .../settings/Preferences/PriorityModePage.tsx | 68 ----------- src/styles/index.ts | 15 +++ src/types/onyx/InboxTab.ts | 6 + src/types/onyx/PriorityMode.ts | 8 -- tests/actions/QueuedOnyxUpdatesTest.ts | 2 - tests/perf-test/SidebarLinks.perf-test.tsx | 4 +- tests/ui/LHNItemsPresence.tsx | 26 ----- tests/unit/ReportUtilsTest.ts | 71 ----------- tests/unit/SidebarFilterTest.ts | 12 -- tests/unit/SidebarOrderTest.ts | 17 --- tests/unit/SidebarTest.ts | 4 +- tests/unit/SidebarUtilsTest.ts | 14 +-- 62 files changed, 267 insertions(+), 768 deletions(-) delete mode 100644 src/PriorityModeHandler.tsx delete mode 100644 src/components/FocusModeNotification.tsx delete mode 100644 src/components/PriorityModeController.tsx delete mode 100644 src/libs/API/parameters/UpdateChatPriorityModeParams.ts create mode 100644 src/pages/inbox/sidebar/InboxTabSelector.tsx delete mode 100644 src/pages/settings/Preferences/PriorityModePage.tsx create mode 100644 src/types/onyx/InboxTab.ts delete mode 100644 src/types/onyx/PriorityMode.ts diff --git a/src/CONST/index.ts b/src/CONST/index.ts index b33731461863..3fbca9aa9280 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -2083,9 +2083,11 @@ const CONST = { MEMORY_THRESHOLD_IOS_WARNING_MB: 300, // > 300MB monitor closely }, }, - PRIORITY_MODE: { - GSD: 'gsd', - DEFAULT: 'default', + INBOX_TAB: { + ALL: 'all', + UNREADS: 'unreads', + EXPENSES: 'expenses', + DIRECT_MESSAGES: 'directMessages', }, THEME: { DEFAULT: 'system', @@ -9485,7 +9487,6 @@ const CONST = { ADDRESS: 'SettingsProfile-Address', }, SETTINGS_PREFERENCES: { - PRIORITY_MODE: 'SettingsPreferences-PriorityMode', LANGUAGE: 'SettingsPreferences-Language', PAYMENT_CURRENCY: 'SettingsPreferences-PaymentCurrency', THEME: 'SettingsPreferences-Theme', diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 70f8a81666f6..902b9b101e27 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -33,7 +33,6 @@ import {startBootsplashMonitor} from './libs/telemetry/bootsplashTelemetry'; import {cleanupMemoryTrackingTelemetry, initializeMemoryTrackingTelemetry} from './libs/telemetry/TelemetrySynchronizer'; import Visibility from './libs/Visibility'; import ONYXKEYS from './ONYXKEYS'; -import PriorityModeHandler from './PriorityModeHandler'; import type {Route} from './ROUTES'; import {useSplashScreenActions, useSplashScreenState} from './SplashScreenStateContext'; @@ -273,7 +272,6 @@ function Expensify() { return ( <> {shouldInit && } - diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index b3062bf03dc7..b7da30c7c155 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -159,8 +159,8 @@ const ONYXKEYS = { /** Contains the platforms for which the user muted the sounds */ NVP_MUTED_PLATFORMS: 'nvp_mutedPlatforms', - /** Contains the user preference for the LHN priority mode */ - NVP_PRIORITY_MODE: 'nvp_priorityMode', + /** Contains the user preference for the active inbox tab filter */ + NVP_INBOX_TAB: 'nvp_inboxTab', /** Contains the users's block expiration (if they have one) */ NVP_BLOCKED_FROM_CONCIERGE: 'nvp_private_blockedFromConcierge', @@ -212,9 +212,6 @@ const ONYXKEYS = { /** Whether the app is currently loading a translation */ RAM_ONLY_ARE_TRANSLATIONS_LOADING: 'areTranslationsLoading', - /** Whether the user has tried focus mode yet */ - NVP_TRY_FOCUS_MODE: 'nvp_tryFocusMode', - /** Whether the user has dismissed the hold educational interstitial */ NVP_DISMISSED_HOLD_USE_EXPLANATION: 'nvp_dismissedHoldUseExplanation', @@ -1356,7 +1353,7 @@ type OnyxValuesMapping = { [ONYXKEYS.BETAS]: OnyxTypes.Beta[]; [ONYXKEYS.BETA_CONFIGURATION]: OnyxTypes.BetaConfiguration; [ONYXKEYS.NVP_MUTED_PLATFORMS]: Partial>; - [ONYXKEYS.NVP_PRIORITY_MODE]: ValueOf; + [ONYXKEYS.NVP_INBOX_TAB]: ValueOf; [ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE]: OnyxTypes.BlockedFromConcierge; [ONYXKEYS.QUEUE_FLUSHED_DATA]: AnyOnyxUpdate[]; [ONYXKEYS.TRANSACTIONS_PENDING_3DS_REVIEW]: OnyxTypes.TransactionsPending3DSReview; @@ -1366,7 +1363,6 @@ type OnyxValuesMapping = { [ONYXKEYS.NVP_BLOCKED_FROM_CHAT]: string; [ONYXKEYS.NVP_PRIVATE_PUSH_NOTIFICATION_ID]: string; [ONYXKEYS.NVP_RECENT_ATTENDEES]: Attendee[]; - [ONYXKEYS.NVP_TRY_FOCUS_MODE]: boolean; [ONYXKEYS.NVP_DISMISSED_HOLD_USE_EXPLANATION]: boolean; [ONYXKEYS.NVP_DISMISSED_ASAP_SUBMIT_EXPLANATION]: boolean; [ONYXKEYS.NVP_EMPTY_REPORTS_CONFIRMATION_DISMISSED]: boolean; diff --git a/src/PriorityModeHandler.tsx b/src/PriorityModeHandler.tsx deleted file mode 100644 index 3c3326f24807..000000000000 --- a/src/PriorityModeHandler.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import {useEffect} from 'react'; -import CONST from './CONST'; -import useOnyx from './hooks/useOnyx'; -import usePrevious from './hooks/usePrevious'; -import {openApp} from './libs/actions/App'; -import ONYXKEYS from './ONYXKEYS'; - -/** - * Component that does not render anything but isolates priority-mode–related Onyx subscriptions - * (NVP_PRIORITY_MODE, COLLECTION.REPORT_DRAFT_COMMENT) from the root Expensify component so that - * changes to these keys do not re-render the entire navigation tree. - */ -function PriorityModeHandler() { - const [priorityMode] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE); - const [allReportsWithDraftComments] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT); - const prevPriorityMode = usePrevious(priorityMode); - - useEffect(() => { - if (!(prevPriorityMode === CONST.PRIORITY_MODE.GSD && priorityMode === CONST.PRIORITY_MODE.DEFAULT)) { - return; - } - // When a user switches their priority mode away from #focus/GSD we need to call openApp - // to fetch all their chats because #focus mode works with a subset of a user's chats. - openApp(false, allReportsWithDraftComments); - }, [priorityMode, allReportsWithDraftComments, prevPriorityMode]); - - return null; -} - -export default PriorityModeHandler; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 5477d2b21a3c..e30765e78f81 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -556,7 +556,6 @@ const ROUTES = { // eslint-disable-next-line no-restricted-syntax -- Legacy route generation getRoute: (backTo?: string) => getUrlWithBackToParam('settings/subscription/downgrade-blocked', backTo), }, - SETTINGS_PRIORITY_MODE: 'settings/preferences/priority-mode', SETTINGS_LANGUAGE: 'settings/preferences/language', SETTINGS_PAYMENT_CURRENCY: 'setting/preferences/payment-currency', SETTINGS_THEME: 'settings/preferences/theme', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 933edaedffee..8316e488c214 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -160,7 +160,6 @@ const SCREENS = { PREFERENCES: { ROOT: 'Settings_Preferences', - PRIORITY_MODE: 'Settings_Preferences_PriorityMode', LANGUAGE: 'Settings_Preferences_Language', THEME: 'Settings_Preferences_Theme', PAYMENT_CURRENCY: 'Settings_Payment_Currency', diff --git a/src/components/FocusModeNotification.tsx b/src/components/FocusModeNotification.tsx deleted file mode 100644 index d7f82bf7defc..000000000000 --- a/src/components/FocusModeNotification.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import useEnvironment from '@hooks/useEnvironment'; -import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset'; -import useLocalize from '@hooks/useLocalize'; -import useStyleUtils from '@hooks/useStyleUtils'; -import useThemeStyles from '@hooks/useThemeStyles'; -import colors from '@styles/theme/colors'; -import ConfirmModal from './ConfirmModal'; -import RenderHTML from './RenderHTML'; - -type FocusModeNotificationProps = { - onClose: () => void; -}; - -function FocusModeNotification({onClose}: FocusModeNotificationProps) { - const styles = useThemeStyles(); - const StyleUtils = useStyleUtils(); - const illustrations = useMemoizedLazyIllustrations(['ThreeLeggedLaptopWoman']); - const {environmentURL} = useEnvironment(); - const {translate} = useLocalize(); - const priorityModePageUrl = `${environmentURL}/settings/preferences/priority-mode`; - - return ( - - - - } - success - isVisible - image={illustrations.ThreeLeggedLaptopWoman} - imageStyles={StyleUtils.getBackgroundColorStyle(colors.pink800)} - titleStyles={[styles.textHeadline, styles.mbn3]} - /> - ); -} - -export default FocusModeNotification; diff --git a/src/components/LHNOptionsList/OptionRowLHN.tsx b/src/components/LHNOptionsList/OptionRowLHN.tsx index 4f230b11cbf9..c1612c0a17d4 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.tsx +++ b/src/components/LHNOptionsList/OptionRowLHN.tsx @@ -89,12 +89,13 @@ function OptionRowLHN({ const {translate} = useLocalize(); const [isContextMenuActive, setIsContextMenuActive] = useState(false); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const isInFocusMode = viewMode === CONST.OPTION_MODE.COMPACT; - const sidebarInnerRowStyle = StyleSheet.flatten( - isInFocusMode - ? [styles.chatLinkRowPressable, styles.flexGrow1, styles.optionItemAvatarNameWrapper, styles.optionRowCompact, styles.justifyContentCenter] - : [styles.chatLinkRowPressable, styles.flexGrow1, styles.optionItemAvatarNameWrapper, styles.optionRow, styles.justifyContentCenter], - ); + const sidebarInnerRowStyle = StyleSheet.flatten([ + styles.chatLinkRowPressable, + styles.flexGrow1, + styles.optionItemAvatarNameWrapper, + styles.optionRow, + styles.justifyContentCenter, + ]); const alternateTextContainsCustomEmojiWithText = useMemo( () => containsCustomEmojiUtils(optionItem?.alternateText) && !containsOnlyCustomEmoji(optionItem?.alternateText), @@ -171,11 +172,9 @@ function OptionRowLHN({ const textStyle = isOptionFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText; const textUnreadStyle = shouldUseBoldText(optionItem) ? [textStyle, styles.sidebarLinkTextBold] : [textStyle]; const displayNameStyle = [styles.optionDisplayName, styles.optionDisplayNameCompact, styles.pre, textUnreadStyle, styles.flexShrink0, style]; - const alternateTextStyle = isInFocusMode - ? [textStyle, styles.textLabelSupporting, styles.optionAlternateTextCompact, styles.ml2, style] - : [textStyle, styles.optionAlternateText, styles.textLabelSupporting, style]; + const alternateTextStyle = [textStyle, styles.optionAlternateText, styles.textLabelSupporting, style]; - const contentContainerStyles = isInFocusMode ? [styles.flex1, styles.flexRow, styles.overflowHidden, StyleUtils.getCompactContentContainerStyles()] : [styles.flex1]; + const contentContainerStyles = [styles.flex1]; const hoveredBackgroundColor = !!styles.sidebarLinkHover && 'backgroundColor' in styles.sidebarLinkHover ? styles.sidebarLinkHover.backgroundColor : theme.sidebar; const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor; @@ -329,9 +328,9 @@ function OptionRowLHN({ >) => priorityMode === CONST.PRIORITY_MODE.GSD; - -/** - * This component is used to automatically switch a user into #focus mode when they exceed a certain number of reports. - * We do this primarily for performance reasons. Similar to the "Welcome action" we must wait for a number of things to - * happen when the user signs in or refreshes the page: - * - * - NVP that tracks whether they have already been switched over. We only do this once. - * - Priority mode NVP (that dictates the ordering/filtering logic of the LHN) - * - Reports to load (in ReconnectApp or OpenApp). As we check the count of the reports to determine whether the - * user is eligible to be automatically switched. - * - */ -export default function PriorityModeController() { - const {orderedReportIDs} = useSidebarOrderedReportsState(); - const lhnReportCount = orderedReportIDs.length; - const [isLoadingReportData] = useOnyx(ONYXKEYS.IS_LOADING_REPORT_DATA); - const [isInFocusMode, isInFocusModeMetadata] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE, {selector: isInFocusModeSelector}); - const [hasTriedFocusMode, hasTriedFocusModeMetadata] = useOnyx(ONYXKEYS.NVP_TRY_FOCUS_MODE); - const currentRouteName = useCurrentRouteName(); - const [shouldShowModal, setShouldShowModal] = useState(false); - const closeModal = useCallback(() => setShouldShowModal(false), []); - - // We set this when we have finally auto-switched the user of #focus mode to prevent duplication. - const hasSwitched = useRef(false); - - // Listen for state changes and trigger the #focus mode when appropriate - useEffect(() => { - // Wait for Onyx state to fully load - if ( - isLoadingReportData !== false || - isLoadingOnyxValue(isInFocusModeMetadata, hasTriedFocusModeMetadata) || - typeof isInFocusMode !== 'boolean' || - typeof hasTriedFocusMode !== 'boolean' - ) { - return; - } - - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (hasSwitched.current || isInFocusMode || hasTriedFocusMode) { - return; - } - - if (lhnReportCount < CONST.REPORT.MAX_COUNT_BEFORE_FOCUS_UPDATE) { - Log.info('[PriorityModeController] Not switching user to focus mode as they do not have enough reports', false, {lhnReportCount}); - return; - } - - // We wait for the user to navigate back to the home screen before triggering this switch - const isNarrowLayout = getIsNarrowLayout(); - if ((isNarrowLayout && currentRouteName !== SCREENS.INBOX) || (!isNarrowLayout && currentRouteName !== SCREENS.REPORT)) { - Log.info("[PriorityModeController] Not switching user to focus mode as they aren't on the home screen", false, {lhnReportCount, currentRouteName}); - return; - } - - Log.info('[PriorityModeController] Switching user to focus mode', false, {lhnReportCount, hasTriedFocusMode, isInFocusMode, currentRouteName}); - updateChatPriorityMode(CONST.PRIORITY_MODE.GSD, true); - requestAnimationFrame(() => { - setShouldShowModal(true); - }); - hasSwitched.current = true; - }, [currentRouteName, hasTriedFocusMode, hasTriedFocusModeMetadata, isInFocusMode, isInFocusModeMetadata, isLoadingReportData, lhnReportCount]); - - useEffect(() => { - if (!shouldShowModal) { - return; - } - const isNavigatingToPriorityModePage = currentRouteName === SCREENS.SETTINGS.PREFERENCES.PRIORITY_MODE; - - // Hide focus modal when settings button is pressed from the prompt. - if (isNavigatingToPriorityModePage) { - setShouldShowModal(false); - } - }, [currentRouteName, shouldShowModal]); - - return shouldShowModal ? : null; -} - -/** - * A funky but reliable way to subscribe to screen changes. - */ -function useCurrentRouteName() { - const navigation = useNavigation(); - const [currentRouteName, setCurrentRouteName] = useState(''); - - useEffect(() => { - const unsubscribe = navigation.addListener('state', () => { - setCurrentRouteName(navigationRef.getCurrentRoute()?.name); - }); - return () => unsubscribe(); - }, [navigation]); - - return currentRouteName; -} diff --git a/src/components/ScrollOffsetContextProvider.tsx b/src/components/ScrollOffsetContextProvider.tsx index f051497606d4..2a59f6ee533a 100644 --- a/src/components/ScrollOffsetContextProvider.tsx +++ b/src/components/ScrollOffsetContextProvider.tsx @@ -60,22 +60,22 @@ function getKey(route: PlatformStackRouteProp | NavigationPartial } function ScrollOffsetContextProvider({children}: ScrollOffsetContextProviderProps) { - const [priorityMode] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE); + const [inboxTab] = useOnyx(ONYXKEYS.NVP_INBOX_TAB); const scrollOffsetsRef = useRef>({}); - const previousPriorityMode = usePrevious(priorityMode); + const previousInboxTab = usePrevious(inboxTab); useEffect(() => { - if (previousPriorityMode === null || previousPriorityMode === priorityMode) { + if (previousInboxTab === null || previousInboxTab === inboxTab) { return; } - // If the priority mode changes, we need to clear the scroll offsets for the home and search screens because it affects the size of the elements and scroll positions wouldn't be correct. + // If the inbox tab changes, we need to clear the scroll offsets for the home and search screens because it affects the size of the elements and scroll positions wouldn't be correct. for (const key of Object.keys(scrollOffsetsRef.current)) { if (key.includes(SCREENS.INBOX) || key.includes(SCREENS.SEARCH.ROOT)) { delete scrollOffsetsRef.current[key]; } } - }, [priorityMode, previousPriorityMode]); + }, [inboxTab, previousInboxTab]); const saveScrollOffset: ScrollOffsetContextValue['saveScrollOffset'] = useCallback((route, scrollOffset) => { scrollOffsetsRef.current[getKey(route)] = scrollOffset; diff --git a/src/components/TabSelector/TabLabel.tsx b/src/components/TabSelector/TabLabel.tsx index e47490b89f38..c00d939cdb44 100644 --- a/src/components/TabSelector/TabLabel.tsx +++ b/src/components/TabSelector/TabLabel.tsx @@ -5,6 +5,7 @@ import type {StyleProp, TextStyle} from 'react-native'; import Text from '@components/Text'; import useThemeStyles from '@hooks/useThemeStyles'; import variables from '@styles/variables'; +import type {TabSelectorSize} from './types'; type TabLabelProps = { /** Title of the tab */ @@ -21,16 +22,20 @@ type TabLabelProps = { /** Text style */ textStyle?: StyleProp; + + /** Size variant */ + size?: TabSelectorSize; }; -function TabLabel({title = '', activeOpacity = 0, inactiveOpacity = 1, hasIcon = false, textStyle}: TabLabelProps) { +function TabLabel({title = '', activeOpacity = 0, inactiveOpacity = 1, hasIcon = false, textStyle, size}: TabLabelProps) { const styles = useThemeStyles(); + const smallTextStyle = size === 'small' ? styles.tabTextSmall : undefined; return ( {title} @@ -38,7 +43,7 @@ function TabLabel({title = '', activeOpacity = 0, inactiveOpacity = 1, hasIcon = {title} diff --git a/src/components/TabSelector/TabSelectorBase.tsx b/src/components/TabSelector/TabSelectorBase.tsx index 0c48ea1b091a..ff544715247e 100644 --- a/src/components/TabSelector/TabSelectorBase.tsx +++ b/src/components/TabSelector/TabSelectorBase.tsx @@ -27,6 +27,7 @@ function TabSelectorBase({ position, shouldShowLabelWhenInactive = true, equalWidth = false, + size, shouldShowProductTrainingTooltip = false, renderProductTrainingTooltip, }: TabSelectorBaseProps) { @@ -64,7 +65,7 @@ function TabSelectorBase({ }} ref={containerRef} style={styles.scrollableTabSelector} - contentContainerStyle={styles.tabSelectorContentContainer} + contentContainerStyle={[styles.tabSelectorContentContainer, size === 'small' && styles.tabSelectorContentContainerSmall]} horizontal showsHorizontalScrollIndicator={false} keyboardShouldPersistTaps="handled" @@ -123,6 +124,7 @@ function TabSelectorBase({ shouldShowProductTrainingTooltip={shouldShowProductTrainingTooltip} renderProductTrainingTooltip={renderProductTrainingTooltip} equalWidth={equalWidth} + size={size} badgeText={tab.badgeText} pendingAction={tab.pendingAction} isDisabled={tab.isDisabled} diff --git a/src/components/TabSelector/TabSelectorItem.tsx b/src/components/TabSelector/TabSelectorItem.tsx index fb5ada5f9d94..47c825b2f2ff 100644 --- a/src/components/TabSelector/TabSelectorItem.tsx +++ b/src/components/TabSelector/TabSelectorItem.tsx @@ -34,6 +34,7 @@ function TabSelectorItem({ shouldShowProductTrainingTooltip = false, renderProductTrainingTooltip, equalWidth = false, + size, badgeText, isDisabled = false, pendingAction, @@ -58,6 +59,7 @@ function TabSelectorItem({ accessibilityRole={CONST.ROLE.TAB} style={[ styles.tabSelectorButton, + size === 'small' && styles.tabSelectorButtonSmall, styles.tabBackground(isHovered, isActive, isDisabled, backgroundColor), styles.userSelectNone, isOfflineWithPendingAction ? styles.offlineFeedbackPending : undefined, @@ -89,6 +91,7 @@ function TabSelectorItem({ activeOpacity={styles.tabOpacity(isDisabled, isHovered, isActive, activeOpacity, inactiveOpacity).opacity} inactiveOpacity={styles.tabOpacity(isDisabled, isHovered, isActive, inactiveOpacity, activeOpacity).opacity} hasIcon={!!icon} + size={size} /> )} {!!badgeText && ( diff --git a/src/components/TabSelector/types.ts b/src/components/TabSelector/types.ts index 4d5ac355b75c..20e0d3e0861d 100644 --- a/src/components/TabSelector/types.ts +++ b/src/components/TabSelector/types.ts @@ -52,6 +52,8 @@ type TabSelectorBaseItem = WithSentryLabel & { pendingAction?: PendingAction; }; +type TabSelectorSize = 'default' | 'small'; + type TabSelectorBaseProps = { /** Tabs to render. */ tabs: TabSelectorBaseItem[]; @@ -77,6 +79,9 @@ type TabSelectorBaseProps = { /** Whether tabs should have equal width. */ equalWidth?: boolean; + /** Size variant for the tabs. 'small' uses a compact 28px height. */ + size?: TabSelectorSize; + /** Determines whether the product training tooltip should be displayed to the user. */ shouldShowProductTrainingTooltip?: boolean; @@ -121,6 +126,9 @@ type TabSelectorItemProps = WithSentryLabel & { /** Whether tabs should have equal width */ equalWidth?: boolean; + /** Size variant for the tabs. */ + size?: TabSelectorSize; + /** Determines whether the product training tooltip should be displayed to the user. */ shouldShowProductTrainingTooltip?: boolean; @@ -182,4 +190,4 @@ type BackgroundColor = Animated.AnimatedInterpolation | string; type Opacity = 1 | 0 | Animated.AnimatedInterpolation; -export type {TabSelectorProps, BackgroundColor, GetBackgroundColorConfig, Opacity, GetOpacityConfig, TabSelectorBaseProps, TabSelectorBaseItem, TabSelectorItemProps}; +export type {TabSelectorProps, BackgroundColor, GetBackgroundColorConfig, Opacity, GetOpacityConfig, TabSelectorBaseProps, TabSelectorBaseItem, TabSelectorItemProps, TabSelectorSize}; diff --git a/src/hooks/useSidebarOrderedReports.tsx b/src/hooks/useSidebarOrderedReports.tsx index 8ae7ad1a86bf..bd2ea03621c7 100644 --- a/src/hooks/useSidebarOrderedReports.tsx +++ b/src/hooks/useSidebarOrderedReports.tsx @@ -1,6 +1,8 @@ import {deepEqual} from 'fast-equals'; import React, {createContext, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import {setInboxTab} from '@libs/actions/User'; import Log from '@libs/Log'; import {getTransactionThreadReportID} from '@libs/MergeTransactionUtils'; import {isOneTransactionReport} from '@libs/ReportUtils'; @@ -33,10 +35,12 @@ type SidebarOrderedReportsStateContextValue = { orderedReportIDs: string[]; currentReportID: string | undefined; chatTabBrickRoad: BrickRoad; + activeTab: ValueOf; }; type SidebarOrderedReportsActionsContextValue = { clearLHNCache: () => void; + setActiveTab: (tab: ValueOf) => void; }; type ReportsToDisplayInLHN = Record; @@ -46,10 +50,12 @@ const SidebarOrderedReportsStateContext = createContext({ clearLHNCache: () => {}, + setActiveTab: () => {}, }); const policyMapper = (policy: OnyxEntry): PartialPolicyForSidebar => @@ -73,7 +79,8 @@ function SidebarOrderedReportsContextProvider({ currentReportIDForTests, }: SidebarOrderedReportsContextProviderProps) { const {localeCompare} = useLocalize(); - const [priorityMode = CONST.PRIORITY_MODE.DEFAULT] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE); + const [inboxTab = CONST.INBOX_TAB.ALL] = useOnyx(ONYXKEYS.NVP_INBOX_TAB); + const activeTab = inboxTab ?? CONST.INBOX_TAB.ALL; const [chatReports, {sourceValue: reportUpdates}] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [policies, {sourceValue: policiesUpdates}] = useMappedPolicies(policyMapper); const [transactions, {sourceValue: transactionsUpdates}] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION); @@ -94,7 +101,6 @@ function SidebarOrderedReportsContextProvider({ const [clearCacheDummyCounter, setClearCacheDummyCounter] = useState(0); const prevBetas = usePrevious(betas); - const prevPriorityMode = usePrevious(priorityMode); const perfRef = useRef<{hookDuration: number}>({ hookDuration: 0, @@ -107,7 +113,7 @@ function SidebarOrderedReportsContextProvider({ const getUpdatedReports = useCallback(() => { const reportsToUpdate = new Set(); - if (betas !== prevBetas || priorityMode !== prevPriorityMode) { + if (betas !== prevBetas) { for (const key of Object.keys(chatReports ?? {})) { reportsToUpdate.add(key); } @@ -177,9 +183,7 @@ function SidebarOrderedReportsContextProvider({ chatReports, transactions, betas, - priorityMode, prevBetas, - prevPriorityMode, prevDerivedCurrentReportID, derivedCurrentReportID, ]); @@ -200,7 +204,6 @@ function SidebarOrderedReportsContextProvider({ reports: chatReports, updatedReportsKeys: effectiveUpdatedReports, currentReportId: derivedCurrentReportID, - isInFocusMode: priorityMode === CONST.PRIORITY_MODE.GSD, betas, transactionViolations, reportNameValuePairs, @@ -214,7 +217,6 @@ function SidebarOrderedReportsContextProvider({ derivedCurrentReportID, chatReports, betas, - priorityMode, reportsDrafts, transactionViolations, transactions, @@ -226,7 +228,7 @@ function SidebarOrderedReportsContextProvider({ return reportsToDisplay; // Rule disabled intentionally — triggering a re-render on currentReportsToDisplay would cause an infinite loop // eslint-disable-next-line react-hooks/exhaustive-deps - }, [getUpdatedReports, chatReports, derivedCurrentReportID, priorityMode, betas, transactionViolations, reportNameValuePairs, reportAttributes, reportsDrafts, clearCacheDummyCounter]); + }, [getUpdatedReports, chatReports, derivedCurrentReportID, betas, transactionViolations, reportNameValuePairs, reportAttributes, reportsDrafts, clearCacheDummyCounter]); // Derive a stable boolean map indicating which reports have drafts. const hasDraftByReportIDRef = useRef>({}); @@ -253,16 +255,23 @@ function SidebarOrderedReportsContextProvider({ setCurrentReportsToDisplay(reportsToDisplayInLHN); }, [reportsToDisplayInLHN]); + const useAlphabeticalSort = activeTab === CONST.INBOX_TAB.UNREADS; + const getOrderedReportIDs = useCallback( - () => SidebarUtils.sortReportsToDisplayInLHN(reportsToDisplayInLHN, priorityMode, localeCompare, hasDraftByReportID, reportNameValuePairs, reportAttributes), + () => SidebarUtils.sortReportsToDisplayInLHN(reportsToDisplayInLHN, useAlphabeticalSort, localeCompare, hasDraftByReportID, reportNameValuePairs, reportAttributes), // Rule disabled intentionally - reports should be sorted only when the reportsToDisplayInLHN changes // eslint-disable-next-line react-hooks/exhaustive-deps - [reportsToDisplayInLHN, localeCompare, hasDraftByReportID, reportAttributes], + [reportsToDisplayInLHN, useAlphabeticalSort, localeCompare, hasDraftByReportID, reportAttributes], ); const orderedReportIDs = useMemo(() => getOrderedReportIDs(), [getOrderedReportIDs]); - // Get the actual reports based on the ordered IDs + const filteredReportIDs = useMemo( + () => SidebarUtils.filterReportsForInboxTab(orderedReportIDs, reportsToDisplayInLHN, activeTab, hasDraftByReportID, reportNameValuePairs), + [orderedReportIDs, reportsToDisplayInLHN, activeTab, hasDraftByReportID, reportNameValuePairs], + ); + + // Get the actual reports based on the filtered IDs const getOrderedReports = useCallback( (reportIDs: string[]): OnyxTypes.Report[] => { if (!chatReports) { @@ -273,7 +282,7 @@ function SidebarOrderedReportsContextProvider({ [chatReports], ); - const orderedReports = useMemo(() => getOrderedReports(orderedReportIDs), [getOrderedReports, orderedReportIDs]); + const orderedReports = useMemo(() => getOrderedReports(filteredReportIDs), [getOrderedReports, filteredReportIDs]); const clearLHNCache = useCallback(() => { Log.info('[useSidebarOrderedReports] Clearing sidebar cache manually via debug modal'); @@ -281,6 +290,10 @@ function SidebarOrderedReportsContextProvider({ setClearCacheDummyCounter((current) => current + 1); }, []); + const setActiveTab = useCallback((tab: ValueOf) => { + setInboxTab(tab); + }, []); + const stateValue: SidebarOrderedReportsStateContextValue = useMemo(() => { // We need to make sure the current report is in the list of reports, but we do not want // to have to re-generate the list every time the currentReportID changes. To do that @@ -293,33 +306,49 @@ function SidebarOrderedReportsContextProvider({ // any expense, a new LHN item is added in the list and is visible on web. But on mobile, we // just navigate to the screen with expense details, so there seems no point to execute this logic on mobile. if ( - (!shouldUseNarrowLayout || orderedReportIDs.length === 0) && + (!shouldUseNarrowLayout || filteredReportIDs.length === 0) && derivedCurrentReportID && derivedCurrentReportID !== '-1' && - orderedReportIDs.indexOf(derivedCurrentReportID) === -1 + filteredReportIDs.indexOf(derivedCurrentReportID) === -1 ) { const updatedReportIDs = getOrderedReportIDs(); - const updatedReports = getOrderedReports(updatedReportIDs); + const updatedFilteredIDs = SidebarUtils.filterReportsForInboxTab(updatedReportIDs, reportsToDisplayInLHN, activeTab, hasDraftByReportID, reportNameValuePairs); + const updatedReports = getOrderedReports(updatedFilteredIDs); return { orderedReports: updatedReports, - orderedReportIDs: updatedReportIDs, + orderedReportIDs: updatedFilteredIDs, currentReportID: derivedCurrentReportID, chatTabBrickRoad: getChatTabBrickRoad(updatedReportIDs, reportAttributes), + activeTab, }; } return { orderedReports, - orderedReportIDs, + orderedReportIDs: filteredReportIDs, currentReportID: derivedCurrentReportID, chatTabBrickRoad: getChatTabBrickRoad(orderedReportIDs, reportAttributes), + activeTab, }; - }, [getOrderedReportIDs, orderedReportIDs, derivedCurrentReportID, shouldUseNarrowLayout, getOrderedReports, orderedReports, reportAttributes]); + }, [ + getOrderedReportIDs, + orderedReportIDs, + filteredReportIDs, + derivedCurrentReportID, + shouldUseNarrowLayout, + getOrderedReports, + orderedReports, + reportAttributes, + activeTab, + reportsToDisplayInLHN, + hasDraftByReportID, + reportNameValuePairs, + ]); - const actionsValue: SidebarOrderedReportsActionsContextValue = useMemo(() => ({clearLHNCache}), [clearLHNCache]); + const actionsValue: SidebarOrderedReportsActionsContextValue = useMemo(() => ({clearLHNCache, setActiveTab}), [clearLHNCache, setActiveTab]); const currentDeps = { - priorityMode, + activeTab, chatReports, policies, transactions, @@ -334,7 +363,6 @@ function SidebarOrderedReportsContextProvider({ derivedCurrentReportID, prevDerivedCurrentReportID, prevBetas, - prevPriorityMode, reportsToDisplayInLHN, orderedReportIDs, orderedReports, diff --git a/src/languages/de.ts b/src/languages/de.ts index e22f119c149c..9c9f75001ff2 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -2786,19 +2786,11 @@ ${amount} für ${merchant} – ${date}`, receiveRelevantFeatureUpdatesAndExpensifyNews: 'Erhalten Sie relevante Funktionsupdates und Expensify-Neuigkeiten', muteAllSounds: 'Alle Expensify-Sounds stummschalten', }, - priorityModePage: { - priorityMode: 'Prioritätsmodus', - explainerText: 'Wähle, ob du dich nur auf ungelesene und angeheftete Chats #fokussieren möchtest oder alles anzeigen willst, wobei die neuesten und angehefteten Chats oben stehen.', - priorityModes: { - default: { - label: 'Neueste', - description: 'Alle Chats nach Neuestem sortiert anzeigen', - }, - gsd: { - label: '#fokus', - description: 'Nur ungelesene alphabetisch sortiert anzeigen', - }, - }, + inboxTabs: { + all: 'All', + unreads: 'Unread', + expenses: 'Expenses', + directMessages: 'Direct messages', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, @@ -3344,11 +3336,6 @@ ${amount} für ${merchant} – ${date}`, year: 'Jahr', selectYear: 'Bitte ein Jahr auswählen', }, - focusModeUpdateModal: { - title: 'Willkommen im #Fokusmodus!', - prompt: (priorityModePageUrl: string) => - `Behalte den Überblick, indem du nur ungelesene Chats oder Chats siehst, die deine Aufmerksamkeit benötigen. Keine Sorge, du kannst das jederzeit in den Einstellungen ändern.`, - }, notFound: { chatYouLookingForCannotBeFound: 'Der Chat, den du suchst, kann nicht gefunden werden.', getMeOutOfHere: 'Hol mich hier raus', diff --git a/src/languages/en.ts b/src/languages/en.ts index fb20d5d83d9c..12480bbb55c4 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2840,19 +2840,11 @@ const translations = { receiveRelevantFeatureUpdatesAndExpensifyNews: 'Receive relevant feature updates and Expensify news', muteAllSounds: 'Mute all sounds from Expensify', }, - priorityModePage: { - priorityMode: 'Priority mode', - explainerText: 'Choose whether to #focus on unread and pinned chats only, or show everything with the most recent and pinned chats at the top.', - priorityModes: { - default: { - label: 'Most recent', - description: 'Show all chats sorted by most recent', - }, - gsd: { - label: '#focus', - description: 'Only show unread sorted alphabetically', - }, - }, + inboxTabs: { + all: 'All', + unreads: 'Unread', + expenses: 'Expenses', + directMessages: 'Direct messages', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, @@ -3410,11 +3402,6 @@ const translations = { month: 'Month', selectMonth: 'Please select a month', }, - focusModeUpdateModal: { - title: 'Welcome to #focus mode!', - prompt: (priorityModePageUrl: string) => - `Stay on top of things by only seeing unread chats or chats that need your attention. Don’t worry, you can change this at any point in settings.`, - }, notFound: { chatYouLookingForCannotBeFound: 'The chat you are looking for cannot be found.', getMeOutOfHere: 'Get me out of here', diff --git a/src/languages/es.ts b/src/languages/es.ts index af44621bc5c6..d9243ca2d933 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2657,20 +2657,11 @@ ${amount} para ${merchant} - ${date}`, receiveRelevantFeatureUpdatesAndExpensifyNews: 'Recibir noticias sobre Expensify y actualizaciones del producto', muteAllSounds: 'Silenciar todos los sonidos de Expensify', }, - priorityModePage: { - priorityMode: 'Modo prioridad', - explainerText: - 'Elige #concentración si deseas enfocarte sólo en los chats no leídos y en los anclados, o mostrarlo todo con los chats más recientes y los anclados en la parte superior.', - priorityModes: { - default: { - label: 'Más recientes', - description: 'Mostrar todos los chats ordenados desde el más reciente', - }, - gsd: { - label: '#concentración', - description: 'Mostrar sólo los no leídos ordenados alfabéticamente', - }, - }, + inboxTabs: { + all: 'All', + unreads: 'Unread', + expenses: 'Expenses', + directMessages: 'Direct messages', }, reportDetailsPage: { inWorkspace: (policyName) => `en ${policyName}`, @@ -3219,11 +3210,6 @@ ${amount} para ${merchant} - ${date}`, month: 'Mes', selectMonth: 'Por favor, selecciona un mes', }, - focusModeUpdateModal: { - title: '¡Bienvenido al modo #concentración!', - prompt: (priorityModePageUrl) => - `Mantente al tanto de todo viendo sólo los chats no leídos o los que necesitan tu atención. No te preocupes, puedes cambiar el ajuste en cualquier momento desde la configuración.`, - }, notFound: { chatYouLookingForCannotBeFound: 'El chat que estás buscando no se pudo encontrar.', getMeOutOfHere: 'Sácame de aquí', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index dd2cf4363012..57e3862724a6 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -2792,19 +2792,11 @@ ${amount} pour ${merchant} - ${date}`, receiveRelevantFeatureUpdatesAndExpensifyNews: 'Recevoir des mises à jour de fonctionnalités pertinentes et des actualités Expensify', muteAllSounds: 'Couper tous les sons d’Expensify', }, - priorityModePage: { - priorityMode: 'Mode priorité', - explainerText: 'Choisissez de #vous concentrer uniquement sur les discussions non lues et épinglées, ou d’afficher tout avec les discussions les plus récentes et épinglées en haut.', - priorityModes: { - default: { - label: 'Le plus récent', - description: 'Afficher toutes les discussions triées par les plus récentes', - }, - gsd: { - label: '#focus', - description: 'Afficher uniquement les non lus triés par ordre alphabétique', - }, - }, + inboxTabs: { + all: 'All', + unreads: 'Unread', + expenses: 'Expenses', + directMessages: 'Direct messages', }, reportDetailsPage: { inWorkspace: (policyName: string) => `dans ${policyName}`, @@ -3353,11 +3345,6 @@ ${amount} pour ${merchant} - ${date}`, year: 'Année', selectYear: 'Veuillez sélectionner une année', }, - focusModeUpdateModal: { - title: 'Bienvenue en mode #focus !', - prompt: (priorityModePageUrl: string) => - `Gardez le contrôle en affichant uniquement les discussions non lues ou celles qui nécessitent votre attention. Ne vous inquiétez pas, vous pouvez modifier ce paramètre à tout moment dans les paramètres.`, - }, notFound: { chatYouLookingForCannotBeFound: 'La discussion que vous recherchez est introuvable.', getMeOutOfHere: 'Faites-moi sortir d’ici', diff --git a/src/languages/it.ts b/src/languages/it.ts index f177ee5d7b4f..eea77cc79424 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -2781,19 +2781,11 @@ ${amount} per ${merchant} - ${date}`, receiveRelevantFeatureUpdatesAndExpensifyNews: 'Ricevi aggiornamenti rilevanti sulle funzionalità e notizie su Expensify', muteAllSounds: 'Disattiva tutti i suoni da Expensify', }, - priorityModePage: { - priorityMode: 'Modalità prioritaria', - explainerText: 'Scegli se #concentrarti solo sulle chat non lette e fissate, oppure mostrare tutto con le chat più recenti e fissate in alto.', - priorityModes: { - default: { - label: 'Più recenti', - description: 'Mostra tutte le chat ordinate dalla più recente', - }, - gsd: { - label: '#focus', - description: 'Mostra solo i non letti in ordine alfabetico', - }, - }, + inboxTabs: { + all: 'All', + unreads: 'Unread', + expenses: 'Expenses', + directMessages: 'Direct messages', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, @@ -3336,11 +3328,6 @@ ${amount} per ${merchant} - ${date}`, year: 'Anno', selectYear: 'Seleziona un anno', }, - focusModeUpdateModal: { - title: 'Benvenuto/a nella modalità #focus!', - prompt: (priorityModePageUrl: string) => - `Resta sempre aggiornato vedendo solo le chat non lette o quelle che richiedono la tua attenzione. Non preoccuparti, puoi modificare questa impostazione in qualsiasi momento nelle impostazioni.`, - }, notFound: { chatYouLookingForCannotBeFound: 'La chat che stai cercando non può essere trovata.', getMeOutOfHere: 'Fammi uscire di qui', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 5fd9507b6714..455bf7a8850a 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -2754,19 +2754,11 @@ ${date} の ${merchant} への ${amount}`, receiveRelevantFeatureUpdatesAndExpensifyNews: '関連する機能のアップデートやExpensifyのニュースを受け取る', muteAllSounds: 'Expensify のすべてのサウンドをミュートする', }, - priorityModePage: { - priorityMode: '優先モード', - explainerText: '未読とピン留めされたチャットのみを#focusに表示するか、すべてのチャットを表示して、最新とピン留めされたチャットを上部に表示するかを選択してください。', - priorityModes: { - default: { - label: '最新', - description: '最新順ですべてのチャットを表示', - }, - gsd: { - label: '#focus', - description: '未読のみをアルファベット順で表示', - }, - }, + inboxTabs: { + all: 'All', + unreads: 'Unread', + expenses: 'Expenses', + directMessages: 'Direct messages', }, reportDetailsPage: { inWorkspace: (policyName: string) => `${policyName} 内`, @@ -3308,11 +3300,6 @@ ${integrationName === CONST.ONBOARDING_ACCOUNTING_MAPPING.other ? 'あなたの' year: '年', selectYear: '年を選択してください', }, - focusModeUpdateModal: { - title: '#focusモードへようこそ!', - prompt: (priorityModePageUrl: string) => - `未読のチャットや対応が必要なチャットだけを表示して、常に状況を把握しましょう。いつでも設定から変更できます。`, - }, notFound: { chatYouLookingForCannotBeFound: 'お探しのチャットが見つかりません。', getMeOutOfHere: 'ここから出して', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 4447363ef30f..4038df5299d4 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -2779,19 +2779,11 @@ ${amount} voor ${merchant} - ${date}`, receiveRelevantFeatureUpdatesAndExpensifyNews: 'Ontvang relevante functiewijzigingen en Expensify-nieuws', muteAllSounds: 'Alle geluiden van Expensify dempen', }, - priorityModePage: { - priorityMode: 'Prioriteitsmodus', - explainerText: 'Kies of je je wilt #focussen op alleen ongelezen en vastgezette chats, of alles wilt weergeven met de meest recente en vastgezette chats bovenaan.', - priorityModes: { - default: { - label: 'Meest recent', - description: 'Toon alle chats gesorteerd op meest recent', - }, - gsd: { - label: '#focus', - description: 'Toon alleen ongelezen, alfabetisch gesorteerd', - }, - }, + inboxTabs: { + all: 'All', + unreads: 'Unread', + expenses: 'Expenses', + directMessages: 'Direct messages', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, @@ -3333,11 +3325,6 @@ ${amount} voor ${merchant} - ${date}`, year: 'Jaar', selectYear: 'Selecteer een jaar', }, - focusModeUpdateModal: { - title: 'Welkom bij de #focus-modus!', - prompt: (priorityModePageUrl: string) => - `Houd het overzicht door alleen ongelezen chats of chats die je aandacht nodig hebben te zien. Geen zorgen, je kunt dit op elk moment wijzigen in de instellingen.`, - }, notFound: { chatYouLookingForCannotBeFound: 'De chat die je zoekt, kan niet worden gevonden.', getMeOutOfHere: 'Haal me hier weg', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index fd9e3b730a1d..496974e494b8 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -2772,19 +2772,11 @@ ${amount} dla ${merchant} - ${date}`, receiveRelevantFeatureUpdatesAndExpensifyNews: 'Otrzymuj istotne aktualizacje funkcji i wiadomości od Expensify', muteAllSounds: 'Wycisz wszystkie dźwięki z Expensify', }, - priorityModePage: { - priorityMode: 'Tryb priorytetowy', - explainerText: 'Wybierz, czy #skupić się tylko na nieprzeczytanych i przypiętych czatach, czy wyświetlać wszystko, z najnowszymi i przypiętymi czatami na górze.', - priorityModes: { - default: { - label: 'Najnowsze', - description: 'Pokaż wszystkie czaty posortowane od najnowszych', - }, - gsd: { - label: '#skupienie', - description: 'Pokaż tylko nieprzeczytane posortowane alfabetycznie', - }, - }, + inboxTabs: { + all: 'All', + unreads: 'Unread', + expenses: 'Expenses', + directMessages: 'Direct messages', }, reportDetailsPage: { inWorkspace: (policyName: string) => `w ${policyName}`, @@ -3324,11 +3316,6 @@ ${amount} dla ${merchant} - ${date}`, year: 'Rok', selectYear: 'Wybierz rok', }, - focusModeUpdateModal: { - title: 'Witamy w trybie #focus!', - prompt: (priorityModePageUrl: string) => - `Miej wszystko pod kontrolą, wyświetlając tylko nieprzeczytane czaty lub czaty wymagające Twojej uwagi. Nie martw się, możesz to zmienić w dowolnym momencie w ustawieniach.`, - }, notFound: { chatYouLookingForCannotBeFound: 'Nie można znaleźć czatu, którego szukasz.', getMeOutOfHere: 'Wyprowadź mnie stąd', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 52479b6be5a0..9cecddddfb48 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -2772,19 +2772,11 @@ ${amount} para ${merchant} - ${date}`, receiveRelevantFeatureUpdatesAndExpensifyNews: 'Receba atualizações relevantes de recursos e novidades da Expensify', muteAllSounds: 'Silenciar todos os sons do Expensify', }, - priorityModePage: { - priorityMode: 'Modo prioridade', - explainerText: 'Escolha se deseja #focar apenas em chats não lidos e fixados ou mostrar tudo, com os chats mais recentes e fixados no topo.', - priorityModes: { - default: { - label: 'Mais recente', - description: 'Mostrar todos os chats ordenados por mais recentes', - }, - gsd: { - label: '#foco', - description: 'Mostrar apenas não lidas em ordem alfabética', - }, - }, + inboxTabs: { + all: 'All', + unreads: 'Unread', + expenses: 'Expenses', + directMessages: 'Direct messages', }, reportDetailsPage: { inWorkspace: (policyName: string) => `em ${policyName}`, @@ -3325,11 +3317,6 @@ ${amount} para ${merchant} - ${date}`, year: 'Ano', selectYear: 'Selecione um ano', }, - focusModeUpdateModal: { - title: 'Bem-vindo ao modo #focus!', - prompt: (priorityModePageUrl: string) => - `Mantenha tudo sob controle vendo apenas os chats não lidos ou que precisam da sua atenção. Não se preocupe, você pode alterar isso a qualquer momento em configurações.`, - }, notFound: { chatYouLookingForCannotBeFound: 'O chat que você está procurando não foi encontrado.', getMeOutOfHere: 'Me tire daqui', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 40049ed183d1..aade94e81823 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -2704,19 +2704,11 @@ ${amount},商户:${merchant} - 日期:${date}`, receiveRelevantFeatureUpdatesAndExpensifyNews: '接收相关功能更新和 Expensify 新闻', muteAllSounds: '静音所有来自 Expensify 的声音', }, - priorityModePage: { - priorityMode: '优先模式', - explainerText: '选择仅#focus未读和置顶聊天,或显示所有聊天,并将最新和置顶聊天排在顶部。', - priorityModes: { - default: { - label: '最新', - description: '按最新排序显示所有聊天', - }, - gsd: { - label: '#focus', - description: '仅按字母顺序显示未读', - }, - }, + inboxTabs: { + all: 'All', + unreads: 'Unread', + expenses: 'Expenses', + directMessages: 'Direct messages', }, reportDetailsPage: { inWorkspace: (policyName: string) => `在 ${policyName} 中`, @@ -3255,10 +3247,6 @@ ${amount},商户:${merchant} - 日期:${date}`, year: '年份', selectYear: '请选择年份', }, - focusModeUpdateModal: { - title: '欢迎进入 #focus 模式!', - prompt: (priorityModePageUrl: string) => `通过仅查看未读聊天或需要你关注的聊天来随时掌握进展。别担心,你可以随时在设置中更改此项。`, - }, notFound: { chatYouLookingForCannotBeFound: '找不到您要查找的聊天。', getMeOutOfHere: '带我离开这里', diff --git a/src/libs/API/parameters/UpdateChatPriorityModeParams.ts b/src/libs/API/parameters/UpdateChatPriorityModeParams.ts deleted file mode 100644 index 8bbb7bf6943c..000000000000 --- a/src/libs/API/parameters/UpdateChatPriorityModeParams.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type {ValueOf} from 'type-fest'; -import type CONST from '@src/CONST'; - -type UpdateChatPriorityModeParams = { - value: ValueOf; - automatic: boolean; -}; - -export default UpdateChatPriorityModeParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 7c7c54e55efd..96ef0f32a456 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -91,7 +91,6 @@ export type {default as SignInWithShortLivedAuthTokenParams} from './SignInWithS export type {default as SignInWithSupportAuthTokenParams} from './SignInWithSupportAuthTokenParams'; export type {default as UnlinkLoginParams} from './UnlinkLoginParams'; export type {default as UpdateAutomaticTimezoneParams} from './UpdateAutomaticTimezoneParams'; -export type {default as UpdateChatPriorityModeParams} from './UpdateChatPriorityModeParams'; export type {default as DuplicateWorkspaceParams} from './DuplicateWorkspaceParams'; export type {default as UpdateDateOfBirthParams} from './UpdateDateOfBirthParams'; export type {default as UpdateDisplayNameParams} from './UpdateDisplayNameParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 15959c65d415..05255a2dae10 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -90,7 +90,6 @@ const WRITE_COMMANDS = { VALIDATE_LOGIN: 'ValidateLogin', VALIDATE_SECONDARY_LOGIN: 'ValidateSecondaryLogin', UPDATE_PREFERRED_EMOJI_SKIN_TONE: 'UpdatePreferredEmojiSkinTone', - UPDATE_CHAT_PRIORITY_MODE: 'UpdateChatPriorityMode', TOGGLE_PLATFORM_MUTE: 'TogglePlatformMute', SET_CONTACT_METHOD_AS_DEFAULT: 'SetContactMethodAsDefault', UPDATE_THEME: 'UpdateTheme', @@ -642,7 +641,6 @@ type WriteCommandParameters = { [WRITE_COMMANDS.VALIDATE_LOGIN]: Parameters.ValidateLoginParams; [WRITE_COMMANDS.VALIDATE_SECONDARY_LOGIN]: Parameters.ValidateSecondaryLoginParams; [WRITE_COMMANDS.UPDATE_PREFERRED_EMOJI_SKIN_TONE]: Parameters.UpdatePreferredEmojiSkinToneParams; - [WRITE_COMMANDS.UPDATE_CHAT_PRIORITY_MODE]: Parameters.UpdateChatPriorityModeParams; [WRITE_COMMANDS.SET_CONTACT_METHOD_AS_DEFAULT]: Parameters.SetContactMethodAsDefaultParams; [WRITE_COMMANDS.TOGGLE_PLATFORM_MUTE]: Parameters.TogglePlatformMuteParams; [WRITE_COMMANDS.UPDATE_THEME]: Parameters.UpdateThemeParams; diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index 31a0a1ee5ef5..882b2dc74af7 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -1405,7 +1405,6 @@ function getReasonForShowingRowInLHN({ doesReportHaveViolations, hasRBR = false, isReportArchived, - isInFocusMode = false, betas = undefined, draftComment, }: { @@ -1414,7 +1413,6 @@ function getReasonForShowingRowInLHN({ doesReportHaveViolations: boolean; hasRBR?: boolean; isReportArchived: boolean | undefined; - isInFocusMode?: boolean; betas?: OnyxEntry; draftComment: string | undefined; }): TranslationPaths | null { @@ -1427,7 +1425,6 @@ function getReasonForShowingRowInLHN({ chatReport, // We can't pass report.reportID because it will cause reason to always be isFocused currentReportId: '-1', - isInFocusMode, betas, excludeEmptyChats: true, doesReportHaveViolations, diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 9b381ec63314..63ac14d40790 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -12,7 +12,6 @@ import KYCWallContextProvider from '@components/KYCWall/KYCWallContext'; import LockedAccountModalProvider from '@components/LockedAccountModalProvider'; import OpenAppFailureModal from '@components/OpenAppFailureModal'; import OptionsListContextProvider from '@components/OptionListContextProvider'; -import PriorityModeController from '@components/PriorityModeController'; import {ProductTrainingContextProvider} from '@components/ProductTrainingContext'; import {SearchContextProvider} from '@components/Search/SearchContext'; import {SearchRouterContextProvider} from '@components/Search/SearchRouter/SearchRouterContext'; @@ -410,7 +409,6 @@ function AuthScreens() { - diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 36e3c84a261f..3774bbf6a49f 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -407,7 +407,6 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/settings/Profile/Contacts/NewContactMethodConfirmMagicCodePage').default, [SCREENS.SETTINGS.PROFILE.CONTACT_METHOD_VERIFY_ACCOUNT]: () => require('../../../../pages/settings/Profile/Contacts/VerifyAccountPage').default, - [SCREENS.SETTINGS.PREFERENCES.PRIORITY_MODE]: () => require('../../../../pages/settings/Preferences/PriorityModePage').default, [SCREENS.WORKSPACE.ACCOUNTING.ROOT]: () => require('../../../../pages/workspace/accounting/PolicyAccountingPage').default, [SCREENS.SETTINGS.PREFERENCES.LANGUAGE]: () => require('../../../../pages/settings/Preferences/LanguagePage').default, [SCREENS.SETTINGS.PREFERENCES.THEME]: () => require('../../../../pages/settings/Preferences/ThemePage').default, diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP.ts index 7e59623ed3ac..7ae9ae4d67dc 100755 --- a/src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP.ts +++ b/src/libs/Navigation/linkingConfig/RELATIONS/SETTINGS_TO_RHP.ts @@ -24,12 +24,7 @@ const SETTINGS_TO_RHP: Partial> = { - [SCREENS.SETTINGS.ROOT]: [SCREENS.SETTINGS.SHARE_CODE, SCREENS.SETTINGS.PROFILE.STATUS, SCREENS.SETTINGS.PREFERENCES.PRIORITY_MODE], + [SCREENS.SETTINGS.ROOT]: [SCREENS.SETTINGS.SHARE_CODE, SCREENS.SETTINGS.PROFILE.STATUS], }; export default SIDEBAR_TO_RHP; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 3057fbe0034d..401ac54bbe29 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -149,10 +149,6 @@ const config: LinkingOptions['config'] = { screens: { [SCREENS.RIGHT_MODAL.SETTINGS]: { screens: { - [SCREENS.SETTINGS.PREFERENCES.PRIORITY_MODE]: { - path: ROUTES.SETTINGS_PRIORITY_MODE, - exact: true, - }, [SCREENS.SETTINGS.PREFERENCES.LANGUAGE]: { path: ROUTES.SETTINGS_LANGUAGE, exact: true, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 214d096a99d1..c662d36fbdd2 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -125,7 +125,6 @@ type SettingsNavigatorParamList = { backTo?: Routes; forwardTo?: Routes; }; - [SCREENS.SETTINGS.PREFERENCES.PRIORITY_MODE]: undefined; [SCREENS.SETTINGS.PREFERENCES.PAYMENT_CURRENCY]: undefined; [SCREENS.SETTINGS.PREFERENCES.LANGUAGE]: undefined; [SCREENS.SETTINGS.PREFERENCES.THEME]: undefined; diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 6b372a0b54bf..a6a0619ebdc6 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -2157,7 +2157,6 @@ function isValidReport(option: SearchOption, policy: OnyxEntry, currentReportId: topmostReportId, betas, doesReportHaveViolations, - isInFocusMode: false, excludeEmptyChats: false, includeSelfDM, login: option.login, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 3c04a6ebab12..d238e7f22b62 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -9508,7 +9508,6 @@ type ShouldReportBeInOptionListParams = { report: OnyxEntry; chatReport: OnyxEntry; currentReportId: string | undefined; - isInFocusMode: boolean; betas: OnyxEntry; excludeEmptyChats: boolean; doesReportHaveViolations: boolean; @@ -9525,7 +9524,6 @@ function reasonForReportToBeInOptionList({ report, chatReport, currentReportId, - isInFocusMode, betas, excludeEmptyChats, doesReportHaveViolations, @@ -9536,8 +9534,6 @@ function reasonForReportToBeInOptionList({ isReportArchived, requiresAttention, }: ShouldReportBeInOptionListParams): ValueOf | null { - const isInDefaultMode = !isInFocusMode; - // Include the currently viewed report. If we excluded the currently viewed report, then there // would be no way to highlight it in the options list and it would be confusing to users because they lose // a sense of context. @@ -9656,17 +9652,8 @@ function reasonForReportToBeInOptionList({ return CONST.REPORT_IN_LHN_REASONS.HAS_ADD_WORKSPACE_ROOM_ERRORS; } - // All unread chats (even archived ones) in GSD mode will be shown. This is because GSD mode is specifically for focusing the user on the most relevant chats, primarily, the unread ones - if (isInFocusMode) { - const oneTransactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, currentReportActions); - const oneTransactionThreadReport = deprecatedAllReports?.[`${ONYXKEYS.COLLECTION.REPORT}${oneTransactionThreadReportID}`]; - return isUnread(report, oneTransactionThreadReport, isReportArchived) && getReportNotificationPreference(report) !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE - ? CONST.REPORT_IN_LHN_REASONS.IS_UNREAD - : null; - } - - // Archived reports should always be shown when in default (most recent) mode. This is because you should still be able to access and search for the chats to find them. - if (isInDefaultMode && isArchivedNonExpenseReport(report, isReportArchived)) { + // Archived reports should always be shown so users can still access and search for them. + if (isArchivedNonExpenseReport(report, isReportArchived)) { return CONST.REPORT_IN_LHN_REASONS.IS_ARCHIVED; } diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index f85b14ff3336..623caf8ad8d8 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -21,7 +21,6 @@ import type Beta from '@src/types/onyx/Beta'; import type {ReportAttributes} from '@src/types/onyx/DerivedValues'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import type Policy from '@src/types/onyx/Policy'; -import type PriorityMode from '@src/types/onyx/PriorityMode'; import type Report from '@src/types/onyx/Report'; import type ReportAction from '@src/types/onyx/ReportAction'; import {formatPhoneNumber as formatPhoneNumberPhoneUtils} from './LocalePhoneNumber'; @@ -169,6 +168,7 @@ import { isChatThread, isConciergeChatReport, isDeprecatedGroupDM, + isDM, isDomainRoom, isExpenseReport, isExpenseRequest, @@ -177,6 +177,7 @@ import { isInvoiceReport, isInvoiceRoom, isIOUOwnedByCurrentUser, + isIOUReport, isJoinRequestInAdminRoom, isMoneyRequestReport, isOneOnOneChat, @@ -264,7 +265,6 @@ function shouldDisplayReportInLHN( report: Report, reports: OnyxCollection, currentReportId: string | undefined, - isInFocusMode: boolean, betas: OnyxEntry, transactionViolations: OnyxCollection, draftComment: OnyxEntry, @@ -320,7 +320,6 @@ function shouldDisplayReportInLHN( report, chatReport, currentReportId, - isInFocusMode, betas, excludeEmptyChats: true, doesReportHaveViolations, @@ -337,14 +336,12 @@ function getReportsToDisplayInLHN( currentReportId: string | undefined, reports: OnyxCollection, betas: OnyxEntry, - priorityMode: OnyxEntry, draftComments: OnyxCollection, transactionViolations: OnyxCollection, transactions: OnyxCollection, reportNameValuePairs?: OnyxCollection, reportAttributes?: ReportAttributesDerivedValue['reports'], ) { - const isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD; const allReportsDictValues = reports ?? {}; const reportsToDisplay: ReportsToDisplayInLHN = {}; @@ -359,7 +356,6 @@ function getReportsToDisplayInLHN( report, reports, currentReportId, - isInFocusMode, betas, transactionViolations, reportDraftComment, @@ -383,7 +379,6 @@ type UpdateReportsToDisplayInLHNProps = { reports: OnyxCollection; updatedReportsKeys: string[]; currentReportId: string | undefined; - isInFocusMode: boolean; betas: OnyxEntry; transactionViolations: OnyxCollection; reportNameValuePairs?: OnyxCollection; @@ -397,7 +392,6 @@ function updateReportsToDisplayInLHN({ reports, updatedReportsKeys, currentReportId, - isInFocusMode, betas, transactionViolations, reportNameValuePairs, @@ -431,7 +425,6 @@ function updateReportsToDisplayInLHN({ report, reports, currentReportId, - isInFocusMode, betas, transactionViolations, reportDraftComment, @@ -626,24 +619,23 @@ function combineReportCategories( */ function sortReportsToDisplayInLHN( reportsToDisplay: ReportsToDisplayInLHN, - priorityMode: OnyxEntry, + useAlphabeticalSort: boolean, localeCompare: LocaleContextProps['localeCompare'], reportsDrafts: Record | undefined, reportNameValuePairs: OnyxCollection | undefined, reportAttributes: ReportAttributesDerivedValue['reports'] | undefined, ): string[] { - const isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD; - const isInDefaultMode = !isInFocusMode; + const isInDefaultMode = !useAlphabeticalSort; // The LHN is split into five distinct groups, and each group is sorted a little differently. The groups will ALWAYS be in this order: // 1. Pinned/GBR - Always sorted by reportDisplayName // 2. Error reports - Always sorted by reportDisplayName // 3. Drafts - Always sorted by reportDisplayName // 4. Non-archived reports and settled IOUs - // - Sorted by lastVisibleActionCreated in default (most recent) view mode - // - Sorted by reportDisplayName in GSD (focus) view mode + // - Sorted by lastVisibleActionCreated when not using alphabetical sort + // - Sorted by reportDisplayName when using alphabetical sort // 5. Archived reports - // - Sorted by lastVisibleActionCreated in default (most recent) view mode - // - Sorted by reportDisplayName in GSD (focus) view mode + // - Sorted by lastVisibleActionCreated when not using alphabetical sort + // - Sorted by reportDisplayName when using alphabetical sort // Step 1: Categorize reports const categories = categorizeReportsForLHN(reportsToDisplay, reportsDrafts, reportAttributes, reportNameValuePairs); @@ -1416,6 +1408,40 @@ function getRoomWelcomeMessage( return welcomeMessage; } +function filterReportsForInboxTab( + reportIDs: string[], + reportsToDisplay: ReportsToDisplayInLHN, + activeTab: ValueOf, + reportsDrafts?: Record, + reportNameValuePairs?: OnyxCollection, +): string[] { + if (activeTab === CONST.INBOX_TAB.ALL) { + return reportIDs; + } + + return reportIDs.filter((reportID) => { + const report = reportsToDisplay[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; + if (!report) { + return false; + } + + switch (activeTab) { + case CONST.INBOX_TAB.UNREADS: { + const isReportArchived = isArchivedReport(reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`]); + const isReportUnread = isUnread(report, undefined, isReportArchived) && getReportNotificationPreference(report) !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE; + const hasDraft = !!reportsDrafts?.[reportID]; + return isReportUnread || !!report.isPinned || !!report.requiresAttention || !!report.hasErrorsOtherThanFailedReceipt || hasDraft; + } + case CONST.INBOX_TAB.EXPENSES: + return isExpenseReport(report) || isIOUReport(report) || isInvoiceReport(report) || isPolicyExpenseChat(report); + case CONST.INBOX_TAB.DIRECT_MESSAGES: + return isDM(report) || isSelfDM(report) || isGroupChatUtil(report); + default: + return true; + } + }); +} + // Exported for unit testing only. Do not use directly in production code. export { categorizeReportsForLHN as _categorizeReportsForLHN, @@ -1427,6 +1453,7 @@ export { export default { getOptionData, sortReportsToDisplayInLHN, + filterReportsForInboxTab, getWelcomeMessage, getReasonAndReportActionThatHasRedBrickRoad, getReportsToDisplayInLHN, diff --git a/src/libs/UnreadIndicatorUpdater/index.ts b/src/libs/UnreadIndicatorUpdater/index.ts index 272620e70b58..b3066332fa2f 100644 --- a/src/libs/UnreadIndicatorUpdater/index.ts +++ b/src/libs/UnreadIndicatorUpdater/index.ts @@ -58,7 +58,6 @@ function getUnreadReportsForUnreadIndicator(reports: OnyxCollection, cur currentReportId: currentReportID, betas: [], doesReportHaveViolations: false, - isInFocusMode: false, excludeEmptyChats: false, isReportArchived, draftComment, diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts index 82a90babd548..2cc411ee3d2f 100644 --- a/src/libs/actions/App.ts +++ b/src/libs/actions/App.ts @@ -128,7 +128,6 @@ const KEYS_TO_PRESERVE: OnyxKey[] = [ ONYXKEYS.NETWORK, ONYXKEYS.SESSION, ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, - ONYXKEYS.NVP_TRY_FOCUS_MODE, ONYXKEYS.PREFERRED_THEME, ONYXKEYS.NVP_PREFERRED_LOCALE, ONYXKEYS.CREDENTIALS, diff --git a/src/libs/actions/Delegate.ts b/src/libs/actions/Delegate.ts index a31097d7e3a7..bea82b9297c4 100644 --- a/src/libs/actions/Delegate.ts +++ b/src/libs/actions/Delegate.ts @@ -21,7 +21,6 @@ import updateSessionAuthTokens from './Session/updateSessionAuthTokens'; import updateSessionUser from './Session/updateSessionUser'; const KEYS_TO_PRESERVE_DELEGATE_ACCESS = [ - ONYXKEYS.NVP_TRY_FOCUS_MODE, ONYXKEYS.PREFERRED_THEME, ONYXKEYS.NVP_PREFERRED_LOCALE, ONYXKEYS.RAM_ONLY_ARE_TRANSLATIONS_LOADING, diff --git a/src/libs/actions/QueuedOnyxUpdates.ts b/src/libs/actions/QueuedOnyxUpdates.ts index 62ce86816243..32ff59170cc7 100644 --- a/src/libs/actions/QueuedOnyxUpdates.ts +++ b/src/libs/actions/QueuedOnyxUpdates.ts @@ -35,7 +35,6 @@ function flushQueue(): Promise { if (!currentAccountID && !CONFIG.IS_TEST_ENV) { const preservedKeys = new Set([ ONYXKEYS.NVP_TRY_NEW_DOT, - ONYXKEYS.NVP_TRY_FOCUS_MODE, ONYXKEYS.PREFERRED_THEME, ONYXKEYS.NVP_PREFERRED_LOCALE, ONYXKEYS.RAM_ONLY_ARE_TRANSLATIONS_LOADING, diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 8047f230359c..c0f55b9817ab 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -296,7 +296,6 @@ function isExpiredSession(sessionCreationDate: number): boolean { } const KEYS_TO_PRESERVE_SUPPORTAL = [ - ONYXKEYS.NVP_TRY_FOCUS_MODE, ONYXKEYS.PREFERRED_THEME, ONYXKEYS.NVP_PREFERRED_LOCALE, ONYXKEYS.RAM_ONLY_ARE_TRANSLATIONS_LOADING, diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index b1fce73ddde6..138d02d821d5 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -18,7 +18,6 @@ import type { SetContactMethodAsDefaultParams, SetNameValuePairParams, TogglePlatformMuteParams, - UpdateChatPriorityModeParams, UpdateNewsletterSubscriptionParams, UpdatePreferredEmojiSkinToneParams, UpdateStatusParams, @@ -950,37 +949,8 @@ function updatePreferredSkinTone(skinTone: number) { API.write(WRITE_COMMANDS.UPDATE_PREFERRED_EMOJI_SKIN_TONE, parameters, {optimisticData}); } -/** - * Sync user chat priority mode with Onyx and Server - * @param mode - * @param [automatic] if we changed the mode automatically - */ -function updateChatPriorityMode(mode: ValueOf, automatic = false) { - const autoSwitchedToFocusMode = mode === CONST.PRIORITY_MODE.GSD && automatic; - const optimisticData: Array> = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.NVP_PRIORITY_MODE, - value: mode, - }, - ]; - - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.NVP_TRY_FOCUS_MODE, - value: true, - }); - - const parameters: UpdateChatPriorityModeParams = { - value: mode, - automatic, - }; - - API.write(WRITE_COMMANDS.UPDATE_CHAT_PRIORITY_MODE, parameters, {optimisticData}); - - if (!autoSwitchedToFocusMode) { - Navigation.goBack(); - } +function setInboxTab(tab: ValueOf) { + Onyx.merge(ONYXKEYS.NVP_INBOX_TAB, tab); } function setShouldUseStagingServer(shouldUseStagingServer: boolean) { @@ -1907,12 +1877,12 @@ export { isBlockedFromConcierge, subscribeToUserEvents, updatePreferredSkinTone, + setInboxTab, setShouldUseStagingServer, togglePlatformMute, joinScreenShare, clearScreenShareRequest, generateStatementPDF, - updateChatPriorityMode, setContactMethodAsDefault, updateTheme, resetContactMethodValidateCodeSentState, diff --git a/src/pages/Debug/Report/DebugReportPage.tsx b/src/pages/Debug/Report/DebugReportPage.tsx index 1510b45cfddc..de203b55cc32 100644 --- a/src/pages/Debug/Report/DebugReportPage.tsx +++ b/src/pages/Debug/Report/DebugReportPage.tsx @@ -69,7 +69,6 @@ function DebugReportPage({ [reportAttributesSelector], ); const [draftComment] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`); - const [priorityMode] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE); const [betas] = useOnyx(ONYXKEYS.BETAS); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); @@ -106,7 +105,6 @@ function DebugReportPage({ doesReportHaveViolations: shouldDisplayViolations, hasRBR, isReportArchived, - isInFocusMode: priorityMode === CONST.PRIORITY_MODE.GSD, draftComment, }); @@ -153,7 +151,7 @@ function DebugReportPage({ : undefined, }, ]; - }, [report, transactionViolations, isReportArchived, chatReport, reportActions, transactions, reportAttributes?.reportErrors, betas, priorityMode, draftComment, translate]); + }, [report, transactionViolations, isReportArchived, chatReport, reportActions, transactions, reportAttributes?.reportErrors, betas, draftComment, translate]); const icons = useMemoizedLazyExpensifyIcons(['Eye']); diff --git a/src/pages/inbox/sidebar/BaseSidebarScreen.tsx b/src/pages/inbox/sidebar/BaseSidebarScreen.tsx index f20aa890543e..fb4a748e7460 100644 --- a/src/pages/inbox/sidebar/BaseSidebarScreen.tsx +++ b/src/pages/inbox/sidebar/BaseSidebarScreen.tsx @@ -14,6 +14,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {isMobile} from '@libs/Browser'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import ONYXKEYS from '@src/ONYXKEYS'; +import InboxTabSelector from './InboxTabSelector'; import SidebarLinksData from './SidebarLinksData'; // Once the app finishes loading for the first time, we never show the skeleton again @@ -59,6 +60,7 @@ function BaseSidebarScreen() { shouldDisplaySearch={shouldUseNarrowLayout} shouldDisplayHelpButton={shouldUseNarrowLayout} /> + {!shouldShowSkeleton && } {shouldShowSkeleton ? ( + + + ); +} + +InboxTabSelector.displayName = 'InboxTabSelector'; + +export default InboxTabSelector; diff --git a/src/pages/inbox/sidebar/SidebarLinks.tsx b/src/pages/inbox/sidebar/SidebarLinks.tsx index 6793096bc0fe..38d7d019de41 100644 --- a/src/pages/inbox/sidebar/SidebarLinks.tsx +++ b/src/pages/inbox/sidebar/SidebarLinks.tsx @@ -1,8 +1,6 @@ import React, {memo, useCallback, useEffect, useMemo} from 'react'; import {StyleSheet, View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import type {EdgeInsets} from 'react-native-safe-area-context'; -import type {ValueOf} from 'type-fest'; import LHNEmptyState from '@components/LHNOptionsList/LHNEmptyState'; import LHNOptionsList from '@components/LHNOptionsList/LHNOptionsList'; import OptionsListSkeletonView from '@components/OptionsListSkeletonView'; @@ -28,14 +26,11 @@ type SidebarLinksProps = { /** List of options to display */ optionListItems: Report[]; - /** The chat priority mode */ - priorityMode?: OnyxEntry>; - /** Method to change currently active report */ isActiveReport: (reportID: string) => boolean; }; -function SidebarLinks({insets, optionListItems, priorityMode = CONST.PRIORITY_MODE.DEFAULT, isActiveReport}: SidebarLinksProps) { +function SidebarLinks({insets, optionListItems, isActiveReport}: SidebarLinksProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {shouldUseNarrowLayout} = useResponsiveLayout(); @@ -74,8 +69,6 @@ function SidebarLinks({insets, optionListItems, priorityMode = CONST.PRIORITY_MO [shouldUseNarrowLayout, isActiveReport], ); - const viewMode = priorityMode === CONST.PRIORITY_MODE.GSD ? CONST.OPTION_MODE.COMPACT : CONST.OPTION_MODE.DEFAULT; - const sidebarSkeletonReasonAttributes: SkeletonSpanReasonAttributes = { context: 'SidebarLinks', isLoadingReportData, @@ -101,7 +94,7 @@ function SidebarLinks({insets, optionListItems, priorityMode = CONST.PRIORITY_MO data={optionListItems} onSelectRow={showReportPage} shouldDisableFocusOptions={shouldUseNarrowLayout} - optionMode={viewMode} + optionMode={CONST.OPTION_MODE.DEFAULT} onFirstItemRendered={setSidebarLoaded} /> )} diff --git a/src/pages/inbox/sidebar/SidebarLinksData.tsx b/src/pages/inbox/sidebar/SidebarLinksData.tsx index 1c4879c15884..39e75e8a5396 100644 --- a/src/pages/inbox/sidebar/SidebarLinksData.tsx +++ b/src/pages/inbox/sidebar/SidebarLinksData.tsx @@ -4,12 +4,10 @@ import React, {useCallback, useEffect, useRef} from 'react'; import {View} from 'react-native'; import type {EdgeInsets} from 'react-native-safe-area-context'; import useLocalize from '@hooks/useLocalize'; -import useOnyx from '@hooks/useOnyx'; import {useSidebarOrderedReportsState} from '@hooks/useSidebarOrderedReports'; import useThemeStyles from '@hooks/useThemeStyles'; import {cancelSpan, endSpan, getSpan} from '@libs/telemetry/activeSpans'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; import SidebarLinks from './SidebarLinks'; type SidebarLinksDataProps = { @@ -21,7 +19,6 @@ function SidebarLinksData({insets}: SidebarLinksDataProps) { const isFocused = useIsFocused(); const styles = useThemeStyles(); const {translate} = useLocalize(); - const [priorityMode = CONST.PRIORITY_MODE.DEFAULT] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE); const {orderedReports, currentReportID} = useSidebarOrderedReportsState('SidebarLinksData'); @@ -75,10 +72,7 @@ function SidebarLinksData({insets}: SidebarLinksDataProps) { onLayout={onLayout} > diff --git a/src/pages/settings/Preferences/PreferencesPage.tsx b/src/pages/settings/Preferences/PreferencesPage.tsx index b3426c834830..28bdc3836966 100755 --- a/src/pages/settings/Preferences/PreferencesPage.tsx +++ b/src/pages/settings/Preferences/PreferencesPage.tsx @@ -32,8 +32,6 @@ function PreferencesPage() { const {getCurrencySymbol} = useCurrencyListActions(); const illustrations = useMemoizedLazyIllustrations(['Gears']); const preferencesIllustration = usePreferencesSectionIllustration(); - const [priorityMode] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE); - const platform = getPlatform(true); const [mutedPlatforms = getEmptyObject>>()] = useOnyx(ONYXKEYS.NVP_MUTED_PLATFORMS); const isPlatformMuted = mutedPlatforms[platform]; @@ -113,14 +111,6 @@ function PreferencesPage() { /> - Navigation.navigate(ROUTES.SETTINGS_PRIORITY_MODE)} - wrapperStyle={styles.sectionMenuItemTopDescription} - sentryLabel={CONST.SENTRY_LABEL.SETTINGS_PREFERENCES.PRIORITY_MODE} - /> ; - text: string; - alternateText: string; - keyForList: ValueOf; - isSelected: boolean; -}; - -function PriorityModePage() { - const {translate} = useLocalize(); - const [priorityMode = CONST.PRIORITY_MODE.DEFAULT] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE); - const styles = useThemeStyles(); - const priorityModes = Object.values(CONST.PRIORITY_MODE).map((mode) => ({ - value: mode, - text: translate(`priorityModePage.priorityModes.${mode}.label`), - alternateText: translate(`priorityModePage.priorityModes.${mode}.description`), - keyForList: mode, - isSelected: priorityMode === mode, - })); - - const updateMode = useCallback( - (mode: PriorityModeItem) => { - if (mode.value === priorityMode) { - Navigation.goBack(); - return; - } - updateChatPriorityMode(mode.value); - }, - [priorityMode], - ); - - return ( - - Navigation.goBack()} - /> - {translate('priorityModePage.explainerText')} - mode.isSelected)?.keyForList} - /> - - ); -} - -export default PriorityModePage; diff --git a/src/styles/index.ts b/src/styles/index.ts index 71ec72fea456..eae83119cc2f 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4359,6 +4359,11 @@ const staticStyles = (theme: ThemeColors) => scrollMarginInline: variables.tabSelectorScrollMarginInline, }, + tabSelectorButtonSmall: { + height: variables.componentSizeSmall, + paddingHorizontal: 12, + }, + tabSelector: { flexDirection: 'row', paddingHorizontal: 20, @@ -4371,6 +4376,11 @@ const staticStyles = (theme: ThemeColors) => paddingHorizontal: 20, }, + tabSelectorContentContainerSmall: { + paddingTop: 4, + paddingBottom: 4, + }, + scrollableTabSelector: { flexGrow: 0, }, @@ -6209,6 +6219,11 @@ const dynamicStyles = (theme: ThemeColors) => fontSize: variables.fontSizeLabel, }) satisfies TextStyle, + tabTextSmall: { + fontSize: variables.fontSizeSmall, + lineHeight: 16, + } satisfies TextStyle, + tabBackground: (hovered: boolean, isFocused: boolean, isDisabled: boolean, background: string | Animated.AnimatedInterpolation) => { if (isDisabled) { return {backgroundColor: undefined}; diff --git a/src/types/onyx/InboxTab.ts b/src/types/onyx/InboxTab.ts new file mode 100644 index 000000000000..616f5b89f764 --- /dev/null +++ b/src/types/onyx/InboxTab.ts @@ -0,0 +1,6 @@ +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; + +type InboxTab = ValueOf; + +export default InboxTab; diff --git a/src/types/onyx/PriorityMode.ts b/src/types/onyx/PriorityMode.ts deleted file mode 100644 index 404c678945ad..000000000000 --- a/src/types/onyx/PriorityMode.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type {OnyxEntry} from 'react-native-onyx'; -import type {ValueOf} from 'type-fest'; -import type CONST from '@src/CONST'; - -/** Modes that define how the user's chats are displayed in his chat list */ -type PriorityMode = OnyxEntry>; - -export default PriorityMode; diff --git a/tests/actions/QueuedOnyxUpdatesTest.ts b/tests/actions/QueuedOnyxUpdatesTest.ts index 5a7190f42945..10c0b93c3f7b 100644 --- a/tests/actions/QueuedOnyxUpdatesTest.ts +++ b/tests/actions/QueuedOnyxUpdatesTest.ts @@ -100,7 +100,6 @@ describe('actions/QueuedOnyxUpdates', () => { await flushQueue(); - await testOnyxKeyValue(ONYXKEYS.NVP_TRY_FOCUS_MODE); await testOnyxKeyValue(ONYXKEYS.PREFERRED_THEME); await testOnyxKeyValue(ONYXKEYS.NVP_PREFERRED_LOCALE); await testOnyxKeyValue(ONYXKEYS.SESSION); @@ -145,7 +144,6 @@ describe('actions/QueuedOnyxUpdates', () => { await flushQueue(); - await testOnyxKeyValue(ONYXKEYS.NVP_TRY_FOCUS_MODE); await testOnyxKeyValue(ONYXKEYS.PREFERRED_THEME); await testOnyxKeyValue(ONYXKEYS.NVP_PREFERRED_LOCALE); await testOnyxKeyValue(ONYXKEYS.SESSION); diff --git a/tests/perf-test/SidebarLinks.perf-test.tsx b/tests/perf-test/SidebarLinks.perf-test.tsx index 34c7b84e2b6b..f26496ca27e3 100644 --- a/tests/perf-test/SidebarLinks.perf-test.tsx +++ b/tests/perf-test/SidebarLinks.perf-test.tsx @@ -80,7 +80,7 @@ describe('SidebarLinks', () => { await Onyx.multiSet({ [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS], - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, + [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, ...mockedResponseMap, }); @@ -102,7 +102,7 @@ describe('SidebarLinks', () => { await Onyx.multiSet({ [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS], - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, + [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, ...mockedResponseMap, }); diff --git a/tests/ui/LHNItemsPresence.tsx b/tests/ui/LHNItemsPresence.tsx index a6aa9fd36dbb..2241f4654d7a 100644 --- a/tests/ui/LHNItemsPresence.tsx +++ b/tests/ui/LHNItemsPresence.tsx @@ -138,7 +138,6 @@ describe('SidebarLinksData', () => { await waitForBatchedUpdates(); await act(async () => { await Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -396,9 +395,7 @@ describe('SidebarLinksData', () => { await waitForBatchedUpdatesWithAct(); - // When the user is in the default mode await act(async () => { - await Onyx.merge(ONYXKEYS.NVP_PRIORITY_MODE, CONST.PRIORITY_MODE.DEFAULT); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${archivedReport.reportID}`, reportNameValuePairs); }); @@ -440,12 +437,6 @@ describe('SidebarLinksData', () => { await waitForBatchedUpdatesWithAct(); // When the user is in focus mode - await act(async () => { - await Onyx.merge(ONYXKEYS.NVP_PRIORITY_MODE, CONST.PRIORITY_MODE.GSD); - }); - - await waitForBatchedUpdatesWithAct(); - // Then the report should appear in the sidebar because it's unread expect(getOptionRows()).toHaveLength(1); @@ -704,25 +695,8 @@ describe('SidebarLinksData', () => { await waitForBatchedUpdatesWithAct(); - // And the user is in default mode - await act(async () => { - await Onyx.merge(ONYXKEYS.NVP_PRIORITY_MODE, CONST.PRIORITY_MODE.DEFAULT); - }); - - await waitForBatchedUpdatesWithAct(); - // Then the report should appear in the sidebar expect(getOptionRows()).toHaveLength(1); - - await act(async () => { - // When the user is in focus mode - await Onyx.merge(ONYXKEYS.NVP_PRIORITY_MODE, CONST.PRIORITY_MODE.GSD); - }); - - await waitForBatchedUpdatesWithAct(); - - // Then the report should not disappear in the sidebar because it's read - expect(getOptionRows()).toHaveLength(0); }); it('should not display an empty submitted report having only a CREATED action', async () => { diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index fce1994b920e..847038475834 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -5881,14 +5881,12 @@ describe('ReportUtils', () => { it('should return true when the report is current active report', () => { const report = LHNTestUtils.getFakeReport(); const currentReportId = report.reportID; - const isInFocusMode = true; const betas = [CONST.BETAS.DEFAULT_ROOMS]; expect( shouldReportBeInOptionList({ report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -5901,8 +5899,6 @@ describe('ReportUtils', () => { it('should return true for empty submitted report if it is the current focused report', async () => { const report: Report = {...LHNTestUtils.getFakeReport(), total: 0, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED}; const currentReportId = report.reportID; - - const isInFocusMode = true; const betas = [CONST.BETAS.DEFAULT_ROOMS]; const createdReportAction: ReportAction = {...LHNTestUtils.getFakeReportAction(), actionName: CONST.REPORT.ACTIONS.TYPE.CREATED}; await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, {[createdReportAction.reportActionID]: createdReportAction}); @@ -5912,7 +5908,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -5930,8 +5925,6 @@ describe('ReportUtils', () => { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, }; const currentReportId = `${report.reportID}1`; - - const isInFocusMode = true; const betas = [CONST.BETAS.DEFAULT_ROOMS]; const createdReportAction: ReportAction = {...LHNTestUtils.getFakeReportAction(), actionName: CONST.REPORT.ACTIONS.TYPE.CREATED}; await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, {[createdReportAction.reportActionID]: createdReportAction}); @@ -5941,7 +5934,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -5982,7 +5974,6 @@ describe('ReportUtils', () => { }); const transactionThreadReport = buildTransactionThread(expenseCreatedAction1, expenseReport); const currentReportId = '1'; - const isInFocusMode = false; const betas = [CONST.BETAS.DEFAULT_ROOMS]; await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, expenseReport); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, { @@ -5994,7 +5985,6 @@ describe('ReportUtils', () => { report: transactionThreadReport, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: true, excludeEmptyChats: false, @@ -6010,14 +6000,12 @@ describe('ReportUtils', () => { hasOutstandingChildRequest: true, }; const currentReportId = '3'; - const isInFocusMode = true; const betas = [CONST.BETAS.DEFAULT_ROOMS]; expect( shouldReportBeInOptionList({ report: chatReport, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6030,7 +6018,6 @@ describe('ReportUtils', () => { it('should return true when the report has valid draft comment', async () => { const report = LHNTestUtils.getFakeReport(); const currentReportId = '3'; - const isInFocusMode = false; const betas = [CONST.BETAS.DEFAULT_ROOMS]; await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report.reportID}`, 'fake draft'); @@ -6040,7 +6027,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6056,14 +6042,12 @@ describe('ReportUtils', () => { isPinned: true, }; const currentReportId = '3'; - const isInFocusMode = false; const betas = [CONST.BETAS.DEFAULT_ROOMS]; expect( shouldReportBeInOptionList({ report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6087,7 +6071,6 @@ describe('ReportUtils', () => { lastMessageText: 'fake', }; const currentReportId = '3'; - const isInFocusMode = true; const betas = [CONST.BETAS.DEFAULT_ROOMS]; await Onyx.merge(ONYXKEYS.SESSION, { @@ -6099,7 +6082,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6119,7 +6101,6 @@ describe('ReportUtils', () => { private_isArchived: DateUtils.getDBTime(), }; const currentReportId = '3'; - const isInFocusMode = false; const betas = [CONST.BETAS.DEFAULT_ROOMS]; await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${archivedReport.reportID}`, reportNameValuePairs); @@ -6130,7 +6111,6 @@ describe('ReportUtils', () => { report: archivedReport, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6150,7 +6130,6 @@ describe('ReportUtils', () => { private_isArchived: DateUtils.getDBTime(), }; const currentReportId = '3'; - const isInFocusMode = true; const betas = [CONST.BETAS.DEFAULT_ROOMS]; await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${archivedReport.reportID}`, reportNameValuePairs); @@ -6161,7 +6140,6 @@ describe('ReportUtils', () => { report: archivedReport, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6177,7 +6155,6 @@ describe('ReportUtils', () => { chatType: CONST.REPORT.CHAT_TYPE.SELF_DM, }; const currentReportId = '3'; - const isInFocusMode = false; const betas = [CONST.BETAS.DEFAULT_ROOMS]; const includeSelfDM = true; expect( @@ -6185,7 +6162,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6206,14 +6182,12 @@ describe('ReportUtils', () => { }, }; const currentReportId = ''; - const isInFocusMode = true; const betas = [CONST.BETAS.DEFAULT_ROOMS]; expect( shouldReportBeInOptionList({ report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6226,14 +6200,12 @@ describe('ReportUtils', () => { it('should return false when the report does not have participants', () => { const report = LHNTestUtils.getFakeReport([]); const currentReportId = ''; - const isInFocusMode = true; const betas = [CONST.BETAS.DEFAULT_ROOMS]; expect( shouldReportBeInOptionList({ report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6249,14 +6221,12 @@ describe('ReportUtils', () => { chatType: CONST.REPORT.CHAT_TYPE.DOMAIN_ALL, }; const currentReportId = ''; - const isInFocusMode = false; const betas: Beta[] = []; expect( shouldReportBeInOptionList({ report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6288,7 +6258,6 @@ describe('ReportUtils', () => { const transactionThreadReport = buildTransactionThread(expenseCreatedAction, expenseReport); expenseCreatedAction.childReportID = transactionThreadReport.reportID; const currentReportId = '1'; - const isInFocusMode = false; const betas = [CONST.BETAS.DEFAULT_ROOMS]; await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${expenseReport.reportID}`, expenseReport); await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseReport.reportID}`, { @@ -6299,7 +6268,6 @@ describe('ReportUtils', () => { report: transactionThreadReport, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6312,14 +6280,12 @@ describe('ReportUtils', () => { it('should return false when the report is empty chat and the excludeEmptyChats setting is true', () => { const report = LHNTestUtils.getFakeReport(); const currentReportId = ''; - const isInFocusMode = false; const betas = [CONST.BETAS.DEFAULT_ROOMS]; expect( shouldReportBeInOptionList({ report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: true, @@ -6336,7 +6302,6 @@ describe('ReportUtils', () => { reportID: conciergeReportID, }; const currentReportId = ''; - const isInFocusMode = false; const betas = [CONST.BETAS.DEFAULT_ROOMS]; await Onyx.set(ONYXKEYS.CONCIERGE_REPORT_ID, conciergeReportID); @@ -6347,7 +6312,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: true, @@ -6360,14 +6324,12 @@ describe('ReportUtils', () => { it('should return false when the users email is domain-based and the includeDomainEmail is false', () => { const report = LHNTestUtils.getFakeReport(); const currentReportId = ''; - const isInFocusMode = false; const betas = [CONST.BETAS.DEFAULT_ROOMS]; expect( shouldReportBeInOptionList({ report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, login: '+@domain.com', @@ -6402,7 +6364,6 @@ describe('ReportUtils', () => { report.parentReportID = parentReport.reportID; report.parentReportActionID = parentReportAction.reportActionID; const currentReportId = ''; - const isInFocusMode = false; const betas = [CONST.BETAS.DEFAULT_ROOMS]; await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${parentReport.reportID}`, parentReport); @@ -6415,7 +6376,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6428,14 +6388,12 @@ describe('ReportUtils', () => { it('should return false when the report is read and we are in the focus mode', () => { const report = LHNTestUtils.getFakeReport(); const currentReportId = ''; - const isInFocusMode = true; const betas = [CONST.BETAS.DEFAULT_ROOMS]; expect( shouldReportBeInOptionList({ report, chatReport: mockedChatReport, currentReportId, - isInFocusMode, betas, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6469,7 +6427,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId: '', - isInFocusMode: false, betas: [], doesReportHaveViolations: false, excludeEmptyChats: true, @@ -6500,7 +6457,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId: '', - isInFocusMode: true, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6529,7 +6485,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId: '', - isInFocusMode: true, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6551,7 +6506,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6595,7 +6549,6 @@ describe('ReportUtils', () => { report: threadReport, chatReport: mockedChatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6646,7 +6599,6 @@ describe('ReportUtils', () => { report: threadReport, chatReport: mockedChatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6667,7 +6619,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6689,7 +6640,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: true, @@ -6710,7 +6660,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6731,7 +6680,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId: '999', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6752,7 +6700,6 @@ describe('ReportUtils', () => { report, chatReport: mockedChatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6768,7 +6715,6 @@ describe('ReportUtils', () => { report: null as unknown as Report, chatReport: mockedChatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -6824,7 +6770,6 @@ describe('ReportUtils', () => { report: transactionThreadReport, chatReport: mockedChatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: true, excludeEmptyChats: false, @@ -11986,7 +11931,6 @@ describe('ReportUtils', () => { report: chatReport, chatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -12044,7 +11988,6 @@ describe('ReportUtils', () => { report: chatReport, chatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -12151,7 +12094,6 @@ describe('ReportUtils', () => { report: expenseReport, chatReport: undefined, currentReportId: undefined, - isInFocusMode: true, betas: undefined, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -12194,7 +12136,6 @@ describe('ReportUtils', () => { report: expenseReport, chatReport: undefined, currentReportId: undefined, - isInFocusMode: true, betas: undefined, doesReportHaveViolations: false, excludeEmptyChats: false, @@ -12545,7 +12486,6 @@ describe('ReportUtils', () => { report: chatReport, chatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: shouldShowRBR, excludeEmptyChats: false, @@ -12728,7 +12668,6 @@ describe('ReportUtils', () => { report: chatReport, chatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -12913,7 +12852,6 @@ describe('ReportUtils', () => { report: chatReport, chatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -12935,7 +12873,6 @@ describe('ReportUtils', () => { report: chatReport, chatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -13036,7 +12973,6 @@ describe('ReportUtils', () => { report: expenseReportChat, chatReport: expenseReportChat, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -13076,7 +13012,6 @@ describe('ReportUtils', () => { report: expenseReportChat, chatReport: expenseReportChat, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -13552,7 +13487,6 @@ describe('ReportUtils', () => { report: chatReport, chatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -13619,7 +13553,6 @@ describe('ReportUtils', () => { report: chatReport, chatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -13690,7 +13623,6 @@ describe('ReportUtils', () => { report: chatReport, chatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -13760,7 +13692,6 @@ describe('ReportUtils', () => { report: chatReport, chatReport, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -15525,7 +15456,6 @@ describe('ReportUtils', () => { report: policyExpenseChat, chatReport: policyExpenseChat, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, @@ -15594,7 +15524,6 @@ describe('ReportUtils', () => { report: policyExpenseChat, chatReport: policyExpenseChat, currentReportId: '', - isInFocusMode: false, betas: [CONST.BETAS.DEFAULT_ROOMS], doesReportHaveViolations: false, excludeEmptyChats: false, diff --git a/tests/unit/SidebarFilterTest.ts b/tests/unit/SidebarFilterTest.ts index 1970472dfdc3..c377ef0c6e25 100644 --- a/tests/unit/SidebarFilterTest.ts +++ b/tests/unit/SidebarFilterTest.ts @@ -17,7 +17,6 @@ jest.mock('@libs/Permissions'); const ONYXKEYS = { PERSONAL_DETAILS_LIST: 'personalDetailsList', IS_LOADING_APP: 'isLoadingApp', - NVP_PRIORITY_MODE: 'nvp_priorityMode', SESSION: 'session', BETAS: 'betas', COLLECTION: { @@ -372,7 +371,6 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -421,7 +419,6 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, @@ -492,7 +489,6 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${draftReport.reportID}`]: 'draft report message', @@ -546,7 +542,6 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -612,7 +607,6 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -714,7 +708,6 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -769,7 +762,6 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -822,7 +814,6 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -873,7 +864,6 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -920,7 +910,6 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -956,7 +945,6 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, diff --git a/tests/unit/SidebarOrderTest.ts b/tests/unit/SidebarOrderTest.ts index 11975ca61b92..55a7cfbc6cbe 100644 --- a/tests/unit/SidebarOrderTest.ts +++ b/tests/unit/SidebarOrderTest.ts @@ -122,7 +122,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, @@ -160,7 +159,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, @@ -209,7 +207,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report1.reportID}`]: 'report1 draft', @@ -256,7 +253,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, @@ -319,7 +315,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, @@ -393,7 +388,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, @@ -472,7 +466,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, [`${ONYXKEYS.COLLECTION.POLICY}${fakeReport.policyID}`]: fakePolicy, @@ -523,7 +516,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT + report2.reportID]: 'This is a draft', @@ -570,7 +562,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report.reportID}`]: 'This is a draft', @@ -612,7 +603,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, @@ -696,7 +686,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report2.reportID}`]: 'Report2 draft comment', @@ -754,7 +743,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, @@ -816,7 +804,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportDraftCommentCollectionDataSet, @@ -888,7 +875,6 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportNameValuePairsCollectionDataSet, @@ -932,7 +918,6 @@ describe('Sidebar', () => { // When Onyx is updated with the data and the sidebar re-renders .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, @@ -973,7 +958,6 @@ describe('Sidebar', () => { // with all reports having unread comments .then(() => Onyx.multiSet({ - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, }), @@ -1035,7 +1019,6 @@ describe('Sidebar', () => { .then(() => Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportNameValuePairsCollectionDataSet, diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index a0fbc44f2995..54a42f4a4ecd 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -94,7 +94,7 @@ describe('Sidebar', () => { return act(async () => { await Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportNameValuePairsCollection, @@ -157,7 +157,7 @@ describe('Sidebar', () => { return act(async () => { await Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportNameValuePairsCollection, diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index 31acccfe67cf..e4a52b030dd0 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -3471,10 +3471,10 @@ describe('SidebarUtils', () => { ]); const mockLocaleCompare = (a: string, b: string) => a.localeCompare(b); - const priorityMode = CONST.PRIORITY_MODE.DEFAULT; + const useAlphabeticalSort = false; // When the reports are sorted - const result = SidebarUtils.sortReportsToDisplayInLHN(reports, priorityMode, mockLocaleCompare, undefined, undefined, undefined); + const result = SidebarUtils.sortReportsToDisplayInLHN(reports, useAlphabeticalSort, mockLocaleCompare, undefined, undefined, undefined); // Then the reports are sorted in the correct order expect(result).toEqual(['0', '1', '2']); // Pinned first, Error second, Normal third @@ -3499,11 +3499,11 @@ describe('SidebarUtils', () => { const mockLocaleCompare = (a: string, b: string) => a.localeCompare(b); - // When the reports are sorted in default mode - const defaultResult = SidebarUtils.sortReportsToDisplayInLHN(reports, CONST.PRIORITY_MODE.DEFAULT, mockLocaleCompare, undefined, undefined, undefined); + // When the reports are sorted in default mode (not alphabetical) + const defaultResult = SidebarUtils.sortReportsToDisplayInLHN(reports, false, mockLocaleCompare, undefined, undefined, undefined); - // When the reports are sorted in GSD mode - const gsdResult = SidebarUtils.sortReportsToDisplayInLHN(reports, CONST.PRIORITY_MODE.GSD, mockLocaleCompare, undefined, undefined, undefined); + // When the reports are sorted in alphabetical mode + const gsdResult = SidebarUtils.sortReportsToDisplayInLHN(reports, true, mockLocaleCompare, undefined, undefined, undefined); // Then the reports are sorted in the correct order expect(defaultResult).toEqual(['1', '0']); // Most recent first (index 1 has later date) @@ -3523,7 +3523,6 @@ describe('SidebarUtils', () => { reports, updatedReportsKeys: [`${ONYXKEYS.COLLECTION.REPORT}999`], currentReportId: '1', - isInFocusMode: false, betas: [], transactions: {}, transactionViolations: {}, @@ -3544,7 +3543,6 @@ describe('SidebarUtils', () => { reports, updatedReportsKeys: ['0'], currentReportId: undefined, - isInFocusMode: false, betas: [], transactions: {}, transactionViolations: {}, From 9af6b204a644f685ae4648f107a917a35497c736 Mon Sep 17 00:00:00 2001 From: Shawn Borton Date: Fri, 17 Apr 2026 15:55:47 +0200 Subject: [PATCH 2/7] Update tabs to All/To do/Expenses/DMs and add Unreads toggle Replace the Unread tab with a "To do" tab that shows items needing action (GBR/RBR indicators). Rename Direct Messages to DMs. Add a separate Unreads toggle in the Inbox header using a small 20px Switch component. The toggle filters to show only unread items and works across all tabs. State persists via NVP_INBOX_UNREAD_FILTER. - Add 'small' size variant to Switch component (36x20 track, 14x14 thumb) - Add switchTrackSmall/switchThumbSmall styles - Add InboxUnreadToggle component with "Unreads" label + small switch - Update filterReportsForInboxTab to apply unread filter orthogonally Co-Authored-By: Claude Opus 4.6 (1M context) --- src/CONST/index.ts | 4 +-- src/ONYXKEYS.ts | 4 +++ src/components/Switch.tsx | 22 ++++++++++---- src/hooks/useSidebarOrderedReports.tsx | 30 ++++++++++++------- src/languages/de.ts | 5 ++-- src/languages/en.ts | 5 ++-- src/languages/es.ts | 5 ++-- src/languages/fr.ts | 5 ++-- src/languages/it.ts | 5 ++-- src/languages/ja.ts | 5 ++-- src/languages/nl.ts | 5 ++-- src/languages/pl.ts | 5 ++-- src/languages/pt-BR.ts | 5 ++-- src/languages/zh-hans.ts | 5 ++-- src/libs/SidebarUtils.ts | 29 ++++++++++-------- src/libs/actions/User.ts | 5 ++++ src/pages/inbox/sidebar/BaseSidebarScreen.tsx | 5 +++- src/pages/inbox/sidebar/InboxTabSelector.tsx | 4 +-- src/pages/inbox/sidebar/InboxUnreadToggle.tsx | 30 +++++++++++++++++++ src/styles/index.ts | 14 +++++++++ 20 files changed, 144 insertions(+), 53 deletions(-) create mode 100644 src/pages/inbox/sidebar/InboxUnreadToggle.tsx diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 3fbca9aa9280..be0e4fd88728 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -2085,9 +2085,9 @@ const CONST = { }, INBOX_TAB: { ALL: 'all', - UNREADS: 'unreads', + TODO: 'todo', EXPENSES: 'expenses', - DIRECT_MESSAGES: 'directMessages', + DMS: 'dms', }, THEME: { DEFAULT: 'system', diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index b7da30c7c155..3dce345ee6e7 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -162,6 +162,9 @@ const ONYXKEYS = { /** Contains the user preference for the active inbox tab filter */ NVP_INBOX_TAB: 'nvp_inboxTab', + /** Whether to show only unread chats in the inbox */ + NVP_INBOX_UNREAD_FILTER: 'nvp_inboxUnreadFilter', + /** Contains the users's block expiration (if they have one) */ NVP_BLOCKED_FROM_CONCIERGE: 'nvp_private_blockedFromConcierge', @@ -1354,6 +1357,7 @@ type OnyxValuesMapping = { [ONYXKEYS.BETA_CONFIGURATION]: OnyxTypes.BetaConfiguration; [ONYXKEYS.NVP_MUTED_PLATFORMS]: Partial>; [ONYXKEYS.NVP_INBOX_TAB]: ValueOf; + [ONYXKEYS.NVP_INBOX_UNREAD_FILTER]: boolean; [ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE]: OnyxTypes.BlockedFromConcierge; [ONYXKEYS.QUEUE_FLUSHED_DATA]: AnyOnyxUpdate[]; [ONYXKEYS.TRANSACTIONS_PENDING_3DS_REVIEW]: OnyxTypes.TransactionsPending3DSReview; diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx index ed82f75422e4..0c888a2efa66 100644 --- a/src/components/Switch.tsx +++ b/src/components/Switch.tsx @@ -26,6 +26,9 @@ type SwitchProps = { /** Callback to fire when the switch is toggled in disabled state */ disabledAction?: () => void; + + /** Size variant. 'small' renders a compact 20px tall switch. */ + size?: 'default' | 'small'; }; const OFFSET_X = { @@ -33,14 +36,21 @@ const OFFSET_X = { ON: 20, }; -function Switch({isOn, onToggle, accessibilityLabel, disabled, showLockIcon, disabledAction}: SwitchProps) { +const OFFSET_X_SMALL = { + OFF: 0, + ON: 16, +}; + +function Switch({isOn, onToggle, accessibilityLabel, disabled, showLockIcon, disabledAction, size}: SwitchProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const offsetX = useSharedValue(isOn ? OFFSET_X.ON : OFFSET_X.OFF); + const isSmall = size === 'small'; + const offsets = isSmall ? OFFSET_X_SMALL : OFFSET_X; + const offsetX = useSharedValue(isOn ? offsets.ON : offsets.OFF); const theme = useTheme(); const expensifyIcons = useMemoizedLazyExpensifyIcons(['Lock']); - const targetOffsetX = isOn ? OFFSET_X.ON : OFFSET_X.OFF; + const targetOffsetX = isOn ? offsets.ON : offsets.OFF; const prevIsOn = useRef(isOn); const hasUserToggled = useRef(false); @@ -85,7 +95,7 @@ function Switch({isOn, onToggle, accessibilityLabel, disabled, showLockIcon, dis })); const animatedSwitchTrackStyle = useAnimatedStyle(() => ({ - backgroundColor: interpolateColor(offsetX.get(), [OFFSET_X.OFF, OFFSET_X.ON], [theme.icon, theme.success]), + backgroundColor: interpolateColor(offsetX.get(), [offsets.OFF, offsets.ON], [theme.icon, theme.success]), })); // Enhance accessibility label to include locked state when disabled @@ -109,8 +119,8 @@ function Switch({isOn, onToggle, accessibilityLabel, disabled, showLockIcon, dis pressDimmingValue={0.8} sentryLabel={enhancedAccessibilityLabel} > - - + + {(!!disabled || !!showLockIcon) && ( ; + showUnreadOnly: boolean; }; type SidebarOrderedReportsActionsContextValue = { clearLHNCache: () => void; setActiveTab: (tab: ValueOf) => void; + toggleUnreadFilter: () => void; }; type ReportsToDisplayInLHN = Record; @@ -51,11 +53,13 @@ const SidebarOrderedReportsStateContext = createContext({ clearLHNCache: () => {}, setActiveTab: () => {}, + toggleUnreadFilter: () => {}, }); const policyMapper = (policy: OnyxEntry): PartialPolicyForSidebar => @@ -81,6 +85,8 @@ function SidebarOrderedReportsContextProvider({ const {localeCompare} = useLocalize(); const [inboxTab = CONST.INBOX_TAB.ALL] = useOnyx(ONYXKEYS.NVP_INBOX_TAB); const activeTab = inboxTab ?? CONST.INBOX_TAB.ALL; + const [inboxUnreadFilter = false] = useOnyx(ONYXKEYS.NVP_INBOX_UNREAD_FILTER); + const showUnreadOnly = inboxUnreadFilter ?? false; const [chatReports, {sourceValue: reportUpdates}] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [policies, {sourceValue: policiesUpdates}] = useMappedPolicies(policyMapper); const [transactions, {sourceValue: transactionsUpdates}] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION); @@ -255,20 +261,18 @@ function SidebarOrderedReportsContextProvider({ setCurrentReportsToDisplay(reportsToDisplayInLHN); }, [reportsToDisplayInLHN]); - const useAlphabeticalSort = activeTab === CONST.INBOX_TAB.UNREADS; - const getOrderedReportIDs = useCallback( - () => SidebarUtils.sortReportsToDisplayInLHN(reportsToDisplayInLHN, useAlphabeticalSort, localeCompare, hasDraftByReportID, reportNameValuePairs, reportAttributes), + () => SidebarUtils.sortReportsToDisplayInLHN(reportsToDisplayInLHN, false, localeCompare, hasDraftByReportID, reportNameValuePairs, reportAttributes), // Rule disabled intentionally - reports should be sorted only when the reportsToDisplayInLHN changes // eslint-disable-next-line react-hooks/exhaustive-deps - [reportsToDisplayInLHN, useAlphabeticalSort, localeCompare, hasDraftByReportID, reportAttributes], + [reportsToDisplayInLHN, localeCompare, hasDraftByReportID, reportAttributes], ); const orderedReportIDs = useMemo(() => getOrderedReportIDs(), [getOrderedReportIDs]); const filteredReportIDs = useMemo( - () => SidebarUtils.filterReportsForInboxTab(orderedReportIDs, reportsToDisplayInLHN, activeTab, hasDraftByReportID, reportNameValuePairs), - [orderedReportIDs, reportsToDisplayInLHN, activeTab, hasDraftByReportID, reportNameValuePairs], + () => SidebarUtils.filterReportsForInboxTab(orderedReportIDs, reportsToDisplayInLHN, activeTab, showUnreadOnly, reportNameValuePairs), + [orderedReportIDs, reportsToDisplayInLHN, activeTab, showUnreadOnly, reportNameValuePairs], ); // Get the actual reports based on the filtered IDs @@ -294,6 +298,10 @@ function SidebarOrderedReportsContextProvider({ setInboxTab(tab); }, []); + const toggleUnreadFilter = useCallback(() => { + setInboxUnreadFilter(!showUnreadOnly); + }, [showUnreadOnly]); + const stateValue: SidebarOrderedReportsStateContextValue = useMemo(() => { // We need to make sure the current report is in the list of reports, but we do not want // to have to re-generate the list every time the currentReportID changes. To do that @@ -312,7 +320,7 @@ function SidebarOrderedReportsContextProvider({ filteredReportIDs.indexOf(derivedCurrentReportID) === -1 ) { const updatedReportIDs = getOrderedReportIDs(); - const updatedFilteredIDs = SidebarUtils.filterReportsForInboxTab(updatedReportIDs, reportsToDisplayInLHN, activeTab, hasDraftByReportID, reportNameValuePairs); + const updatedFilteredIDs = SidebarUtils.filterReportsForInboxTab(updatedReportIDs, reportsToDisplayInLHN, activeTab, showUnreadOnly, reportNameValuePairs); const updatedReports = getOrderedReports(updatedFilteredIDs); return { orderedReports: updatedReports, @@ -320,6 +328,7 @@ function SidebarOrderedReportsContextProvider({ currentReportID: derivedCurrentReportID, chatTabBrickRoad: getChatTabBrickRoad(updatedReportIDs, reportAttributes), activeTab, + showUnreadOnly, }; } @@ -329,6 +338,7 @@ function SidebarOrderedReportsContextProvider({ currentReportID: derivedCurrentReportID, chatTabBrickRoad: getChatTabBrickRoad(orderedReportIDs, reportAttributes), activeTab, + showUnreadOnly, }; }, [ getOrderedReportIDs, @@ -340,12 +350,12 @@ function SidebarOrderedReportsContextProvider({ orderedReports, reportAttributes, activeTab, + showUnreadOnly, reportsToDisplayInLHN, - hasDraftByReportID, reportNameValuePairs, ]); - const actionsValue: SidebarOrderedReportsActionsContextValue = useMemo(() => ({clearLHNCache, setActiveTab}), [clearLHNCache, setActiveTab]); + const actionsValue: SidebarOrderedReportsActionsContextValue = useMemo(() => ({clearLHNCache, setActiveTab, toggleUnreadFilter}), [clearLHNCache, setActiveTab, toggleUnreadFilter]); const currentDeps = { activeTab, diff --git a/src/languages/de.ts b/src/languages/de.ts index 9c9f75001ff2..befcb46025c3 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -2788,9 +2788,10 @@ ${amount} für ${merchant} – ${date}`, }, inboxTabs: { all: 'All', - unreads: 'Unread', + todo: 'To do', expenses: 'Expenses', - directMessages: 'Direct messages', + dms: 'DMs', + unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, diff --git a/src/languages/en.ts b/src/languages/en.ts index 12480bbb55c4..9be6ecbc314c 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2842,9 +2842,10 @@ const translations = { }, inboxTabs: { all: 'All', - unreads: 'Unread', + todo: 'To do', expenses: 'Expenses', - directMessages: 'Direct messages', + dms: 'DMs', + unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, diff --git a/src/languages/es.ts b/src/languages/es.ts index d9243ca2d933..741f20fc7dfb 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2659,9 +2659,10 @@ ${amount} para ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - unreads: 'Unread', + todo: 'To do', expenses: 'Expenses', - directMessages: 'Direct messages', + dms: 'DMs', + unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName) => `en ${policyName}`, diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 57e3862724a6..d90ee798f375 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -2794,9 +2794,10 @@ ${amount} pour ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - unreads: 'Unread', + todo: 'To do', expenses: 'Expenses', - directMessages: 'Direct messages', + dms: 'DMs', + unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `dans ${policyName}`, diff --git a/src/languages/it.ts b/src/languages/it.ts index eea77cc79424..dd14322c29c2 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -2783,9 +2783,10 @@ ${amount} per ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - unreads: 'Unread', + todo: 'To do', expenses: 'Expenses', - directMessages: 'Direct messages', + dms: 'DMs', + unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 455bf7a8850a..1600d5034687 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -2756,9 +2756,10 @@ ${date} の ${merchant} への ${amount}`, }, inboxTabs: { all: 'All', - unreads: 'Unread', + todo: 'To do', expenses: 'Expenses', - directMessages: 'Direct messages', + dms: 'DMs', + unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `${policyName} 内`, diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 4038df5299d4..9617a0d09edb 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -2781,9 +2781,10 @@ ${amount} voor ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - unreads: 'Unread', + todo: 'To do', expenses: 'Expenses', - directMessages: 'Direct messages', + dms: 'DMs', + unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 496974e494b8..4950507eb6d8 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -2774,9 +2774,10 @@ ${amount} dla ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - unreads: 'Unread', + todo: 'To do', expenses: 'Expenses', - directMessages: 'Direct messages', + dms: 'DMs', + unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `w ${policyName}`, diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 9cecddddfb48..a15d7cc509c4 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -2774,9 +2774,10 @@ ${amount} para ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - unreads: 'Unread', + todo: 'To do', expenses: 'Expenses', - directMessages: 'Direct messages', + dms: 'DMs', + unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `em ${policyName}`, diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index aade94e81823..3db3f03dda5c 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -2706,9 +2706,10 @@ ${amount},商户:${merchant} - 日期:${date}`, }, inboxTabs: { all: 'All', - unreads: 'Unread', + todo: 'To do', expenses: 'Expenses', - directMessages: 'Direct messages', + dms: 'DMs', + unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `在 ${policyName} 中`, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 623caf8ad8d8..2bec6d18432c 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -1412,29 +1412,34 @@ function filterReportsForInboxTab( reportIDs: string[], reportsToDisplay: ReportsToDisplayInLHN, activeTab: ValueOf, - reportsDrafts?: Record, + showUnreadOnly: boolean, reportNameValuePairs?: OnyxCollection, ): string[] { - if (activeTab === CONST.INBOX_TAB.ALL) { - return reportIDs; - } - return reportIDs.filter((reportID) => { const report = reportsToDisplay[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report) { return false; } - switch (activeTab) { - case CONST.INBOX_TAB.UNREADS: { - const isReportArchived = isArchivedReport(reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`]); - const isReportUnread = isUnread(report, undefined, isReportArchived) && getReportNotificationPreference(report) !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE; - const hasDraft = !!reportsDrafts?.[reportID]; - return isReportUnread || !!report.isPinned || !!report.requiresAttention || !!report.hasErrorsOtherThanFailedReceipt || hasDraft; + // Apply unread filter when the toggle is active + if (showUnreadOnly) { + const isReportArchived = isArchivedReport(reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`]); + const isReportUnread = isUnread(report, undefined, isReportArchived) && getReportNotificationPreference(report) !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE; + if (!isReportUnread && !report.isPinned && !report.requiresAttention && !report.hasErrorsOtherThanFailedReceipt) { + return false; } + } + + if (activeTab === CONST.INBOX_TAB.ALL) { + return true; + } + + switch (activeTab) { + case CONST.INBOX_TAB.TODO: + return !!report.requiresAttention || !!report.hasErrorsOtherThanFailedReceipt; case CONST.INBOX_TAB.EXPENSES: return isExpenseReport(report) || isIOUReport(report) || isInvoiceReport(report) || isPolicyExpenseChat(report); - case CONST.INBOX_TAB.DIRECT_MESSAGES: + case CONST.INBOX_TAB.DMS: return isDM(report) || isSelfDM(report) || isGroupChatUtil(report); default: return true; diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 138d02d821d5..975dfeaafc19 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -953,6 +953,10 @@ function setInboxTab(tab: ValueOf) { Onyx.merge(ONYXKEYS.NVP_INBOX_TAB, tab); } +function setInboxUnreadFilter(enabled: boolean) { + Onyx.merge(ONYXKEYS.NVP_INBOX_UNREAD_FILTER, enabled); +} + function setShouldUseStagingServer(shouldUseStagingServer: boolean) { if (CONFIG.IS_HYBRID_APP) { HybridAppModule.shouldUseStaging(shouldUseStagingServer); @@ -1878,6 +1882,7 @@ export { subscribeToUserEvents, updatePreferredSkinTone, setInboxTab, + setInboxUnreadFilter, setShouldUseStagingServer, togglePlatformMute, joinScreenShare, diff --git a/src/pages/inbox/sidebar/BaseSidebarScreen.tsx b/src/pages/inbox/sidebar/BaseSidebarScreen.tsx index fb4a748e7460..8f3bf3d553d8 100644 --- a/src/pages/inbox/sidebar/BaseSidebarScreen.tsx +++ b/src/pages/inbox/sidebar/BaseSidebarScreen.tsx @@ -15,6 +15,7 @@ import {isMobile} from '@libs/Browser'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import ONYXKEYS from '@src/ONYXKEYS'; import InboxTabSelector from './InboxTabSelector'; +import InboxUnreadToggle from './InboxUnreadToggle'; import SidebarLinksData from './SidebarLinksData'; // Once the app finishes loading for the first time, we never show the skeleton again @@ -59,7 +60,9 @@ function BaseSidebarScreen() { breadcrumbLabel={translate('common.inbox')} shouldDisplaySearch={shouldUseNarrowLayout} shouldDisplayHelpButton={shouldUseNarrowLayout} - /> + > + {!shouldShowSkeleton && } + {!shouldShowSkeleton && } {shouldShowSkeleton ? ( diff --git a/src/pages/inbox/sidebar/InboxTabSelector.tsx b/src/pages/inbox/sidebar/InboxTabSelector.tsx index d8e75506b891..0cc87b328b3e 100644 --- a/src/pages/inbox/sidebar/InboxTabSelector.tsx +++ b/src/pages/inbox/sidebar/InboxTabSelector.tsx @@ -13,9 +13,9 @@ function InboxTabSelector() { const tabs: TabSelectorBaseItem[] = [ {key: CONST.INBOX_TAB.ALL, title: translate('inboxTabs.all')}, - {key: CONST.INBOX_TAB.UNREADS, title: translate('inboxTabs.unreads')}, + {key: CONST.INBOX_TAB.TODO, title: translate('inboxTabs.todo')}, {key: CONST.INBOX_TAB.EXPENSES, title: translate('inboxTabs.expenses')}, - {key: CONST.INBOX_TAB.DIRECT_MESSAGES, title: translate('inboxTabs.directMessages')}, + {key: CONST.INBOX_TAB.DMS, title: translate('inboxTabs.dms')}, ]; return ( diff --git a/src/pages/inbox/sidebar/InboxUnreadToggle.tsx b/src/pages/inbox/sidebar/InboxUnreadToggle.tsx new file mode 100644 index 000000000000..e5d38653b2a8 --- /dev/null +++ b/src/pages/inbox/sidebar/InboxUnreadToggle.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import {View} from 'react-native'; +import Switch from '@components/Switch'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import {useSidebarOrderedReportsActions, useSidebarOrderedReportsState} from '@hooks/useSidebarOrderedReports'; +import useThemeStyles from '@hooks/useThemeStyles'; + +function InboxUnreadToggle() { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const {showUnreadOnly} = useSidebarOrderedReportsState(); + const {toggleUnreadFilter} = useSidebarOrderedReportsActions(); + + return ( + + {translate('inboxTabs.unreadToggle')} + toggleUnreadFilter()} + accessibilityLabel={translate('inboxTabs.unreadToggle')} + size="small" + /> + + ); +} + +InboxUnreadToggle.displayName = 'InboxUnreadToggle'; + +export default InboxUnreadToggle; diff --git a/src/styles/index.ts b/src/styles/index.ts index eae83119cc2f..172579d142fb 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3039,6 +3039,13 @@ const staticStyles = (theme: ThemeColors) => padding: 15, }, + switchTrackSmall: { + width: 36, + height: 20, + borderRadius: 10, + padding: 10, + }, + switchThumb: { width: 22, height: 22, @@ -3050,6 +3057,13 @@ const staticStyles = (theme: ThemeColors) => backgroundColor: theme.appBG, }, + switchThumbSmall: { + width: 14, + height: 14, + borderRadius: 7, + left: 3, + }, + radioButtonContainer: { backgroundColor: theme.componentBG, borderRadius: 14, From 8634be9aef25b9b6c02ab26feb9fd67635c86a6d Mon Sep 17 00:00:00 2001 From: Shawn Borton Date: Fri, 17 Apr 2026 15:56:42 +0200 Subject: [PATCH 3/7] Change 'To do' tab label to 'To-do' Co-Authored-By: Claude Opus 4.6 (1M context) --- src/languages/de.ts | 2 +- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- src/languages/fr.ts | 2 +- src/languages/it.ts | 2 +- src/languages/ja.ts | 2 +- src/languages/nl.ts | 2 +- src/languages/pl.ts | 2 +- src/languages/pt-BR.ts | 2 +- src/languages/zh-hans.ts | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index befcb46025c3..f524383c939a 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -2788,7 +2788,7 @@ ${amount} für ${merchant} – ${date}`, }, inboxTabs: { all: 'All', - todo: 'To do', + todo: 'To-do', expenses: 'Expenses', dms: 'DMs', unreadToggle: 'Unreads', diff --git a/src/languages/en.ts b/src/languages/en.ts index 9be6ecbc314c..65997ac79bc6 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2842,7 +2842,7 @@ const translations = { }, inboxTabs: { all: 'All', - todo: 'To do', + todo: 'To-do', expenses: 'Expenses', dms: 'DMs', unreadToggle: 'Unreads', diff --git a/src/languages/es.ts b/src/languages/es.ts index 741f20fc7dfb..a0e6fcbbe9d2 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2659,7 +2659,7 @@ ${amount} para ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - todo: 'To do', + todo: 'To-do', expenses: 'Expenses', dms: 'DMs', unreadToggle: 'Unreads', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index d90ee798f375..c7eabf81d038 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -2794,7 +2794,7 @@ ${amount} pour ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - todo: 'To do', + todo: 'To-do', expenses: 'Expenses', dms: 'DMs', unreadToggle: 'Unreads', diff --git a/src/languages/it.ts b/src/languages/it.ts index dd14322c29c2..b1f599039dc7 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -2783,7 +2783,7 @@ ${amount} per ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - todo: 'To do', + todo: 'To-do', expenses: 'Expenses', dms: 'DMs', unreadToggle: 'Unreads', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 1600d5034687..abf3fc73df15 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -2756,7 +2756,7 @@ ${date} の ${merchant} への ${amount}`, }, inboxTabs: { all: 'All', - todo: 'To do', + todo: 'To-do', expenses: 'Expenses', dms: 'DMs', unreadToggle: 'Unreads', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 9617a0d09edb..4cc5e5d067f1 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -2781,7 +2781,7 @@ ${amount} voor ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - todo: 'To do', + todo: 'To-do', expenses: 'Expenses', dms: 'DMs', unreadToggle: 'Unreads', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 4950507eb6d8..7cc40fa1f87c 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -2774,7 +2774,7 @@ ${amount} dla ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - todo: 'To do', + todo: 'To-do', expenses: 'Expenses', dms: 'DMs', unreadToggle: 'Unreads', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index a15d7cc509c4..912f0c2f827d 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -2774,7 +2774,7 @@ ${amount} para ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - todo: 'To do', + todo: 'To-do', expenses: 'Expenses', dms: 'DMs', unreadToggle: 'Unreads', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 3db3f03dda5c..2eba054e6446 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -2706,7 +2706,7 @@ ${amount},商户:${merchant} - 日期:${date}`, }, inboxTabs: { all: 'All', - todo: 'To do', + todo: 'To-do', expenses: 'Expenses', dms: 'DMs', unreadToggle: 'Unreads', From e2ebe4607b02abdaeac731b9e04f48017d3fe441 Mon Sep 17 00:00:00 2001 From: Shawn Borton Date: Tue, 21 Apr 2026 18:14:56 +0200 Subject: [PATCH 4/7] Replace Priority Mode with Inbox Tab Filters Simplify inbox filtering to four straightforward tabs: All, Unread, Expenses, and DMs. Remove the separate Unreads toggle and To-do tab in favor of a clean Unread tab that shows only unread and non-muted chats. Pinned/GBR/RBR items show only if they qualify for the active tab filter. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/CONST/index.ts | 2 +- src/ONYXKEYS.ts | 4 --- src/hooks/useSidebarOrderedReports.tsx | 23 ++++---------- src/languages/de.ts | 3 +- src/languages/en.ts | 3 +- src/languages/es.ts | 3 +- src/languages/fr.ts | 3 +- src/languages/it.ts | 3 +- src/languages/ja.ts | 3 +- src/languages/nl.ts | 3 +- src/languages/pl.ts | 3 +- src/languages/pt-BR.ts | 3 +- src/languages/zh-hans.ts | 3 +- src/libs/SidebarUtils.ts | 24 +++++---------- src/libs/actions/User.ts | 5 ---- src/pages/inbox/sidebar/BaseSidebarScreen.tsx | 5 +--- src/pages/inbox/sidebar/InboxTabSelector.tsx | 2 +- src/pages/inbox/sidebar/InboxUnreadToggle.tsx | 30 ------------------- 18 files changed, 26 insertions(+), 99 deletions(-) delete mode 100644 src/pages/inbox/sidebar/InboxUnreadToggle.tsx diff --git a/src/CONST/index.ts b/src/CONST/index.ts index be0e4fd88728..6bf0ab68c2c2 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -2085,7 +2085,7 @@ const CONST = { }, INBOX_TAB: { ALL: 'all', - TODO: 'todo', + UNREAD: 'unread', EXPENSES: 'expenses', DMS: 'dms', }, diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 3dce345ee6e7..b7da30c7c155 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -162,9 +162,6 @@ const ONYXKEYS = { /** Contains the user preference for the active inbox tab filter */ NVP_INBOX_TAB: 'nvp_inboxTab', - /** Whether to show only unread chats in the inbox */ - NVP_INBOX_UNREAD_FILTER: 'nvp_inboxUnreadFilter', - /** Contains the users's block expiration (if they have one) */ NVP_BLOCKED_FROM_CONCIERGE: 'nvp_private_blockedFromConcierge', @@ -1357,7 +1354,6 @@ type OnyxValuesMapping = { [ONYXKEYS.BETA_CONFIGURATION]: OnyxTypes.BetaConfiguration; [ONYXKEYS.NVP_MUTED_PLATFORMS]: Partial>; [ONYXKEYS.NVP_INBOX_TAB]: ValueOf; - [ONYXKEYS.NVP_INBOX_UNREAD_FILTER]: boolean; [ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE]: OnyxTypes.BlockedFromConcierge; [ONYXKEYS.QUEUE_FLUSHED_DATA]: AnyOnyxUpdate[]; [ONYXKEYS.TRANSACTIONS_PENDING_3DS_REVIEW]: OnyxTypes.TransactionsPending3DSReview; diff --git a/src/hooks/useSidebarOrderedReports.tsx b/src/hooks/useSidebarOrderedReports.tsx index c54abee7d3b8..f0164fadc729 100644 --- a/src/hooks/useSidebarOrderedReports.tsx +++ b/src/hooks/useSidebarOrderedReports.tsx @@ -2,7 +2,7 @@ import {deepEqual} from 'fast-equals'; import React, {createContext, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; -import {setInboxTab, setInboxUnreadFilter} from '@libs/actions/User'; +import {setInboxTab} from '@libs/actions/User'; import Log from '@libs/Log'; import {getTransactionThreadReportID} from '@libs/MergeTransactionUtils'; import {isOneTransactionReport} from '@libs/ReportUtils'; @@ -36,13 +36,11 @@ type SidebarOrderedReportsStateContextValue = { currentReportID: string | undefined; chatTabBrickRoad: BrickRoad; activeTab: ValueOf; - showUnreadOnly: boolean; }; type SidebarOrderedReportsActionsContextValue = { clearLHNCache: () => void; setActiveTab: (tab: ValueOf) => void; - toggleUnreadFilter: () => void; }; type ReportsToDisplayInLHN = Record; @@ -53,13 +51,11 @@ const SidebarOrderedReportsStateContext = createContext({ clearLHNCache: () => {}, setActiveTab: () => {}, - toggleUnreadFilter: () => {}, }); const policyMapper = (policy: OnyxEntry): PartialPolicyForSidebar => @@ -85,8 +81,6 @@ function SidebarOrderedReportsContextProvider({ const {localeCompare} = useLocalize(); const [inboxTab = CONST.INBOX_TAB.ALL] = useOnyx(ONYXKEYS.NVP_INBOX_TAB); const activeTab = inboxTab ?? CONST.INBOX_TAB.ALL; - const [inboxUnreadFilter = false] = useOnyx(ONYXKEYS.NVP_INBOX_UNREAD_FILTER); - const showUnreadOnly = inboxUnreadFilter ?? false; const [chatReports, {sourceValue: reportUpdates}] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [policies, {sourceValue: policiesUpdates}] = useMappedPolicies(policyMapper); const [transactions, {sourceValue: transactionsUpdates}] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION); @@ -271,8 +265,8 @@ function SidebarOrderedReportsContextProvider({ const orderedReportIDs = useMemo(() => getOrderedReportIDs(), [getOrderedReportIDs]); const filteredReportIDs = useMemo( - () => SidebarUtils.filterReportsForInboxTab(orderedReportIDs, reportsToDisplayInLHN, activeTab, showUnreadOnly, reportNameValuePairs), - [orderedReportIDs, reportsToDisplayInLHN, activeTab, showUnreadOnly, reportNameValuePairs], + () => SidebarUtils.filterReportsForInboxTab(orderedReportIDs, reportsToDisplayInLHN, activeTab, reportNameValuePairs), + [orderedReportIDs, reportsToDisplayInLHN, activeTab, reportNameValuePairs], ); // Get the actual reports based on the filtered IDs @@ -298,10 +292,6 @@ function SidebarOrderedReportsContextProvider({ setInboxTab(tab); }, []); - const toggleUnreadFilter = useCallback(() => { - setInboxUnreadFilter(!showUnreadOnly); - }, [showUnreadOnly]); - const stateValue: SidebarOrderedReportsStateContextValue = useMemo(() => { // We need to make sure the current report is in the list of reports, but we do not want // to have to re-generate the list every time the currentReportID changes. To do that @@ -320,7 +310,7 @@ function SidebarOrderedReportsContextProvider({ filteredReportIDs.indexOf(derivedCurrentReportID) === -1 ) { const updatedReportIDs = getOrderedReportIDs(); - const updatedFilteredIDs = SidebarUtils.filterReportsForInboxTab(updatedReportIDs, reportsToDisplayInLHN, activeTab, showUnreadOnly, reportNameValuePairs); + const updatedFilteredIDs = SidebarUtils.filterReportsForInboxTab(updatedReportIDs, reportsToDisplayInLHN, activeTab, reportNameValuePairs); const updatedReports = getOrderedReports(updatedFilteredIDs); return { orderedReports: updatedReports, @@ -328,7 +318,6 @@ function SidebarOrderedReportsContextProvider({ currentReportID: derivedCurrentReportID, chatTabBrickRoad: getChatTabBrickRoad(updatedReportIDs, reportAttributes), activeTab, - showUnreadOnly, }; } @@ -338,7 +327,6 @@ function SidebarOrderedReportsContextProvider({ currentReportID: derivedCurrentReportID, chatTabBrickRoad: getChatTabBrickRoad(orderedReportIDs, reportAttributes), activeTab, - showUnreadOnly, }; }, [ getOrderedReportIDs, @@ -350,12 +338,11 @@ function SidebarOrderedReportsContextProvider({ orderedReports, reportAttributes, activeTab, - showUnreadOnly, reportsToDisplayInLHN, reportNameValuePairs, ]); - const actionsValue: SidebarOrderedReportsActionsContextValue = useMemo(() => ({clearLHNCache, setActiveTab, toggleUnreadFilter}), [clearLHNCache, setActiveTab, toggleUnreadFilter]); + const actionsValue: SidebarOrderedReportsActionsContextValue = useMemo(() => ({clearLHNCache, setActiveTab}), [clearLHNCache, setActiveTab]); const currentDeps = { activeTab, diff --git a/src/languages/de.ts b/src/languages/de.ts index f524383c939a..80b0180e4926 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -2788,10 +2788,9 @@ ${amount} für ${merchant} – ${date}`, }, inboxTabs: { all: 'All', - todo: 'To-do', + unread: 'Unread', expenses: 'Expenses', dms: 'DMs', - unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, diff --git a/src/languages/en.ts b/src/languages/en.ts index 65997ac79bc6..8e2103eff286 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2842,10 +2842,9 @@ const translations = { }, inboxTabs: { all: 'All', - todo: 'To-do', + unread: 'Unread', expenses: 'Expenses', dms: 'DMs', - unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, diff --git a/src/languages/es.ts b/src/languages/es.ts index a0e6fcbbe9d2..7a8425dd7fec 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2659,10 +2659,9 @@ ${amount} para ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - todo: 'To-do', + unread: 'Unread', expenses: 'Expenses', dms: 'DMs', - unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName) => `en ${policyName}`, diff --git a/src/languages/fr.ts b/src/languages/fr.ts index c7eabf81d038..8633626d7080 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -2794,10 +2794,9 @@ ${amount} pour ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - todo: 'To-do', + unread: 'Unread', expenses: 'Expenses', dms: 'DMs', - unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `dans ${policyName}`, diff --git a/src/languages/it.ts b/src/languages/it.ts index b1f599039dc7..cd28d7c31dad 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -2783,10 +2783,9 @@ ${amount} per ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - todo: 'To-do', + unread: 'Unread', expenses: 'Expenses', dms: 'DMs', - unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, diff --git a/src/languages/ja.ts b/src/languages/ja.ts index abf3fc73df15..0da264509b32 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -2756,10 +2756,9 @@ ${date} の ${merchant} への ${amount}`, }, inboxTabs: { all: 'All', - todo: 'To-do', + unread: 'Unread', expenses: 'Expenses', dms: 'DMs', - unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `${policyName} 内`, diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 4cc5e5d067f1..ead8c12235e4 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -2781,10 +2781,9 @@ ${amount} voor ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - todo: 'To-do', + unread: 'Unread', expenses: 'Expenses', dms: 'DMs', - unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 7cc40fa1f87c..0a08aad6fd97 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -2774,10 +2774,9 @@ ${amount} dla ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - todo: 'To-do', + unread: 'Unread', expenses: 'Expenses', dms: 'DMs', - unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `w ${policyName}`, diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 912f0c2f827d..e2b81c2f510e 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -2774,10 +2774,9 @@ ${amount} para ${merchant} - ${date}`, }, inboxTabs: { all: 'All', - todo: 'To-do', + unread: 'Unread', expenses: 'Expenses', dms: 'DMs', - unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `em ${policyName}`, diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 2eba054e6446..cfbd7bf3d21d 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -2706,10 +2706,9 @@ ${amount},商户:${merchant} - 日期:${date}`, }, inboxTabs: { all: 'All', - todo: 'To-do', + unread: 'Unread', expenses: 'Expenses', dms: 'DMs', - unreadToggle: 'Unreads', }, reportDetailsPage: { inWorkspace: (policyName: string) => `在 ${policyName} 中`, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 2bec6d18432c..0cf4dd3b86aa 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -1412,31 +1412,23 @@ function filterReportsForInboxTab( reportIDs: string[], reportsToDisplay: ReportsToDisplayInLHN, activeTab: ValueOf, - showUnreadOnly: boolean, reportNameValuePairs?: OnyxCollection, ): string[] { + if (activeTab === CONST.INBOX_TAB.ALL) { + return reportIDs; + } + return reportIDs.filter((reportID) => { const report = reportsToDisplay[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; if (!report) { return false; } - // Apply unread filter when the toggle is active - if (showUnreadOnly) { - const isReportArchived = isArchivedReport(reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`]); - const isReportUnread = isUnread(report, undefined, isReportArchived) && getReportNotificationPreference(report) !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE; - if (!isReportUnread && !report.isPinned && !report.requiresAttention && !report.hasErrorsOtherThanFailedReceipt) { - return false; - } - } - - if (activeTab === CONST.INBOX_TAB.ALL) { - return true; - } - switch (activeTab) { - case CONST.INBOX_TAB.TODO: - return !!report.requiresAttention || !!report.hasErrorsOtherThanFailedReceipt; + case CONST.INBOX_TAB.UNREAD: { + const isReportArchived = isArchivedReport(reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`]); + return isUnread(report, undefined, isReportArchived) && getReportNotificationPreference(report) !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE; + } case CONST.INBOX_TAB.EXPENSES: return isExpenseReport(report) || isIOUReport(report) || isInvoiceReport(report) || isPolicyExpenseChat(report); case CONST.INBOX_TAB.DMS: diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 975dfeaafc19..138d02d821d5 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -953,10 +953,6 @@ function setInboxTab(tab: ValueOf) { Onyx.merge(ONYXKEYS.NVP_INBOX_TAB, tab); } -function setInboxUnreadFilter(enabled: boolean) { - Onyx.merge(ONYXKEYS.NVP_INBOX_UNREAD_FILTER, enabled); -} - function setShouldUseStagingServer(shouldUseStagingServer: boolean) { if (CONFIG.IS_HYBRID_APP) { HybridAppModule.shouldUseStaging(shouldUseStagingServer); @@ -1882,7 +1878,6 @@ export { subscribeToUserEvents, updatePreferredSkinTone, setInboxTab, - setInboxUnreadFilter, setShouldUseStagingServer, togglePlatformMute, joinScreenShare, diff --git a/src/pages/inbox/sidebar/BaseSidebarScreen.tsx b/src/pages/inbox/sidebar/BaseSidebarScreen.tsx index 8f3bf3d553d8..fb4a748e7460 100644 --- a/src/pages/inbox/sidebar/BaseSidebarScreen.tsx +++ b/src/pages/inbox/sidebar/BaseSidebarScreen.tsx @@ -15,7 +15,6 @@ import {isMobile} from '@libs/Browser'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import ONYXKEYS from '@src/ONYXKEYS'; import InboxTabSelector from './InboxTabSelector'; -import InboxUnreadToggle from './InboxUnreadToggle'; import SidebarLinksData from './SidebarLinksData'; // Once the app finishes loading for the first time, we never show the skeleton again @@ -60,9 +59,7 @@ function BaseSidebarScreen() { breadcrumbLabel={translate('common.inbox')} shouldDisplaySearch={shouldUseNarrowLayout} shouldDisplayHelpButton={shouldUseNarrowLayout} - > - {!shouldShowSkeleton && } - + /> {!shouldShowSkeleton && } {shouldShowSkeleton ? ( diff --git a/src/pages/inbox/sidebar/InboxTabSelector.tsx b/src/pages/inbox/sidebar/InboxTabSelector.tsx index 0cc87b328b3e..4de90b6aa6e5 100644 --- a/src/pages/inbox/sidebar/InboxTabSelector.tsx +++ b/src/pages/inbox/sidebar/InboxTabSelector.tsx @@ -13,7 +13,7 @@ function InboxTabSelector() { const tabs: TabSelectorBaseItem[] = [ {key: CONST.INBOX_TAB.ALL, title: translate('inboxTabs.all')}, - {key: CONST.INBOX_TAB.TODO, title: translate('inboxTabs.todo')}, + {key: CONST.INBOX_TAB.UNREAD, title: translate('inboxTabs.unread')}, {key: CONST.INBOX_TAB.EXPENSES, title: translate('inboxTabs.expenses')}, {key: CONST.INBOX_TAB.DMS, title: translate('inboxTabs.dms')}, ]; diff --git a/src/pages/inbox/sidebar/InboxUnreadToggle.tsx b/src/pages/inbox/sidebar/InboxUnreadToggle.tsx deleted file mode 100644 index e5d38653b2a8..000000000000 --- a/src/pages/inbox/sidebar/InboxUnreadToggle.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import Switch from '@components/Switch'; -import Text from '@components/Text'; -import useLocalize from '@hooks/useLocalize'; -import {useSidebarOrderedReportsActions, useSidebarOrderedReportsState} from '@hooks/useSidebarOrderedReports'; -import useThemeStyles from '@hooks/useThemeStyles'; - -function InboxUnreadToggle() { - const styles = useThemeStyles(); - const {translate} = useLocalize(); - const {showUnreadOnly} = useSidebarOrderedReportsState(); - const {toggleUnreadFilter} = useSidebarOrderedReportsActions(); - - return ( - - {translate('inboxTabs.unreadToggle')} - toggleUnreadFilter()} - accessibilityLabel={translate('inboxTabs.unreadToggle')} - size="small" - /> - - ); -} - -InboxUnreadToggle.displayName = 'InboxUnreadToggle'; - -export default InboxUnreadToggle; From 53cd10fe3a4a990b86f917052b1d3904f79360ed Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Sat, 30 May 2026 21:47:39 +0000 Subject: [PATCH 5/7] Remove DM & expense filters --- src/CONST/index.ts | 3 +-- src/languages/de.ts | 3 +-- src/languages/en.ts | 3 +-- src/languages/es.ts | 3 +-- src/languages/fr.ts | 3 +-- src/languages/it.ts | 3 +-- src/languages/ja.ts | 3 +-- src/languages/nl.ts | 3 +-- src/languages/pl.ts | 3 +-- src/languages/pt-BR.ts | 3 +-- src/languages/zh-hans.ts | 3 +-- src/libs/SidebarUtils.ts | 9 +++------ src/pages/inbox/sidebar/InboxTabSelector.tsx | 6 +++--- src/types/onyx/InboxTab.ts | 1 + tests/perf-test/SidebarLinks.perf-test.tsx | 4 ++-- tests/unit/SidebarFilterTest.ts | 12 ++++++++++++ tests/unit/SidebarTest.ts | 4 ++-- tests/unit/SidebarUtilsTest.ts | 14 ++++++++------ 18 files changed, 42 insertions(+), 41 deletions(-) diff --git a/src/CONST/index.ts b/src/CONST/index.ts index d3eed1c741bc..d06fa29aa180 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -2245,9 +2245,8 @@ const CONST = { }, INBOX_TAB: { ALL: 'all', + TODO: 'todo', UNREAD: 'unread', - EXPENSES: 'expenses', - DMS: 'dms', }, THEME: { DEFAULT: 'system', diff --git a/src/languages/de.ts b/src/languages/de.ts index b57d98074f67..e3222cf3a286 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -2896,9 +2896,8 @@ ${amount} für ${merchant} – ${date}`, }, inboxTabs: { all: 'All', + todo: 'To-do', unread: 'Unread', - expenses: 'Expenses', - dms: 'DMs', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, diff --git a/src/languages/en.ts b/src/languages/en.ts index 3dd1a57e6add..62629563c128 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2967,9 +2967,8 @@ const translations = { }, inboxTabs: { all: 'All', + todo: 'To-do', unread: 'Unread', - expenses: 'Expenses', - dms: 'DMs', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, diff --git a/src/languages/es.ts b/src/languages/es.ts index 3412103bef82..a222c9dd554d 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2775,9 +2775,8 @@ ${amount} para ${merchant} - ${date}`, }, inboxTabs: { all: 'All', + todo: 'To-do', unread: 'Unread', - expenses: 'Expenses', - dms: 'DMs', }, reportDetailsPage: { inWorkspace: (policyName) => `en ${policyName}`, diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 7a7719b72631..48d1f73eb2dd 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -2904,9 +2904,8 @@ ${amount} pour ${merchant} - ${date}`, }, inboxTabs: { all: 'All', + todo: 'To-do', unread: 'Unread', - expenses: 'Expenses', - dms: 'DMs', }, reportDetailsPage: { inWorkspace: (policyName: string) => `dans ${policyName}`, diff --git a/src/languages/it.ts b/src/languages/it.ts index 7a16dcfd21e9..74008d5b625a 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -2892,9 +2892,8 @@ ${amount} per ${merchant} - ${date}`, }, inboxTabs: { all: 'All', + todo: 'To-do', unread: 'Unread', - expenses: 'Expenses', - dms: 'DMs', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 744f96150eab..846c4766166c 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -2868,9 +2868,8 @@ ${date} の ${merchant} への ${amount}`, }, inboxTabs: { all: 'All', + todo: 'To-do', unread: 'Unread', - expenses: 'Expenses', - dms: 'DMs', }, reportDetailsPage: { inWorkspace: (policyName: string) => `${policyName} 内`, diff --git a/src/languages/nl.ts b/src/languages/nl.ts index e057783c5813..c522b5421e4e 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -2889,9 +2889,8 @@ ${amount} voor ${merchant} - ${date}`, }, inboxTabs: { all: 'All', + todo: 'To-do', unread: 'Unread', - expenses: 'Expenses', - dms: 'DMs', }, reportDetailsPage: { inWorkspace: (policyName: string) => `in ${policyName}`, diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 3a736a4f510f..58a4b7b49757 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -2883,9 +2883,8 @@ ${amount} dla ${merchant} - ${date}`, }, inboxTabs: { all: 'All', + todo: 'To-do', unread: 'Unread', - expenses: 'Expenses', - dms: 'DMs', }, reportDetailsPage: { inWorkspace: (policyName: string) => `w ${policyName}`, diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 1bb944ab259b..0de92a894209 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -2883,9 +2883,8 @@ ${amount} para ${merchant} - ${date}`, }, inboxTabs: { all: 'All', + todo: 'To-do', unread: 'Unread', - expenses: 'Expenses', - dms: 'DMs', }, reportDetailsPage: { inWorkspace: (policyName: string) => `em ${policyName}`, diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 2b07dcc88697..c0641efcf72e 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -2810,9 +2810,8 @@ ${amount},商户:${merchant} - 日期:${date}`, }, inboxTabs: { all: 'All', + todo: 'To-do', unread: 'Unread', - expenses: 'Expenses', - dms: 'DMs', }, reportDetailsPage: { inWorkspace: (policyName: string) => `在 ${policyName} 中`, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 7594fb6e9a81..0e8efa2ba988 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -171,7 +171,6 @@ import { isChatThread, isConciergeChatReport, isDeprecatedGroupDM, - isDM, isDomainRoom, isExpenseReport, isExpenseRequest, @@ -180,7 +179,6 @@ import { isInvoiceReport, isInvoiceRoom, isIOUOwnedByCurrentUser, - isIOUReport, isJoinRequestInAdminRoom, isMoneyRequestReport, isOneOnOneChat, @@ -1505,14 +1503,13 @@ function filterReportsForInboxTab( } switch (activeTab) { + case CONST.INBOX_TAB.TODO: + // Reports with an outstanding GBR (requiresAttention) or RBR (errors) require the user's action. + return !!report.requiresAttention || !!report.hasErrorsOtherThanFailedReceipt; case CONST.INBOX_TAB.UNREAD: { const isReportArchived = isArchivedReport(reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`]); return isUnread(report, undefined, isReportArchived) && getReportNotificationPreference(report) !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE; } - case CONST.INBOX_TAB.EXPENSES: - return isExpenseReport(report) || isIOUReport(report) || isInvoiceReport(report) || isPolicyExpenseChat(report); - case CONST.INBOX_TAB.DMS: - return isDM(report) || isSelfDM(report) || isGroupChatUtil(report); default: return true; } diff --git a/src/pages/inbox/sidebar/InboxTabSelector.tsx b/src/pages/inbox/sidebar/InboxTabSelector.tsx index 4de90b6aa6e5..89c44a76b9c0 100644 --- a/src/pages/inbox/sidebar/InboxTabSelector.tsx +++ b/src/pages/inbox/sidebar/InboxTabSelector.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import type {ValueOf} from 'type-fest'; import TabSelectorBase from '@components/TabSelector/TabSelectorBase'; import TabSelectorContextProvider from '@components/TabSelector/TabSelectorContext'; import type {TabSelectorBaseItem} from '@components/TabSelector/types'; @@ -13,9 +14,8 @@ function InboxTabSelector() { const tabs: TabSelectorBaseItem[] = [ {key: CONST.INBOX_TAB.ALL, title: translate('inboxTabs.all')}, + {key: CONST.INBOX_TAB.TODO, title: translate('inboxTabs.todo')}, {key: CONST.INBOX_TAB.UNREAD, title: translate('inboxTabs.unread')}, - {key: CONST.INBOX_TAB.EXPENSES, title: translate('inboxTabs.expenses')}, - {key: CONST.INBOX_TAB.DMS, title: translate('inboxTabs.dms')}, ]; return ( @@ -23,7 +23,7 @@ function InboxTabSelector() { setActiveTab(key as ValueOf)} size="small" /> diff --git a/src/types/onyx/InboxTab.ts b/src/types/onyx/InboxTab.ts index 616f5b89f764..3fc35c106b39 100644 --- a/src/types/onyx/InboxTab.ts +++ b/src/types/onyx/InboxTab.ts @@ -1,6 +1,7 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; +/** The active Inbox tab filter (All, To-do, or Unread) */ type InboxTab = ValueOf; export default InboxTab; diff --git a/tests/perf-test/SidebarLinks.perf-test.tsx b/tests/perf-test/SidebarLinks.perf-test.tsx index 46f35544a7ee..66f9493aa726 100644 --- a/tests/perf-test/SidebarLinks.perf-test.tsx +++ b/tests/perf-test/SidebarLinks.perf-test.tsx @@ -81,7 +81,7 @@ describe('SidebarLinks', () => { await Onyx.multiSet({ [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS], - + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, ...mockedResponseMap, }); @@ -103,7 +103,7 @@ describe('SidebarLinks', () => { await Onyx.multiSet({ [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS], - + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, ...mockedResponseMap, }); diff --git a/tests/unit/SidebarFilterTest.ts b/tests/unit/SidebarFilterTest.ts index c377ef0c6e25..1970472dfdc3 100644 --- a/tests/unit/SidebarFilterTest.ts +++ b/tests/unit/SidebarFilterTest.ts @@ -17,6 +17,7 @@ jest.mock('@libs/Permissions'); const ONYXKEYS = { PERSONAL_DETAILS_LIST: 'personalDetailsList', IS_LOADING_APP: 'isLoadingApp', + NVP_PRIORITY_MODE: 'nvp_priorityMode', SESSION: 'session', BETAS: 'betas', COLLECTION: { @@ -371,6 +372,7 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -419,6 +421,7 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, @@ -489,6 +492,7 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, [`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${draftReport.reportID}`]: 'draft report message', @@ -542,6 +546,7 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -607,6 +612,7 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -708,6 +714,7 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -762,6 +769,7 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.DEFAULT, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -814,6 +822,7 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -864,6 +873,7 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -910,6 +920,7 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.BETAS]: betas, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, @@ -945,6 +956,7 @@ xdescribe('Sidebar', () => { // When Onyx is updated to contain that data and the sidebar re-renders .then(() => Onyx.multiSet({ + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportCollectionDataSet, diff --git a/tests/unit/SidebarTest.ts b/tests/unit/SidebarTest.ts index 54a42f4a4ecd..a0fbc44f2995 100644 --- a/tests/unit/SidebarTest.ts +++ b/tests/unit/SidebarTest.ts @@ -94,7 +94,7 @@ describe('Sidebar', () => { return act(async () => { await Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, - + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportNameValuePairsCollection, @@ -157,7 +157,7 @@ describe('Sidebar', () => { return act(async () => { await Onyx.multiSet({ [ONYXKEYS.BETAS]: betas, - + [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_APP]: false, ...reportNameValuePairsCollection, diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index 21d5e19e41f4..058c41917496 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -3776,10 +3776,10 @@ describe('SidebarUtils', () => { ]); const mockLocaleCompare = (a: string, b: string) => a.localeCompare(b); - const useAlphabeticalSort = false; + const priorityMode = CONST.PRIORITY_MODE.DEFAULT; // When the reports are sorted - const result = SidebarUtils.sortReportsToDisplayInLHN(reports, useAlphabeticalSort, mockLocaleCompare, undefined, undefined, undefined); + const result = SidebarUtils.sortReportsToDisplayInLHN(reports, priorityMode, mockLocaleCompare, undefined, undefined, undefined); // Then the reports are sorted in the correct order expect(result).toEqual(['0', '1', '2']); // Pinned first, Error second, Normal third @@ -3804,11 +3804,11 @@ describe('SidebarUtils', () => { const mockLocaleCompare = (a: string, b: string) => a.localeCompare(b); - // When the reports are sorted in default mode (not alphabetical) - const defaultResult = SidebarUtils.sortReportsToDisplayInLHN(reports, false, mockLocaleCompare, undefined, undefined, undefined); + // When the reports are sorted in default mode + const defaultResult = SidebarUtils.sortReportsToDisplayInLHN(reports, CONST.PRIORITY_MODE.DEFAULT, mockLocaleCompare, undefined, undefined, undefined); - // When the reports are sorted in alphabetical mode - const gsdResult = SidebarUtils.sortReportsToDisplayInLHN(reports, true, mockLocaleCompare, undefined, undefined, undefined); + // When the reports are sorted in GSD mode + const gsdResult = SidebarUtils.sortReportsToDisplayInLHN(reports, CONST.PRIORITY_MODE.GSD, mockLocaleCompare, undefined, undefined, undefined); // Then the reports are sorted in the correct order expect(defaultResult).toEqual(['1', '0']); // Most recent first (index 1 has later date) @@ -3828,6 +3828,7 @@ describe('SidebarUtils', () => { reports, updatedReportsKeys: [`${ONYXKEYS.COLLECTION.REPORT}999`], currentReportId: '1', + isInFocusMode: false, betas: [], transactions: {}, transactionViolations: {}, @@ -3851,6 +3852,7 @@ describe('SidebarUtils', () => { reports, updatedReportsKeys: ['0'], currentReportId: undefined, + isInFocusMode: false, betas: [], transactions: {}, transactionViolations: {}, From d8015354bb7a3a51585ca503f8d701fe42dec709 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Sat, 30 May 2026 21:58:22 +0000 Subject: [PATCH 6/7] Perf: improve computing of unread reports --- src/hooks/useSidebarOrderedReports.tsx | 10 ++---- src/libs/SidebarUtils.ts | 43 +++++++++++++++----------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/hooks/useSidebarOrderedReports.tsx b/src/hooks/useSidebarOrderedReports.tsx index 21d39d51b348..7dd37487b1bd 100644 --- a/src/hooks/useSidebarOrderedReports.tsx +++ b/src/hooks/useSidebarOrderedReports.tsx @@ -43,7 +43,7 @@ type SidebarOrderedReportsActionsContextValue = { setActiveTab: (tab: ValueOf) => void; }; -type ReportsToDisplayInLHN = Record; +type ReportsToDisplayInLHN = Record; const SidebarOrderedReportsStateContext = createContext({ orderedReports: [], @@ -296,10 +296,7 @@ function SidebarOrderedReportsContextProvider({ // Narrow the ordered reports down to the ones belonging to the active Inbox tab. The "All" tab // returns everything (still honoring Most Recent / Focus mode from the ordering above). - const filteredReportIDs = useMemo( - () => SidebarUtils.filterReportsForInboxTab(orderedReportIDs, reportsToDisplayInLHN, activeTab, reportNameValuePairs), - [orderedReportIDs, reportsToDisplayInLHN, activeTab, reportNameValuePairs], - ); + const filteredReportIDs = useMemo(() => SidebarUtils.filterReportsForInboxTab(orderedReportIDs, reportsToDisplayInLHN, activeTab), [orderedReportIDs, reportsToDisplayInLHN, activeTab]); // Get the actual reports based on the filtered IDs const getOrderedReports = useCallback( @@ -342,7 +339,7 @@ function SidebarOrderedReportsContextProvider({ filteredReportIDs.indexOf(derivedCurrentReportID) === -1 ) { const updatedReportIDs = getOrderedReportIDs(); - const updatedFilteredIDs = SidebarUtils.filterReportsForInboxTab(updatedReportIDs, reportsToDisplayInLHN, activeTab, reportNameValuePairs); + const updatedFilteredIDs = SidebarUtils.filterReportsForInboxTab(updatedReportIDs, reportsToDisplayInLHN, activeTab); const updatedReports = getOrderedReports(updatedFilteredIDs); return { orderedReports: updatedReports, @@ -371,7 +368,6 @@ function SidebarOrderedReportsContextProvider({ reportAttributes, activeTab, reportsToDisplayInLHN, - reportNameValuePairs, ]); const actionsValue: SidebarOrderedReportsActionsContextValue = useMemo(() => ({clearLHNCache, setActiveTab}), [clearLHNCache, setActiveTab]); diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 0e8efa2ba988..8fda67f74a6b 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -393,6 +393,7 @@ function getReportsToDisplayInLHN({ } const reportDraftComment = draftComments?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report.reportID}`]; + const isReportArchived = isArchivedReport(reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`]); const {shouldDisplay, hasErrorsOtherThanFailedReceipt} = shouldDisplayReportInLHN({ report, @@ -404,7 +405,7 @@ function getReportsToDisplayInLHN({ draftComment: reportDraftComment, transactions, isOffline, - isReportArchived: isArchivedReport(reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`]), + isReportArchived, reportAttributes, currentUserLogin, currentUserAccountID, @@ -412,8 +413,9 @@ function getReportsToDisplayInLHN({ if (shouldDisplay) { const requiresAttention = reportAttributes?.[report?.reportID]?.requiresAttention ?? false; - const hasAttentionOrError = requiresAttention || hasErrorsOtherThanFailedReceipt; - reportsToDisplay[reportID] = hasAttentionOrError ? {...report, requiresAttention, hasErrorsOtherThanFailedReceipt} : report; + const isUnreadReport = getIsUnreadReportForInboxTab(report, isReportArchived); + reportsToDisplay[reportID] = + requiresAttention || hasErrorsOtherThanFailedReceipt || isUnreadReport ? {...report, requiresAttention, hasErrorsOtherThanFailedReceipt, isUnreadReport} : report; } } @@ -474,6 +476,7 @@ function updateReportsToDisplayInLHN({ // Get the specific draft comment for this report instead of using a single draft comment for all reports // This fixes the issue where the current report's draft comment was incorrectly used to filter all reports const reportDraftComment = draftComments?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report.reportID}`]; + const isReportArchived = isArchivedReport(reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`] ?? {}); const {shouldDisplay, hasErrorsOtherThanFailedReceipt} = shouldDisplayReportInLHN({ report, @@ -485,7 +488,7 @@ function updateReportsToDisplayInLHN({ draftComment: reportDraftComment, transactions, isOffline, - isReportArchived: isArchivedReport(reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`] ?? {}), + isReportArchived, reportAttributes, currentUserLogin, currentUserAccountID, @@ -493,16 +496,19 @@ function updateReportsToDisplayInLHN({ if (shouldDisplay) { const requiresAttention = reportAttributes?.[report?.reportID]?.requiresAttention ?? false; - const hasAttentionOrError = requiresAttention || hasErrorsOtherThanFailedReceipt; + const isUnreadReport = getIsUnreadReportForInboxTab(report, isReportArchived); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const hasFlags = requiresAttention || hasErrorsOtherThanFailedReceipt || isUnreadReport; const existingEntry = displayedReports[reportID]; - if (hasAttentionOrError) { + if (hasFlags) { if ( existingEntry !== report || existingEntry?.requiresAttention !== requiresAttention || - existingEntry?.hasErrorsOtherThanFailedReceipt !== hasErrorsOtherThanFailedReceipt + existingEntry?.hasErrorsOtherThanFailedReceipt !== hasErrorsOtherThanFailedReceipt || + existingEntry?.isUnreadReport !== isUnreadReport ) { - getMutableCopy()[reportID] = {...report, requiresAttention, hasErrorsOtherThanFailedReceipt}; + getMutableCopy()[reportID] = {...report, requiresAttention, hasErrorsOtherThanFailedReceipt, isUnreadReport}; } } else if (existingEntry !== report) { getMutableCopy()[reportID] = report; @@ -1486,12 +1492,15 @@ function getRoomWelcomeMessage( * The "All" tab returns everything (and still honors Most Recent / Focus mode upstream); the other * tabs narrow that same set to unread, expense-related, or direct-message reports. */ -function filterReportsForInboxTab( - reportIDs: string[], - reportsToDisplay: ReportsToDisplayInLHN, - activeTab: ValueOf, - reportNameValuePairs?: OnyxCollection, -): string[] { +/** + * Whether a report should appear in the "Unread" Inbox tab: it has unread messages and is not muted. + * Computed once while building the LHN report set (which is cached/incremental) so the tab filter only reads a flag. + */ +function getIsUnreadReportForInboxTab(report: Report, isReportArchived: boolean): boolean { + return isUnread(report, undefined, isReportArchived) && getReportNotificationPreference(report) !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE; +} + +function filterReportsForInboxTab(reportIDs: string[], reportsToDisplay: ReportsToDisplayInLHN, activeTab: ValueOf): string[] { if (activeTab === CONST.INBOX_TAB.ALL) { return reportIDs; } @@ -1506,10 +1515,8 @@ function filterReportsForInboxTab( case CONST.INBOX_TAB.TODO: // Reports with an outstanding GBR (requiresAttention) or RBR (errors) require the user's action. return !!report.requiresAttention || !!report.hasErrorsOtherThanFailedReceipt; - case CONST.INBOX_TAB.UNREAD: { - const isReportArchived = isArchivedReport(reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`]); - return isUnread(report, undefined, isReportArchived) && getReportNotificationPreference(report) !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE; - } + case CONST.INBOX_TAB.UNREAD: + return !!report.isUnreadReport; default: return true; } From a45d7d47d44948185454d10ed3c57cbc5f81241a Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Sun, 31 May 2026 00:00:32 +0000 Subject: [PATCH 7/7] Fix checks --- src/styles/index.ts | 25 ++++++++++----------- tests/unit/useSidebarOrderedReportsTest.tsx | 1 + 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index 296ade6c010f..1bbd24d798ee 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4478,6 +4478,11 @@ const staticStyles = (theme: ThemeColors) => paddingBottom: 4, }, + tabTextSmall: { + fontSize: variables.fontSizeSmall, + lineHeight: 16, + }, + scrollableTabSelector: { flexGrow: 0, }, @@ -6351,19 +6356,13 @@ const dynamicStyles = (theme: ThemeColors) => top: fileTopPosition, }) satisfies ViewStyle, - tabText: (isSelected: boolean, hasIcon = false) => - ({ - marginLeft: hasIcon ? 8 : 0, - ...FontUtils.fontFamily.platform.EXP_NEUE_BOLD, - color: isSelected ? theme.text : theme.textSupporting, - lineHeight: variables.lineHeightLarge, - fontSize: variables.fontSizeLabel, - }) satisfies TextStyle, - - tabTextSmall: { - fontSize: variables.fontSizeSmall, - lineHeight: 16, - } satisfies TextStyle, + tabText: (isSelected: boolean, hasIcon = false): TextStyle => ({ + marginLeft: hasIcon ? 8 : 0, + ...FontUtils.fontFamily.platform.EXP_NEUE_BOLD, + color: isSelected ? theme.text : theme.textSupporting, + lineHeight: variables.lineHeightLarge, + fontSize: variables.fontSizeLabel, + }), tabBackground: (hovered: boolean, isFocused: boolean, isDisabled: boolean, background: string | Animated.AnimatedInterpolation) => { if (isDisabled) { diff --git a/tests/unit/useSidebarOrderedReportsTest.tsx b/tests/unit/useSidebarOrderedReportsTest.tsx index 442bd868d9d9..16da3a2ad99a 100644 --- a/tests/unit/useSidebarOrderedReportsTest.tsx +++ b/tests/unit/useSidebarOrderedReportsTest.tsx @@ -16,6 +16,7 @@ jest.mock('@libs/SidebarUtils', () => ({ sortReportsToDisplayInLHN: jest.fn(), getReportsToDisplayInLHN: jest.fn(), updateReportsToDisplayInLHN: jest.fn(), + filterReportsForInboxTab: jest.fn((reportIDs: string[]) => reportIDs), })); jest.mock('@libs/Navigation/Navigation', () => ({ getActiveRouteWithoutParams: jest.fn(() => ''),