From 1e8e0adcf9d01d2ad0ce01877f50db0f54be30eb Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 30 Oct 2023 13:54:25 -1000 Subject: [PATCH 01/39] Add focus mode upgrade modal --- src/CONST.ts | 1 + src/Expensify.js | 6 +++ src/ONYXKEYS.ts | 8 ++++ .../FocusModeUpgradeRequestModal.js | 44 +++++++++++++++++ src/languages/en.ts | 10 ++++ .../Navigation/AppNavigator/AuthScreens.js | 3 ++ src/libs/actions/App.js | 6 +++ src/libs/actions/Report.js | 47 +++++++++++++++++++ src/libs/actions/User.js | 13 ++++- 9 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 src/components/FocusModeUpgradeRequestModal.js diff --git a/src/CONST.ts b/src/CONST.ts index 8507072da5c8..a5b8c18505c8 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -492,6 +492,7 @@ const CONST = { MAX_REPORT_PREVIEW_RECEIPTS: 3, }, REPORT: { + MAX_COUNT_BEFORE_FOCUS_PROMOTION: 30, MAXIMUM_PARTICIPANTS: 8, SPLIT_REPORTID: '-2', ACTIONS: { diff --git a/src/Expensify.js b/src/Expensify.js index b7e3f0f60567..15640d152a64 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -7,6 +7,7 @@ import _ from 'underscore'; import ConfirmModal from './components/ConfirmModal'; import DeeplinkWrapper from './components/DeeplinkWrapper'; import EmojiPicker from './components/EmojiPicker/EmojiPicker'; +import FocusModeUpgradeRequestModal from './components/FocusModeUpgradeRequestModal'; import GrowlNotification from './components/GrowlNotification'; import AppleAuthWrapper from './components/SignInButtons/AppleAuthWrapper'; import SplashScreenHider from './components/SplashScreenHider'; @@ -211,6 +212,7 @@ function Expensify(props) { isVisible /> ) : null} + {props.focusModeRequest ? : null} )} @@ -249,5 +251,9 @@ export default compose( screenShareRequest: { key: ONYXKEYS.SCREEN_SHARE_REQUEST, }, + focusModeRequest: { + key: ONYXKEYS.FOCUS_MODE_UPGRADE_REQUEST, + initWithStoredValues: false, + }, }), )(Expensify); diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 9cd43badac6b..0bcb574366cb 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -149,6 +149,12 @@ const ONYXKEYS = { /** The user's cash card and imported cards (including the Expensify Card) */ CARD_LIST: 'cardList', + /** When the user was last asked to upgrade to focus mode */ + NVP_LAST_OFFERED_FOCUS_MODE: 'lastOfferedFocusMode', + + /** Boolean flag used to display the focus mode upgrade modal */ + FOCUS_MODE_UPGRADE_REQUEST: 'focusModeUpgradeRequest', + /** Stores information about the user's saved statements */ WALLET_STATEMENT: 'walletStatement', @@ -342,6 +348,8 @@ type OnyxValues = { [ONYXKEYS.NVP_PRIORITY_MODE]: ValueOf; [ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE]: OnyxTypes.BlockedFromConcierge; [ONYXKEYS.NVP_PRIVATE_PUSH_NOTIFICATION_ID]: string; + [ONYXKEYS.FOCUS_MODE_UPGRADE_REQUEST_LOGIN]: string; + [ONYXKEYS.FOCUS_MODE_UPGRADE_REQUEST]: boolean; [ONYXKEYS.NVP_LAST_PAYMENT_METHOD]: Record; [ONYXKEYS.NVP_RECENT_WAYPOINTS]: OnyxTypes.RecentWaypoint[]; [ONYXKEYS.PUSH_NOTIFICATIONS_ENABLED]: boolean; diff --git a/src/components/FocusModeUpgradeRequestModal.js b/src/components/FocusModeUpgradeRequestModal.js new file mode 100644 index 000000000000..67cf1925f17c --- /dev/null +++ b/src/components/FocusModeUpgradeRequestModal.js @@ -0,0 +1,44 @@ +import React, {useState} from 'react'; +import ConfirmModal from './ConfirmModal'; +import CONST from '../CONST'; +import * as User from '../libs/actions/User'; +import useLocalize from '@hooks/useLocalize'; + +const FocusModeUpgradeModal = () => { + const {translate} = useLocalize(); + const [response, setResponse] = useState(null); + return ( + <> + {response === null && ( + { + User.updateChatPriorityMode(CONST.PRIORITY_MODE.GSD, false); + setResponse(true); + }} + onCancel={() => { + setResponse(false); + }} + prompt={translate('focusModeUpgradeModal.prompt')} + confirmText={translate('focusModeUpgradeModal.enable')} + cancelText={translate('focusModeUpgradeModal.noEnable')} + isVisible + /> + )} + {(response !== null) && ( + + )} + + ) +} + +FocusModeUpgradeModal.displayName = 'FocusModeUpgradeModal'; +export default FocusModeUpgradeModal; diff --git a/src/languages/en.ts b/src/languages/en.ts index 7cdce062faac..59eeabe04fc5 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1099,6 +1099,16 @@ export default { year: 'Year', selectYear: 'Please select a year', }, + focusModeUpgradeModal: { + title: 'Upgrade to #focus mode', + prompt: 'Do you want to hide your read messages so you can focus?', + enable: 'Enable #focus mode', + noEnable: 'Not now', + enabled: 'Read messages will be hidden, unless they have a green dot, which means it\'s waiting on you. You can change this in your account settings.', + notEnabled: 'If you ever change your mind and want to hide your read messages, enable #focus mode in account settings.', + ok: 'Ok!', + gotItThanks: 'Got it, thanks!', + }, notFound: { chatYouLookingForCannotBeFound: 'The chat you are looking for cannot be found.', getMeOutOfHere: 'Get me out of here', diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 34837135d22d..11c31bfb1da4 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -193,6 +193,9 @@ function AuthScreens({isUsingMemoryOnlyKeys, lastUpdateIDAppliedToClient, sessio App.reconnectApp(lastUpdateIDAppliedToClient); } + // Promote #focus mode upgrade + Report.promoteFocusModeUpgrade(); + App.setUpPoliciesAndNavigate(session); App.redirectThirdPartyDesktopSignIn(); diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 884dddf0eb0a..8826dfc41a67 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -53,6 +53,12 @@ Onyx.connect({ // eslint-disable-next-line no-use-before-define openApp(); } + + // If user switches from Most recent to #focus let's drop all the reports they don't need to free up memory and improve performance. + if (nextPriorityMode === CONST.PRIORITY_MODE.GSD && priorityMode === CONST.PRIORITY_MODE.DEFAULT) { + console.log('@marcaaron focus mode enabled'); + } + priorityMode = nextPriorityMode; }, }); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 3f7dc76b174d..845ff6a4625f 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -101,6 +101,19 @@ Onyx.connect({ }, }); +let isInFocusMode = false; +Onyx.connect({ + key: ONYXKEYS.NVP_PRIORITY_MODE, + callback: (priorityMode) => isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD, +}); + +// In order to tell if the user has been asked to upgrade for their current device we will set an Onyx key with the current device login +let lastOfferedFocusMode = ''; +Onyx.connect({ + key: ONYXKEYS.NVP_LAST_OFFERED_FOCUS_MODE, + callback: val => focusModeUpgradeRequestLogin = val, +}); + const allReports = {}; let conciergeChatReportID; const typingWatchTimers = {}; @@ -1528,6 +1541,39 @@ function navigateToConciergeChat(ignoreConciergeReportID = false) { } } +function promoteFocusModeUpgrade() { + Welcome.serverDataIsReadyPromise().then(() => { + // Check to see if the user is using #focus mode or if we have already asked them to upgrade on any devices. + if (isInFocusMode || lastOfferedFocusMode !== '') { + Log.info('Not offering to promote user to optimized focus mode.', false, {isInFocusMode, lastOfferedFocusMode}); + return; + } + + const reportCount = Object.keys(allReports).length; + if (reportCount < CONST.REPORT.MAX_COUNT_BEFORE_FOCUS_PROMOTION) { + Log.info('Not offering to promote user to optimized focus mode as they do not have many reports', false, {reportCount}); + return; + } + + Log.info('Offering to promote user to focus mode', false, {reportCount}); + + // Record that we asked them to upgrade so we don't ask them again later. + const newLastOfferedTime = DateUtils.getDBTime(); + API.write('SetLastOfferedFocusMode', {value: newLastOfferedTime}, { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.NVP_LAST_OFFERED_FOCUS_MODE, + value: newLastOfferedTime, + }, + ], + }); + + // Trigger modal to display + Onyx.set(ONYXKEYS.FOCUS_MODE_UPGRADE_REQUEST, true); + }); +} + /** * Add a policy report (workspace room) optimistically and navigate to it. * @@ -2540,6 +2586,7 @@ export { getCurrentUserAccountID, setLastOpenedPublicRoom, flagComment, + promoteFocusModeUpgrade, openLastOpenedPublicRoom, updatePrivateNotes, getReportPrivateNote, diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index f7375a5583a6..f6298cd5bb09 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -584,8 +584,9 @@ function updateFrequentlyUsedEmojis(frequentlyUsedEmojis) { /** * Sync user chat priority mode with Onyx and Server * @param {String} mode + * @param {boolean} [shouldNavigate] */ -function updateChatPriorityMode(mode) { +function updateChatPriorityMode(mode, shouldNavigate = true) { const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -600,7 +601,14 @@ function updateChatPriorityMode(mode) { }, {optimisticData}, ); - Navigation.goBack(ROUTES.SETTINGS_PREFERENCES); + + if (shouldNavigate) { + Navigation.goBack(ROUTES.SETTINGS_PREFERENCES); + } +} + +function clearOfferFocusModeUpgrade() { + Onyx.set(ONYXKEYS.FOCUS_MODE_UPGRADE_REQUEST, false); } /** @@ -844,6 +852,7 @@ function clearDraftCustomStatus() { } export { + clearOfferFocusModeUpgrade, closeAccount, resendValidateCode, requestContactMethodValidateCode, From 1e9e892dacbc60e1746d8583cf21433e66d4291f Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 30 Oct 2023 13:55:09 -1000 Subject: [PATCH 02/39] remove unused key fro Onyx --- src/ONYXKEYS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 0bcb574366cb..2a22f84bdac6 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -348,7 +348,7 @@ type OnyxValues = { [ONYXKEYS.NVP_PRIORITY_MODE]: ValueOf; [ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE]: OnyxTypes.BlockedFromConcierge; [ONYXKEYS.NVP_PRIVATE_PUSH_NOTIFICATION_ID]: string; - [ONYXKEYS.FOCUS_MODE_UPGRADE_REQUEST_LOGIN]: string; + [ONYXKEYS.NVP_LAST_OFFERED_FOCUS_MODE]: string; [ONYXKEYS.FOCUS_MODE_UPGRADE_REQUEST]: boolean; [ONYXKEYS.NVP_LAST_PAYMENT_METHOD]: Record; [ONYXKEYS.NVP_RECENT_WAYPOINTS]: OnyxTypes.RecentWaypoint[]; From e091586caa413abb324d3d818e45b21d476ae445 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 30 Oct 2023 15:15:26 -1000 Subject: [PATCH 03/39] Free up some reports when switching to focus mode --- .../FocusModeUpgradeRequestModal.js | 10 +-- src/languages/en.ts | 2 +- src/libs/Navigation/Navigation.js | 29 +++++++++ src/libs/actions/App.js | 5 -- src/libs/actions/Report.js | 63 +++++++++++++++---- src/pages/home/sidebar/SidebarLinksData.js | 12 +++- src/types/onyx/Policy.ts | 3 + 7 files changed, 101 insertions(+), 23 deletions(-) diff --git a/src/components/FocusModeUpgradeRequestModal.js b/src/components/FocusModeUpgradeRequestModal.js index 67cf1925f17c..711889bbe162 100644 --- a/src/components/FocusModeUpgradeRequestModal.js +++ b/src/components/FocusModeUpgradeRequestModal.js @@ -1,8 +1,8 @@ import React, {useState} from 'react'; -import ConfirmModal from './ConfirmModal'; +import useLocalize from '@hooks/useLocalize'; import CONST from '../CONST'; import * as User from '../libs/actions/User'; -import useLocalize from '@hooks/useLocalize'; +import ConfirmModal from './ConfirmModal'; const FocusModeUpgradeModal = () => { const {translate} = useLocalize(); @@ -25,7 +25,7 @@ const FocusModeUpgradeModal = () => { isVisible /> )} - {(response !== null) && ( + {response !== null && ( { /> )} - ) -} + ); +}; FocusModeUpgradeModal.displayName = 'FocusModeUpgradeModal'; export default FocusModeUpgradeModal; diff --git a/src/languages/en.ts b/src/languages/en.ts index 59eeabe04fc5..3487ddbca897 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1104,7 +1104,7 @@ export default { prompt: 'Do you want to hide your read messages so you can focus?', enable: 'Enable #focus mode', noEnable: 'Not now', - enabled: 'Read messages will be hidden, unless they have a green dot, which means it\'s waiting on you. You can change this in your account settings.', + enabled: "Read messages will be hidden, unless they have a green dot, which means it's waiting on you. You can change this in your account settings.", notEnabled: 'If you ever change your mind and want to hide your read messages, enable #focus mode in account settings.', ok: 'Ok!', gotItThanks: 'Got it, thanks!', diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index ae13e2b07206..87f4cf2e91b2 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -262,6 +262,34 @@ function setIsNavigationReady() { resolveNavigationIsReadyPromise(); } +/** + * + * @param {Array} routes + * @param {Set} reportIDs + * @returns {String[]} + */ +function findReportIDsInRoutes(routes, reportIDs = new Set()) { + routes.forEach((route) => { + if (route.params && route.params.reportID) { + reportIDs.add(route.params.reportID); + } + + if (route.state) { + findReportIDsInRoutes(route.state.routes, reportIDs); + } + }); + + return [...reportIDs.values()]; +} + +/** + * @returns {String[]} + */ +function getAllReportIDsInNavigationStack() { + const rootState = navigationRef.getRootState(); + return findReportIDsInRoutes(rootState.routes); +} + export default { setShouldPopAllStateOnUP, canNavigate, @@ -277,6 +305,7 @@ export default { getRouteNameFromStateEvent, getTopMostCentralPaneRouteName, getTopmostReportActionId, + getAllReportIDsInNavigationStack, }; export {navigationRef}; diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 8826dfc41a67..e4265aa6ccd0 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -54,11 +54,6 @@ Onyx.connect({ openApp(); } - // If user switches from Most recent to #focus let's drop all the reports they don't need to free up memory and improve performance. - if (nextPriorityMode === CONST.PRIORITY_MODE.GSD && priorityMode === CONST.PRIORITY_MODE.DEFAULT) { - console.log('@marcaaron focus mode enabled'); - } - priorityMode = nextPriorityMode; }, }); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 845ff6a4625f..eb93e65555a7 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -88,6 +88,20 @@ Onyx.connect({ }, }); +let activePolicyID = ''; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.POLICY, + waitForCollectionCallback: true, + callback: (val) => { + const activePolicy = _.find(val, (policy) => policy.active); + if (!activePolicy) { + return; + } + + activePolicyID = activePolicy.id; + }, +}); + const draftNoteMap = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT, @@ -104,14 +118,14 @@ Onyx.connect({ let isInFocusMode = false; Onyx.connect({ key: ONYXKEYS.NVP_PRIORITY_MODE, - callback: (priorityMode) => isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD, + callback: (priorityMode) => (isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD), }); // In order to tell if the user has been asked to upgrade for their current device we will set an Onyx key with the current device login let lastOfferedFocusMode = ''; Onyx.connect({ key: ONYXKEYS.NVP_LAST_OFFERED_FOCUS_MODE, - callback: val => focusModeUpgradeRequestLogin = val, + callback: (val) => (lastOfferedFocusMode = val), }); const allReports = {}; @@ -1559,15 +1573,19 @@ function promoteFocusModeUpgrade() { // Record that we asked them to upgrade so we don't ask them again later. const newLastOfferedTime = DateUtils.getDBTime(); - API.write('SetLastOfferedFocusMode', {value: newLastOfferedTime}, { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.SET, - key: ONYXKEYS.NVP_LAST_OFFERED_FOCUS_MODE, - value: newLastOfferedTime, - }, - ], - }); + API.write( + 'SetLastOfferedFocusMode', + {value: newLastOfferedTime}, + { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.NVP_LAST_OFFERED_FOCUS_MODE, + value: newLastOfferedTime, + }, + ], + }, + ); // Trigger modal to display Onyx.set(ONYXKEYS.FOCUS_MODE_UPGRADE_REQUEST, true); @@ -1703,6 +1721,28 @@ function deleteReport(reportID) { } } +/** + * @param {string[]} lhnReportIDs + */ +function deleteUnusedReports(lhnReportIDs) { + let reportIDsToDelete = _.filter(allReports, (report) => report.reportID).map((report) => report.reportID); + + // We won't ever delete anything currently in the LHN list of reports + reportIDsToDelete = _.difference(reportIDsToDelete, lhnReportIDs); + + // We also should avoid deleting anything in the navigation stack if we can + reportIDsToDelete = _.difference(reportIDsToDelete, Navigation.getAllReportIDsInNavigationStack()); + + // And we don't want to mess with the workspace chats related to a user's primary policy + const ownWorkspaceChatOnPrimaryPolicy = _.find(allReports, (report) => report.isOwnPolicyExpenseChat && report.policyID === activePolicyID); + if (ownWorkspaceChatOnPrimaryPolicy && ownWorkspaceChatOnPrimaryPolicy.reportID) { + reportIDsToDelete = _.difference(reportIDsToDelete, [ownWorkspaceChatOnPrimaryPolicy.reportID]); + } + + Log.info(`Deleting ${reportIDsToDelete.length} cached reports because we switched to focus mode.`); + reportIDsToDelete.forEach(reportID => deleteReport(reportID)); +} + /** * @param {String} reportID The reportID of the policy report (workspace room) */ @@ -2597,4 +2637,5 @@ export { openRoomMembersPage, savePrivateNotesDraft, getDraftPrivateNote, + deleteUnusedReports, }; diff --git a/src/pages/home/sidebar/SidebarLinksData.js b/src/pages/home/sidebar/SidebarLinksData.js index a5d58768a95d..87494d3de223 100644 --- a/src/pages/home/sidebar/SidebarLinksData.js +++ b/src/pages/home/sidebar/SidebarLinksData.js @@ -1,13 +1,15 @@ import {deepEqual} from 'fast-equals'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {useCallback, useMemo, useRef} from 'react'; +import React, {useCallback, useEffect, useMemo, useRef} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import withCurrentReportID from '@components/withCurrentReportID'; import withNavigationFocus from '@components/withNavigationFocus'; import useLocalize from '@hooks/useLocalize'; +import usePrevious from '@hooks/usePrevious'; +import * as Report from '@libs/actions/Report'; import compose from '@libs/compose'; import * as SessionUtils from '@libs/SessionUtils'; import SidebarUtils from '@libs/SidebarUtils'; @@ -68,6 +70,7 @@ function SidebarLinksData({isFocused, allReportActions, betas, chatReports, curr const reportIDsRef = useRef(null); const isLoading = SessionUtils.didUserLogInDuringSession() && isLoadingReportData; + const previousPriorityMode = usePrevious(priorityMode); const optionListItems = useMemo(() => { const reportIDs = SidebarUtils.getOrderedReportIDs(null, chatReports, betas, policies, priorityMode, allReportActions); if (deepEqual(reportIDsRef.current, reportIDs)) { @@ -81,6 +84,13 @@ function SidebarLinksData({isFocused, allReportActions, betas, chatReports, curr return reportIDsRef.current || []; }, [allReportActions, betas, chatReports, policies, priorityMode, isLoading]); + useEffect(() => { + // If we are switching from "Most recent" to "#focus" then let's try to free up some unused reports to improve the app performance + if (previousPriorityMode === CONST.PRIORITY_MODE.DEFAULT && priorityMode === CONST.PRIORITY_MODE.GSD) { + Report.deleteUnusedReports(optionListItems); + } + }, [priorityMode, optionListItems]); + // 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 // we first generate the list as if there was no current report, then here we check if diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 6b115bf0609f..e094e289daf4 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -3,6 +3,9 @@ import CONST from '@src/CONST'; import * as OnyxCommon from './OnyxCommon'; type Policy = { + /** Whether this is the user's active/primary policy */ + active: boolean; + /** The ID of the policy */ id: string; From 3e12bb5034cc2783b3adcc894bcc383be7c9c3dd Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 30 Oct 2023 15:17:56 -1000 Subject: [PATCH 04/39] remove unneeded line --- src/libs/actions/App.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index e4265aa6ccd0..884dddf0eb0a 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -53,7 +53,6 @@ Onyx.connect({ // eslint-disable-next-line no-use-before-define openApp(); } - priorityMode = nextPriorityMode; }, }); From 5b6d499894f1e8d31d55f3b6e59f65853264ff44 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 30 Oct 2023 15:31:30 -1000 Subject: [PATCH 05/39] Get rid of the underscore dangle --- src/libs/actions/Report.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index eb93e65555a7..8c90fce8000e 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1041,6 +1041,11 @@ function broadcastUserIsLeavingRoom(reportID) { Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, leavingStatus); } +/** + * Debounce the prompt to promote focus mode as many reports updates could happen in a short burst + */ +const promoteFocusModeUpgrade = _.debounce(tryFocusModeUpgrade, 300, true); + /** * When a report changes in Onyx, this fetches the report from the API if the report doesn't have a name * @@ -1078,6 +1083,9 @@ function handleReportChanged(report) { if (report.reportID && report.reportName === undefined) { reconnect(report.reportID); } + + // Check if we should show a prompt and offer the user to switch to focus mode + promoteFocusModeUpgrade(); } Onyx.connect({ @@ -1555,7 +1563,7 @@ function navigateToConciergeChat(ignoreConciergeReportID = false) { } } -function promoteFocusModeUpgrade() { +function tryFocusModeUpgrade() { Welcome.serverDataIsReadyPromise().then(() => { // Check to see if the user is using #focus mode or if we have already asked them to upgrade on any devices. if (isInFocusMode || lastOfferedFocusMode !== '') { @@ -1572,7 +1580,7 @@ function promoteFocusModeUpgrade() { Log.info('Offering to promote user to focus mode', false, {reportCount}); // Record that we asked them to upgrade so we don't ask them again later. - const newLastOfferedTime = DateUtils.getDBTime(); + lastOfferedFocusMode = DateUtils.getDBTime(); API.write( 'SetLastOfferedFocusMode', {value: newLastOfferedTime}, @@ -1581,7 +1589,7 @@ function promoteFocusModeUpgrade() { { onyxMethod: Onyx.METHOD.SET, key: ONYXKEYS.NVP_LAST_OFFERED_FOCUS_MODE, - value: newLastOfferedTime, + value: lastOfferedFocusMode, }, ], }, From 8340d366f89d28540d7d73fea170f80dbd9b1245 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Mon, 30 Oct 2023 16:16:31 -1000 Subject: [PATCH 06/39] Use correct variable name --- src/libs/actions/Report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 8c90fce8000e..ed4ffa4496c7 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1583,7 +1583,7 @@ function tryFocusModeUpgrade() { lastOfferedFocusMode = DateUtils.getDBTime(); API.write( 'SetLastOfferedFocusMode', - {value: newLastOfferedTime}, + {value: lastOfferedFocusMode}, { optimisticData: [ { From 71ca1b9ecc156983137fe148ac7bb3125b3bed13 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 12:13:33 -1000 Subject: [PATCH 07/39] Simplify logic --- src/Expensify.js | 16 +++++-- src/ONYXKEYS.ts | 12 ++--- .../FocusModeUpgradeRequestModal.js | 44 ------------------- src/languages/en.ts | 12 ++--- src/libs/actions/Report.js | 37 +++++----------- src/libs/actions/User.js | 15 +++++-- 6 files changed, 45 insertions(+), 91 deletions(-) delete mode 100644 src/components/FocusModeUpgradeRequestModal.js diff --git a/src/Expensify.js b/src/Expensify.js index 15640d152a64..0c1f349117ed 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -7,7 +7,6 @@ import _ from 'underscore'; import ConfirmModal from './components/ConfirmModal'; import DeeplinkWrapper from './components/DeeplinkWrapper'; import EmojiPicker from './components/EmojiPicker/EmojiPicker'; -import FocusModeUpgradeRequestModal from './components/FocusModeUpgradeRequestModal'; import GrowlNotification from './components/GrowlNotification'; import AppleAuthWrapper from './components/SignInButtons/AppleAuthWrapper'; import SplashScreenHider from './components/SplashScreenHider'; @@ -212,7 +211,16 @@ function Expensify(props) { isVisible /> ) : null} - {props.focusModeRequest ? : null} + {props.focusModeRequest ? ( + + ) : null} )} @@ -251,8 +259,8 @@ export default compose( screenShareRequest: { key: ONYXKEYS.SCREEN_SHARE_REQUEST, }, - focusModeRequest: { - key: ONYXKEYS.FOCUS_MODE_UPGRADE_REQUEST, + focusModeNotification: { + key: ONYXKEYS.FOCUS_MODE_NOTIFICATION, initWithStoredValues: false, }, }), diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 2a22f84bdac6..bf89011322b7 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -149,11 +149,11 @@ const ONYXKEYS = { /** The user's cash card and imported cards (including the Expensify Card) */ CARD_LIST: 'cardList', - /** When the user was last asked to upgrade to focus mode */ - NVP_LAST_OFFERED_FOCUS_MODE: 'lastOfferedFocusMode', + /** Whether the user has tried focus mode yet */ + NVP_HAS_TRIED_FOCUS_MODE: 'hasTriedFocusMode', - /** Boolean flag used to display the focus mode upgrade modal */ - FOCUS_MODE_UPGRADE_REQUEST: 'focusModeUpgradeRequest', + /** Boolean flag used to display the focus mode notification */ + FOCUS_MODE_NOTIFICATION: 'focusModeNotification', /** Stores information about the user's saved statements */ WALLET_STATEMENT: 'walletStatement', @@ -348,8 +348,8 @@ type OnyxValues = { [ONYXKEYS.NVP_PRIORITY_MODE]: ValueOf; [ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE]: OnyxTypes.BlockedFromConcierge; [ONYXKEYS.NVP_PRIVATE_PUSH_NOTIFICATION_ID]: string; - [ONYXKEYS.NVP_LAST_OFFERED_FOCUS_MODE]: string; - [ONYXKEYS.FOCUS_MODE_UPGRADE_REQUEST]: boolean; + [ONYXKEYS.NVP_HAS_TRIED_FOCUS_MODE]: boolean; + [ONYXKEYS.FOCUS_MODE_NOTIFICATION]: boolean; [ONYXKEYS.NVP_LAST_PAYMENT_METHOD]: Record; [ONYXKEYS.NVP_RECENT_WAYPOINTS]: OnyxTypes.RecentWaypoint[]; [ONYXKEYS.PUSH_NOTIFICATIONS_ENABLED]: boolean; diff --git a/src/components/FocusModeUpgradeRequestModal.js b/src/components/FocusModeUpgradeRequestModal.js deleted file mode 100644 index 711889bbe162..000000000000 --- a/src/components/FocusModeUpgradeRequestModal.js +++ /dev/null @@ -1,44 +0,0 @@ -import React, {useState} from 'react'; -import useLocalize from '@hooks/useLocalize'; -import CONST from '../CONST'; -import * as User from '../libs/actions/User'; -import ConfirmModal from './ConfirmModal'; - -const FocusModeUpgradeModal = () => { - const {translate} = useLocalize(); - const [response, setResponse] = useState(null); - return ( - <> - {response === null && ( - { - User.updateChatPriorityMode(CONST.PRIORITY_MODE.GSD, false); - setResponse(true); - }} - onCancel={() => { - setResponse(false); - }} - prompt={translate('focusModeUpgradeModal.prompt')} - confirmText={translate('focusModeUpgradeModal.enable')} - cancelText={translate('focusModeUpgradeModal.noEnable')} - isVisible - /> - )} - {response !== null && ( - - )} - - ); -}; - -FocusModeUpgradeModal.displayName = 'FocusModeUpgradeModal'; -export default FocusModeUpgradeModal; diff --git a/src/languages/en.ts b/src/languages/en.ts index 3487ddbca897..593e287e079c 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1099,15 +1099,9 @@ export default { year: 'Year', selectYear: 'Please select a year', }, - focusModeUpgradeModal: { - title: 'Upgrade to #focus mode', - prompt: 'Do you want to hide your read messages so you can focus?', - enable: 'Enable #focus mode', - noEnable: 'Not now', - enabled: "Read messages will be hidden, unless they have a green dot, which means it's waiting on you. You can change this in your account settings.", - notEnabled: 'If you ever change your mind and want to hide your read messages, enable #focus mode in account settings.', - ok: 'Ok!', - gotItThanks: 'Got it, thanks!', + focusModeUpdateModal: { + title: 'Welcome to #focus mode!', + prompt: 'Read messages will be hidden, unless they have a green dot, which means it\'s waiting on you. You can change this in your account settings.', }, notFound: { chatYouLookingForCannotBeFound: 'The chat you are looking for cannot be found.', diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index ed4ffa4496c7..86bf72545208 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -21,6 +21,7 @@ import * as Pusher from '@libs/Pusher/pusher'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as UserUtils from '@libs/UserUtils'; +import * as User from '@libs/actions/User'; import Visibility from '@libs/Visibility'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; @@ -122,10 +123,10 @@ Onyx.connect({ }); // In order to tell if the user has been asked to upgrade for their current device we will set an Onyx key with the current device login -let lastOfferedFocusMode = ''; +let hasTriedFocusMode = ''; Onyx.connect({ - key: ONYXKEYS.NVP_LAST_OFFERED_FOCUS_MODE, - callback: (val) => (lastOfferedFocusMode = val), + key: ONYXKEYS.NVP_HAS_TRIED_FOCUS_MODE, + callback: (val) => (hasTriedFocusMode = val), }); const allReports = {}; @@ -1566,37 +1567,23 @@ function navigateToConciergeChat(ignoreConciergeReportID = false) { function tryFocusModeUpgrade() { Welcome.serverDataIsReadyPromise().then(() => { // Check to see if the user is using #focus mode or if we have already asked them to upgrade on any devices. - if (isInFocusMode || lastOfferedFocusMode !== '') { - Log.info('Not offering to promote user to optimized focus mode.', false, {isInFocusMode, lastOfferedFocusMode}); + if (isInFocusMode || hasTriedFocusMode) { + Log.info('Not switching user to optimized focus mode.', false, {isInFocusMode, hasTriedFocusMode}); return; } - const reportCount = Object.keys(allReports).length; + const reportCount = _.keys(allReports).length; if (reportCount < CONST.REPORT.MAX_COUNT_BEFORE_FOCUS_PROMOTION) { - Log.info('Not offering to promote user to optimized focus mode as they do not have many reports', false, {reportCount}); + Log.info('Not switching user to optimized focus mode as they do not have enough reports', false, {reportCount}); return; } - Log.info('Offering to promote user to focus mode', false, {reportCount}); + Log.info('Switching user to optimized focus mode', false, {reportCount}); // Record that we asked them to upgrade so we don't ask them again later. - lastOfferedFocusMode = DateUtils.getDBTime(); - API.write( - 'SetLastOfferedFocusMode', - {value: lastOfferedFocusMode}, - { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.SET, - key: ONYXKEYS.NVP_LAST_OFFERED_FOCUS_MODE, - value: lastOfferedFocusMode, - }, - ], - }, - ); - - // Trigger modal to display - Onyx.set(ONYXKEYS.FOCUS_MODE_UPGRADE_REQUEST, true); + hasTriedFocusMode = true; + User.updateChatPriorityMode(CONST.PRIORITY_MODE.GSD, false); + Onyx.set(ONYXKEYS.FOCUS_MODE_NOTIFICATION, true); }); } diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index f6298cd5bb09..8d0be3d733ba 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -594,6 +594,15 @@ function updateChatPriorityMode(mode, shouldNavigate = true) { value: mode, }, ]; + + if (mode === CONST.PRIORITY_MODE.GSD) { + optimisticData.push({ + onyxMethod: Onyx.METHOD_MERGE, + key: ONYXKEYS.NVP_HAS_TRIED_FOCUS_MODE, + value: true, + }); + } + API.write( 'UpdateChatPriorityMode', { @@ -607,8 +616,8 @@ function updateChatPriorityMode(mode, shouldNavigate = true) { } } -function clearOfferFocusModeUpgrade() { - Onyx.set(ONYXKEYS.FOCUS_MODE_UPGRADE_REQUEST, false); +function clearFocusModeNotification() { + Onyx.set(ONYXKEYS.FOCUS_MODE_NOTIFICATION, false); } /** @@ -852,7 +861,7 @@ function clearDraftCustomStatus() { } export { - clearOfferFocusModeUpgrade, + clearFocusModeNotification, closeAccount, resendValidateCode, requestContactMethodValidateCode, From b37e92a150feecfc2dc22dd28ef803908990afb7 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 12:18:56 -1000 Subject: [PATCH 08/39] Update variable names --- src/libs/Navigation/AppNavigator/AuthScreens.js | 3 +-- src/libs/actions/Report.js | 15 ++++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 11c31bfb1da4..533393de78d4 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -193,8 +193,7 @@ function AuthScreens({isUsingMemoryOnlyKeys, lastUpdateIDAppliedToClient, sessio App.reconnectApp(lastUpdateIDAppliedToClient); } - // Promote #focus mode upgrade - Report.promoteFocusModeUpgrade(); + Report.tryFocusMode(); App.setUpPoliciesAndNavigate(session); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 86bf72545208..71e889d4e94d 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1045,7 +1045,8 @@ function broadcastUserIsLeavingRoom(reportID) { /** * Debounce the prompt to promote focus mode as many reports updates could happen in a short burst */ -const promoteFocusModeUpgrade = _.debounce(tryFocusModeUpgrade, 300, true); +// eslint-disable-next-line no-use-before-define +const tryFocusMode = _.debounce(tryFocusModeUpdate, 300, true); /** * When a report changes in Onyx, this fetches the report from the API if the report doesn't have a name @@ -1085,8 +1086,8 @@ function handleReportChanged(report) { reconnect(report.reportID); } - // Check if we should show a prompt and offer the user to switch to focus mode - promoteFocusModeUpgrade(); + // Try to switch the user to focus mode + tryFocusMode(); } Onyx.connect({ @@ -1564,7 +1565,7 @@ function navigateToConciergeChat(ignoreConciergeReportID = false) { } } -function tryFocusModeUpgrade() { +function tryFocusModeUpdate() { Welcome.serverDataIsReadyPromise().then(() => { // Check to see if the user is using #focus mode or if we have already asked them to upgrade on any devices. if (isInFocusMode || hasTriedFocusMode) { @@ -1573,7 +1574,7 @@ function tryFocusModeUpgrade() { } const reportCount = _.keys(allReports).length; - if (reportCount < CONST.REPORT.MAX_COUNT_BEFORE_FOCUS_PROMOTION) { + if (reportCount < CONST.REPORT.MAX_COUNT_BEFORE_FOCUS_UPDATE) { Log.info('Not switching user to optimized focus mode as they do not have enough reports', false, {reportCount}); return; } @@ -1720,7 +1721,7 @@ function deleteReport(reportID) { * @param {string[]} lhnReportIDs */ function deleteUnusedReports(lhnReportIDs) { - let reportIDsToDelete = _.filter(allReports, (report) => report.reportID).map((report) => report.reportID); + let reportIDsToDelete = _.chain(allReports).filter(allReports, (report) => report.reportID).map((report) => report.reportID).value(); // We won't ever delete anything currently in the LHN list of reports reportIDsToDelete = _.difference(reportIDsToDelete, lhnReportIDs); @@ -2621,7 +2622,7 @@ export { getCurrentUserAccountID, setLastOpenedPublicRoom, flagComment, - promoteFocusModeUpgrade, + tryFocusMode, openLastOpenedPublicRoom, updatePrivateNotes, getReportPrivateNote, From 2e57969de51cf5b2ab512e348962ff521017709b Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 15:16:49 -1000 Subject: [PATCH 09/39] Adding this solution so there is a reference to it but breaking it out into a different PR --- src/Expensify.js | 8 ++++---- src/libs/actions/Report.js | 12 ++++++++++-- src/libs/actions/User.js | 2 +- src/pages/home/sidebar/SidebarLinksData.js | 3 ++- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Expensify.js b/src/Expensify.js index 0c1f349117ed..b593cea3ecbf 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -211,11 +211,11 @@ function Expensify(props) { isVisible /> ) : null} - {props.focusModeRequest ? ( + {props.focusModeNotification ? ( report.reportID).map((report) => report.reportID).value(); + let reportIDsToDelete = _.map(allReports, (report) => report.reportID); // We won't ever delete anything currently in the LHN list of reports reportIDsToDelete = _.difference(reportIDsToDelete, lhnReportIDs); @@ -1736,7 +1736,15 @@ function deleteUnusedReports(lhnReportIDs) { } Log.info(`Deleting ${reportIDsToDelete.length} cached reports because we switched to focus mode.`); - reportIDsToDelete.forEach(reportID => deleteReport(reportID)); + + // We do this at some set delay because it will kill the UI if we try to delete many things at once. + const interval = setInterval(() => { + const reportID = reportIDsToDelete.pop(); + deleteReport(reportID); + if (reportIDsToDelete.length === 0) { + clearInterval(interval); + } + }, 500); } /** diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index 8d0be3d733ba..6ebf72cec5f4 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -597,7 +597,7 @@ function updateChatPriorityMode(mode, shouldNavigate = true) { if (mode === CONST.PRIORITY_MODE.GSD) { optimisticData.push({ - onyxMethod: Onyx.METHOD_MERGE, + onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.NVP_HAS_TRIED_FOCUS_MODE, value: true, }); diff --git a/src/pages/home/sidebar/SidebarLinksData.js b/src/pages/home/sidebar/SidebarLinksData.js index 87494d3de223..518c44c61aad 100644 --- a/src/pages/home/sidebar/SidebarLinksData.js +++ b/src/pages/home/sidebar/SidebarLinksData.js @@ -84,12 +84,13 @@ function SidebarLinksData({isFocused, allReportActions, betas, chatReports, curr return reportIDsRef.current || []; }, [allReportActions, betas, chatReports, policies, priorityMode, isLoading]); + // eslint-disable-next-line rulesdir/prefer-early-return useEffect(() => { // If we are switching from "Most recent" to "#focus" then let's try to free up some unused reports to improve the app performance if (previousPriorityMode === CONST.PRIORITY_MODE.DEFAULT && priorityMode === CONST.PRIORITY_MODE.GSD) { Report.deleteUnusedReports(optionListItems); } - }, [priorityMode, optionListItems]); + }, [priorityMode, optionListItems, previousPriorityMode]); // 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 From c219a3fe067e3b93998f514f8f7a5cd9b906959d Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 15:17:34 -1000 Subject: [PATCH 10/39] Remove logic to delete unused reports for now --- src/libs/actions/Report.js | 31 ---------------------- src/pages/home/sidebar/SidebarLinksData.js | 13 +-------- 2 files changed, 1 insertion(+), 43 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index e25cf4f7065b..aa8b1f3762a3 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1717,36 +1717,6 @@ function deleteReport(reportID) { } } -/** - * @param {string[]} lhnReportIDs - */ -function deleteUnusedReports(lhnReportIDs) { - let reportIDsToDelete = _.map(allReports, (report) => report.reportID); - - // We won't ever delete anything currently in the LHN list of reports - reportIDsToDelete = _.difference(reportIDsToDelete, lhnReportIDs); - - // We also should avoid deleting anything in the navigation stack if we can - reportIDsToDelete = _.difference(reportIDsToDelete, Navigation.getAllReportIDsInNavigationStack()); - - // And we don't want to mess with the workspace chats related to a user's primary policy - const ownWorkspaceChatOnPrimaryPolicy = _.find(allReports, (report) => report.isOwnPolicyExpenseChat && report.policyID === activePolicyID); - if (ownWorkspaceChatOnPrimaryPolicy && ownWorkspaceChatOnPrimaryPolicy.reportID) { - reportIDsToDelete = _.difference(reportIDsToDelete, [ownWorkspaceChatOnPrimaryPolicy.reportID]); - } - - Log.info(`Deleting ${reportIDsToDelete.length} cached reports because we switched to focus mode.`); - - // We do this at some set delay because it will kill the UI if we try to delete many things at once. - const interval = setInterval(() => { - const reportID = reportIDsToDelete.pop(); - deleteReport(reportID); - if (reportIDsToDelete.length === 0) { - clearInterval(interval); - } - }, 500); -} - /** * @param {String} reportID The reportID of the policy report (workspace room) */ @@ -2641,5 +2611,4 @@ export { openRoomMembersPage, savePrivateNotesDraft, getDraftPrivateNote, - deleteUnusedReports, }; diff --git a/src/pages/home/sidebar/SidebarLinksData.js b/src/pages/home/sidebar/SidebarLinksData.js index 518c44c61aad..a5d58768a95d 100644 --- a/src/pages/home/sidebar/SidebarLinksData.js +++ b/src/pages/home/sidebar/SidebarLinksData.js @@ -1,15 +1,13 @@ import {deepEqual} from 'fast-equals'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {useCallback, useEffect, useMemo, useRef} from 'react'; +import React, {useCallback, useMemo, useRef} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import withCurrentReportID from '@components/withCurrentReportID'; import withNavigationFocus from '@components/withNavigationFocus'; import useLocalize from '@hooks/useLocalize'; -import usePrevious from '@hooks/usePrevious'; -import * as Report from '@libs/actions/Report'; import compose from '@libs/compose'; import * as SessionUtils from '@libs/SessionUtils'; import SidebarUtils from '@libs/SidebarUtils'; @@ -70,7 +68,6 @@ function SidebarLinksData({isFocused, allReportActions, betas, chatReports, curr const reportIDsRef = useRef(null); const isLoading = SessionUtils.didUserLogInDuringSession() && isLoadingReportData; - const previousPriorityMode = usePrevious(priorityMode); const optionListItems = useMemo(() => { const reportIDs = SidebarUtils.getOrderedReportIDs(null, chatReports, betas, policies, priorityMode, allReportActions); if (deepEqual(reportIDsRef.current, reportIDs)) { @@ -84,14 +81,6 @@ function SidebarLinksData({isFocused, allReportActions, betas, chatReports, curr return reportIDsRef.current || []; }, [allReportActions, betas, chatReports, policies, priorityMode, isLoading]); - // eslint-disable-next-line rulesdir/prefer-early-return - useEffect(() => { - // If we are switching from "Most recent" to "#focus" then let's try to free up some unused reports to improve the app performance - if (previousPriorityMode === CONST.PRIORITY_MODE.DEFAULT && priorityMode === CONST.PRIORITY_MODE.GSD) { - Report.deleteUnusedReports(optionListItems); - } - }, [priorityMode, optionListItems, previousPriorityMode]); - // 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 // we first generate the list as if there was no current report, then here we check if From 9d70253a75b99817c395fa840f67dc52a6742030 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 15:24:13 -1000 Subject: [PATCH 11/39] Move call to set priority mode into modal --- src/Expensify.js | 9 +-------- src/components/FocusModeNotification.js | 25 +++++++++++++++++++++++++ src/libs/actions/Report.js | 16 ---------------- 3 files changed, 26 insertions(+), 24 deletions(-) create mode 100644 src/components/FocusModeNotification.js diff --git a/src/Expensify.js b/src/Expensify.js index b593cea3ecbf..3382dd136c4a 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -212,14 +212,7 @@ function Expensify(props) { /> ) : null} {props.focusModeNotification ? ( - + ) : null} )} diff --git a/src/components/FocusModeNotification.js b/src/components/FocusModeNotification.js new file mode 100644 index 000000000000..d12a7ee1be8a --- /dev/null +++ b/src/components/FocusModeNotification.js @@ -0,0 +1,25 @@ +import React, {useEffect} from 'react'; +import CONST from '@src/CONST'; +import useLocalize from '@hooks/useLocalize'; +import * as User from '@userActions/User'; +import ConfirmModal from './ConfirmModal'; + +function FocusModeNotification() { + const {translate} = useLocalize(); + useEffect(() => { + User.updateChatPriorityMode(CONST.PRIORITY_MODE.GSD, false); + }, []); + return ( + + ); +} + +FocusModeNotification.displayName = 'FocusModeNotification'; +export default FocusModeNotification; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index aa8b1f3762a3..bf049631c58b 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -21,7 +21,6 @@ import * as Pusher from '@libs/Pusher/pusher'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as UserUtils from '@libs/UserUtils'; -import * as User from '@libs/actions/User'; import Visibility from '@libs/Visibility'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; @@ -89,20 +88,6 @@ Onyx.connect({ }, }); -let activePolicyID = ''; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.POLICY, - waitForCollectionCallback: true, - callback: (val) => { - const activePolicy = _.find(val, (policy) => policy.active); - if (!activePolicy) { - return; - } - - activePolicyID = activePolicy.id; - }, -}); - const draftNoteMap = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT, @@ -1583,7 +1568,6 @@ function tryFocusModeUpdate() { // Record that we asked them to upgrade so we don't ask them again later. hasTriedFocusMode = true; - User.updateChatPriorityMode(CONST.PRIORITY_MODE.GSD, false); Onyx.set(ONYXKEYS.FOCUS_MODE_NOTIFICATION, true); }); } From 33af8959f75b64de8f2753679d6d31f4d0650070 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 15:29:26 -1000 Subject: [PATCH 12/39] Rename method. Remove unused logic --- src/CONST.ts | 2 +- .../Navigation/AppNavigator/AuthScreens.js | 2 +- src/libs/Navigation/Navigation.js | 29 ------------------- src/libs/actions/Report.js | 6 ++-- 4 files changed, 5 insertions(+), 34 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index a5b8c18505c8..c16e13288aac 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -492,7 +492,7 @@ const CONST = { MAX_REPORT_PREVIEW_RECEIPTS: 3, }, REPORT: { - MAX_COUNT_BEFORE_FOCUS_PROMOTION: 30, + MAX_COUNT_BEFORE_FOCUS_UPDATE: 30, MAXIMUM_PARTICIPANTS: 8, SPLIT_REPORTID: '-2', ACTIONS: { diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 533393de78d4..7f07d7f57aed 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -193,7 +193,7 @@ function AuthScreens({isUsingMemoryOnlyKeys, lastUpdateIDAppliedToClient, sessio App.reconnectApp(lastUpdateIDAppliedToClient); } - Report.tryFocusMode(); + Report.autoSwitchToFocusMode(); App.setUpPoliciesAndNavigate(session); diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index 87f4cf2e91b2..ae13e2b07206 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -262,34 +262,6 @@ function setIsNavigationReady() { resolveNavigationIsReadyPromise(); } -/** - * - * @param {Array} routes - * @param {Set} reportIDs - * @returns {String[]} - */ -function findReportIDsInRoutes(routes, reportIDs = new Set()) { - routes.forEach((route) => { - if (route.params && route.params.reportID) { - reportIDs.add(route.params.reportID); - } - - if (route.state) { - findReportIDsInRoutes(route.state.routes, reportIDs); - } - }); - - return [...reportIDs.values()]; -} - -/** - * @returns {String[]} - */ -function getAllReportIDsInNavigationStack() { - const rootState = navigationRef.getRootState(); - return findReportIDsInRoutes(rootState.routes); -} - export default { setShouldPopAllStateOnUP, canNavigate, @@ -305,7 +277,6 @@ export default { getRouteNameFromStateEvent, getTopMostCentralPaneRouteName, getTopmostReportActionId, - getAllReportIDsInNavigationStack, }; export {navigationRef}; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index bf049631c58b..46786601c959 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1031,7 +1031,7 @@ function broadcastUserIsLeavingRoom(reportID) { * Debounce the prompt to promote focus mode as many reports updates could happen in a short burst */ // eslint-disable-next-line no-use-before-define -const tryFocusMode = _.debounce(tryFocusModeUpdate, 300, true); +const autoSwitchToFocusMode = _.debounce(tryFocusModeUpdate, 300, true); /** * When a report changes in Onyx, this fetches the report from the API if the report doesn't have a name @@ -1072,7 +1072,7 @@ function handleReportChanged(report) { } // Try to switch the user to focus mode - tryFocusMode(); + autoSwitchToFocusMode(); } Onyx.connect({ @@ -2584,7 +2584,7 @@ export { getCurrentUserAccountID, setLastOpenedPublicRoom, flagComment, - tryFocusMode, + autoSwitchToFocusMode, openLastOpenedPublicRoom, updatePrivateNotes, getReportPrivateNote, From 901b1ba9a9a927a1c7a67c2daa93ccc0fd6988cb Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 15:30:48 -1000 Subject: [PATCH 13/39] Remove policy stuff --- src/types/onyx/Policy.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index e094e289daf4..6b115bf0609f 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -3,9 +3,6 @@ import CONST from '@src/CONST'; import * as OnyxCommon from './OnyxCommon'; type Policy = { - /** Whether this is the user's active/primary policy */ - active: boolean; - /** The ID of the policy */ id: string; From 3cbd44aa6fb5dc6be503c4f05dd74b5cd462dac1 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 15:54:32 -1000 Subject: [PATCH 14/39] Add missing import --- src/Expensify.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Expensify.js b/src/Expensify.js index 3382dd136c4a..5caabcb583ab 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -36,6 +36,7 @@ import Visibility from './libs/Visibility'; import ONYXKEYS from './ONYXKEYS'; import PopoverReportActionContextMenu from './pages/home/report/ContextMenu/PopoverReportActionContextMenu'; import * as ReportActionContextMenu from './pages/home/report/ContextMenu/ReportActionContextMenu'; +import FocusModeNotification from '@components/FocusModeNotification'; Onyx.registerLogger(({level, message}) => { if (level === 'alert') { From 63bf35e71a290b585b3d5c3a78294cc4c170b796 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 15:55:12 -1000 Subject: [PATCH 15/39] Run prettier --- src/Expensify.js | 6 ++---- src/components/FocusModeNotification.js | 2 +- src/languages/en.ts | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Expensify.js b/src/Expensify.js index 5caabcb583ab..fbf475c9f258 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -4,6 +4,7 @@ import React, {useCallback, useEffect, useLayoutEffect, useMemo, useRef, useStat import {AppState, Linking} from 'react-native'; import Onyx, {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import FocusModeNotification from '@components/FocusModeNotification'; import ConfirmModal from './components/ConfirmModal'; import DeeplinkWrapper from './components/DeeplinkWrapper'; import EmojiPicker from './components/EmojiPicker/EmojiPicker'; @@ -36,7 +37,6 @@ import Visibility from './libs/Visibility'; import ONYXKEYS from './ONYXKEYS'; import PopoverReportActionContextMenu from './pages/home/report/ContextMenu/PopoverReportActionContextMenu'; import * as ReportActionContextMenu from './pages/home/report/ContextMenu/ReportActionContextMenu'; -import FocusModeNotification from '@components/FocusModeNotification'; Onyx.registerLogger(({level, message}) => { if (level === 'alert') { @@ -212,9 +212,7 @@ function Expensify(props) { isVisible /> ) : null} - {props.focusModeNotification ? ( - - ) : null} + {props.focusModeNotification ? : null} )} diff --git a/src/components/FocusModeNotification.js b/src/components/FocusModeNotification.js index d12a7ee1be8a..bb08b41abf9b 100644 --- a/src/components/FocusModeNotification.js +++ b/src/components/FocusModeNotification.js @@ -1,7 +1,7 @@ import React, {useEffect} from 'react'; -import CONST from '@src/CONST'; import useLocalize from '@hooks/useLocalize'; import * as User from '@userActions/User'; +import CONST from '@src/CONST'; import ConfirmModal from './ConfirmModal'; function FocusModeNotification() { diff --git a/src/languages/en.ts b/src/languages/en.ts index 593e287e079c..353700fcf51e 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1101,7 +1101,7 @@ export default { }, focusModeUpdateModal: { title: 'Welcome to #focus mode!', - prompt: 'Read messages will be hidden, unless they have a green dot, which means it\'s waiting on you. You can change this in your account settings.', + prompt: "Read messages will be hidden, unless they have a green dot, which means it's waiting on you. You can change this in your account settings.", }, notFound: { chatYouLookingForCannotBeFound: 'The chat you are looking for cannot be found.', From cf78f3e4c46733f4c532ec49ac9aed606c0d3f15 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 16:13:42 -1000 Subject: [PATCH 16/39] Update Onyx key name --- src/ONYXKEYS.ts | 4 ++-- src/libs/actions/Report.js | 2 +- src/libs/actions/User.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index bf89011322b7..6112fda2b692 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -150,7 +150,7 @@ const ONYXKEYS = { CARD_LIST: 'cardList', /** Whether the user has tried focus mode yet */ - NVP_HAS_TRIED_FOCUS_MODE: 'hasTriedFocusMode', + NVP_TRY_FOCUS_MODE: 'tryFocusMode', /** Boolean flag used to display the focus mode notification */ FOCUS_MODE_NOTIFICATION: 'focusModeNotification', @@ -348,7 +348,7 @@ type OnyxValues = { [ONYXKEYS.NVP_PRIORITY_MODE]: ValueOf; [ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE]: OnyxTypes.BlockedFromConcierge; [ONYXKEYS.NVP_PRIVATE_PUSH_NOTIFICATION_ID]: string; - [ONYXKEYS.NVP_HAS_TRIED_FOCUS_MODE]: boolean; + [ONYXKEYS.NVP_TRY_FOCUS_MODE]: boolean; [ONYXKEYS.FOCUS_MODE_NOTIFICATION]: boolean; [ONYXKEYS.NVP_LAST_PAYMENT_METHOD]: Record; [ONYXKEYS.NVP_RECENT_WAYPOINTS]: OnyxTypes.RecentWaypoint[]; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 46786601c959..13eeea3582de 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -110,7 +110,7 @@ Onyx.connect({ // In order to tell if the user has been asked to upgrade for their current device we will set an Onyx key with the current device login let hasTriedFocusMode = ''; Onyx.connect({ - key: ONYXKEYS.NVP_HAS_TRIED_FOCUS_MODE, + key: ONYXKEYS.NVP_TRY_FOCUS_MODE, callback: (val) => (hasTriedFocusMode = val), }); diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index 6ebf72cec5f4..5afe5df3e799 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -598,7 +598,7 @@ function updateChatPriorityMode(mode, shouldNavigate = true) { if (mode === CONST.PRIORITY_MODE.GSD) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.NVP_HAS_TRIED_FOCUS_MODE, + key: ONYXKEYS.NVP_TRY_FOCUS_MODE, value: true, }); } From 9b46e8aafd5cc9afbe2c2e9b66cd7704a79d03bb Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 16:16:43 -1000 Subject: [PATCH 17/39] Fix up comments --- src/libs/actions/Report.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 13eeea3582de..fe22f4d7c09e 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -107,7 +107,7 @@ Onyx.connect({ callback: (priorityMode) => (isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD), }); -// In order to tell if the user has been asked to upgrade for their current device we will set an Onyx key with the current device login +// We use this NVP to track whether the user has already tested out #focus mode. If so, we won't switch them automatically. let hasTriedFocusMode = ''; Onyx.connect({ key: ONYXKEYS.NVP_TRY_FOCUS_MODE, @@ -1552,7 +1552,7 @@ function navigateToConciergeChat(ignoreConciergeReportID = false) { function tryFocusModeUpdate() { Welcome.serverDataIsReadyPromise().then(() => { - // Check to see if the user is using #focus mode or if we have already asked them to upgrade on any devices. + // Check to see if the user is using #focus mode, has tried it before, or we have already switched them over automatically. if (isInFocusMode || hasTriedFocusMode) { Log.info('Not switching user to optimized focus mode.', false, {isInFocusMode, hasTriedFocusMode}); return; @@ -1566,8 +1566,10 @@ function tryFocusModeUpdate() { Log.info('Switching user to optimized focus mode', false, {reportCount}); - // Record that we asked them to upgrade so we don't ask them again later. + // Record that we automatically switched them so we don't ask again. hasTriedFocusMode = true; + + // Setting this triggers a modal to open and notify the user. Onyx.set(ONYXKEYS.FOCUS_MODE_NOTIFICATION, true); }); } From 10f27836749104ae8a56c62ce240494110b7979c Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 16:26:47 -1000 Subject: [PATCH 18/39] Only set NVP if we auto switched --- src/components/FocusModeNotification.js | 2 +- src/libs/actions/User.js | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/FocusModeNotification.js b/src/components/FocusModeNotification.js index bb08b41abf9b..272e7a99f3b9 100644 --- a/src/components/FocusModeNotification.js +++ b/src/components/FocusModeNotification.js @@ -7,7 +7,7 @@ import ConfirmModal from './ConfirmModal'; function FocusModeNotification() { const {translate} = useLocalize(); useEffect(() => { - User.updateChatPriorityMode(CONST.PRIORITY_MODE.GSD, false); + User.updateChatPriorityMode(CONST.PRIORITY_MODE.GSD, true); }, []); return ( Date: Tue, 31 Oct 2023 16:57:16 -1000 Subject: [PATCH 19/39] Add extra log params. Guard against modal showing on sign out --- src/libs/actions/Report.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index fe22f4d7c09e..5d03568bffc0 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -108,10 +108,17 @@ Onyx.connect({ }); // We use this NVP to track whether the user has already tested out #focus mode. If so, we won't switch them automatically. -let hasTriedFocusMode = ''; +let hasTriedFocusMode = true; Onyx.connect({ key: ONYXKEYS.NVP_TRY_FOCUS_MODE, - callback: (val) => (hasTriedFocusMode = val), + callback: (val) => { + // If there's no value e.g. user signed out we assume they don't need the notification + if (!_.isBoolean(val)) { + return; + } + + hasTriedFocusMode = val; + }, }); const allReports = {}; @@ -1564,7 +1571,7 @@ function tryFocusModeUpdate() { return; } - Log.info('Switching user to optimized focus mode', false, {reportCount}); + Log.info('Switching user to optimized focus mode', false, {reportCount, hasTriedFocusMode, isInFocusMode}); // Record that we automatically switched them so we don't ask again. hasTriedFocusMode = true; From 61961193243f17d2811a644c80966c81608ae856 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 17:07:46 -1000 Subject: [PATCH 20/39] Default to hiding the notification --- src/Expensify.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Expensify.js b/src/Expensify.js index fbf475c9f258..675e5494bad9 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -4,7 +4,7 @@ import React, {useCallback, useEffect, useLayoutEffect, useMemo, useRef, useStat import {AppState, Linking} from 'react-native'; import Onyx, {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import FocusModeNotification from '@components/FocusModeNotification'; +import FocusModeNotification from './components/FocusModeNotification'; import ConfirmModal from './components/ConfirmModal'; import DeeplinkWrapper from './components/DeeplinkWrapper'; import EmojiPicker from './components/EmojiPicker/EmojiPicker'; @@ -77,6 +77,9 @@ const propTypes = { /** Whether the app is waiting for the server's response to determine if a room is public */ isCheckingPublicRoom: PropTypes.bool, + /** Whether we should display the notification alerting the user that focus mode has been auto-enabled */ + focusModeNotification: PropTypes.bool, + ...withLocalizePropTypes, }; @@ -89,6 +92,7 @@ const defaultProps = { isSidebarLoaded: false, screenShareRequest: null, isCheckingPublicRoom: true, + focusModeNotification: false, }; function Expensify(props) { From 3d8d96d1030f0bd56196dea5288d15983e7d7bcc Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 17:38:18 -1000 Subject: [PATCH 21/39] Pass automatic param. Do not try to switch unless we have a currentUserAccountID --- src/libs/actions/Report.js | 17 +++++++---------- src/libs/actions/User.js | 1 + 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 5d03568bffc0..ee0851ad1abf 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -107,18 +107,10 @@ Onyx.connect({ callback: (priorityMode) => (isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD), }); -// We use this NVP to track whether the user has already tested out #focus mode. If so, we won't switch them automatically. -let hasTriedFocusMode = true; +let hasTriedFocusMode; Onyx.connect({ key: ONYXKEYS.NVP_TRY_FOCUS_MODE, - callback: (val) => { - // If there's no value e.g. user signed out we assume they don't need the notification - if (!_.isBoolean(val)) { - return; - } - - hasTriedFocusMode = val; - }, + callback: val => hasTriedFocusMode = val, }); const allReports = {}; @@ -1559,6 +1551,11 @@ function navigateToConciergeChat(ignoreConciergeReportID = false) { function tryFocusModeUpdate() { Welcome.serverDataIsReadyPromise().then(() => { + // User is signed out so do not try to switch them + if (!currentUserAccountID) { + return; + } + // Check to see if the user is using #focus mode, has tried it before, or we have already switched them over automatically. if (isInFocusMode || hasTriedFocusMode) { Log.info('Not switching user to optimized focus mode.', false, {isInFocusMode, hasTriedFocusMode}); diff --git a/src/libs/actions/User.js b/src/libs/actions/User.js index b398b54086b7..76e1016273a7 100644 --- a/src/libs/actions/User.js +++ b/src/libs/actions/User.js @@ -608,6 +608,7 @@ function updateChatPriorityMode(mode, automatic = false) { 'UpdateChatPriorityMode', { value: mode, + automatic, }, {optimisticData}, ); From aa4df37c7a2175f1f50666434fbc7e924f773594 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 17:52:10 -1000 Subject: [PATCH 22/39] Prettier --- src/Expensify.js | 2 +- src/libs/actions/Report.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Expensify.js b/src/Expensify.js index 675e5494bad9..29e1f8c5eb45 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -4,10 +4,10 @@ import React, {useCallback, useEffect, useLayoutEffect, useMemo, useRef, useStat import {AppState, Linking} from 'react-native'; import Onyx, {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import FocusModeNotification from './components/FocusModeNotification'; import ConfirmModal from './components/ConfirmModal'; import DeeplinkWrapper from './components/DeeplinkWrapper'; import EmojiPicker from './components/EmojiPicker/EmojiPicker'; +import FocusModeNotification from './components/FocusModeNotification'; import GrowlNotification from './components/GrowlNotification'; import AppleAuthWrapper from './components/SignInButtons/AppleAuthWrapper'; import SplashScreenHider from './components/SplashScreenHider'; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index ee0851ad1abf..db3d49714ba5 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -110,7 +110,7 @@ Onyx.connect({ let hasTriedFocusMode; Onyx.connect({ key: ONYXKEYS.NVP_TRY_FOCUS_MODE, - callback: val => hasTriedFocusMode = val, + callback: (val) => (hasTriedFocusMode = val), }); const allReports = {}; From c78324affdab247f2dc6b8cf10974b8e5a98947b Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 17:55:15 -1000 Subject: [PATCH 23/39] Need to add spanish translations --- src/languages/es.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index f1e24a7a6777..d8b677680305 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1095,6 +1095,10 @@ export default { year: 'Año', selectYear: 'Por favor, selecciona un año', }, + focusModeUpdateModal: { + title: 'Welcome to #focus mode!', + prompt: "Read messages will be hidden, unless they have a green dot, which means it's waiting on you. You can change this in your account settings.", + }, notFound: { chatYouLookingForCannotBeFound: 'El chat que estás buscando no se pudo encontrar.', getMeOutOfHere: 'Sácame de aquí', From b9ef6e31266ea3cab089cf3618238baac9032a71 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 31 Oct 2023 18:21:31 -1000 Subject: [PATCH 24/39] Add workaround so that we are not waiting for serverDataIsReadyPromise() to happen forever --- src/libs/actions/App.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 884dddf0eb0a..914b2df5fc90 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -185,6 +185,11 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false) { ], }; if (!isOpenApp) { + defaultData.optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER, + value: false, + }); return defaultData; } return { From 00e99ef09de6b4f402e3dd71d5cef1760aecc984 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 1 Nov 2023 11:18:19 -1000 Subject: [PATCH 25/39] Add PriorityMode action --- .../Navigation/AppNavigator/AuthScreens.js | 3 +- src/libs/actions/App.js | 5 - src/libs/actions/PriorityMode.ts | 118 ++++++++++++++++++ src/libs/actions/Report.js | 51 +------- 4 files changed, 123 insertions(+), 54 deletions(-) create mode 100644 src/libs/actions/PriorityMode.ts diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 7f07d7f57aed..fa653354ad44 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -22,6 +22,7 @@ import * as Modal from '@userActions/Modal'; import * as PersonalDetails from '@userActions/PersonalDetails'; import * as Report from '@userActions/Report'; import * as Session from '@userActions/Session'; +import * as PriorityMode from '@userActions/PriorityMode'; import Timing from '@userActions/Timing'; import * as User from '@userActions/User'; import CONFIG from '@src/CONFIG'; @@ -193,7 +194,7 @@ function AuthScreens({isUsingMemoryOnlyKeys, lastUpdateIDAppliedToClient, sessio App.reconnectApp(lastUpdateIDAppliedToClient); } - Report.autoSwitchToFocusMode(); + PriorityMode.autoSwitchToFocusMode(); App.setUpPoliciesAndNavigate(session); diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 914b2df5fc90..884dddf0eb0a 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -185,11 +185,6 @@ function getOnyxDataForOpenOrReconnect(isOpenApp = false) { ], }; if (!isOpenApp) { - defaultData.optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER, - value: false, - }); return defaultData; } return { diff --git a/src/libs/actions/PriorityMode.ts b/src/libs/actions/PriorityMode.ts new file mode 100644 index 000000000000..db9692223182 --- /dev/null +++ b/src/libs/actions/PriorityMode.ts @@ -0,0 +1,118 @@ +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import debounce from 'lodash/debounce'; +import Onyx, { OnyxCollection } from 'react-native-onyx'; +import * as CollectionUtils from '@libs/CollectionUtils'; +import { Report } from '@src/types/onyx'; +import Log from '@libs/Log'; + +let resolveIsReadyPromise: (args?: unknown[]) => void; +let isReadyPromise = new Promise((resolve) => { + resolveIsReadyPromise = resolve; +}); +function resetHasReadRequiredDataFromStorage() { + // Create a new promise and a new resolve function + isReadyPromise = new Promise((resolve) => { + resolveIsReadyPromise = resolve; + }); +} + +let currentUserAccountID: number | undefined | null; +Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: (val) => { + currentUserAccountID = val?.accountID; + }, +}); + +let allReports: OnyxCollection | undefined; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT, + callback: (report, key) => { + if (!key || !report) { + return; + } + + if (!allReports) { + allReports = {}; + } + + const reportID = CollectionUtils.extractCollectionItemID(key); + allReports[reportID] = report; + }, +}); + +let isLoadingReportData = true; +Onyx.connect({ + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + initWithStoredValues: false, + callback: (value) => { + isLoadingReportData = value ?? false; + // eslint-disable-next-line @typescript-eslint/no-use-before-define + checkRequiredData(); + }, +}); + +let isInFocusMode: boolean | undefined; +Onyx.connect({ + key: ONYXKEYS.NVP_PRIORITY_MODE, + callback: (priorityMode) => (isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD), +}); + +let hasTriedFocusMode: boolean | undefined | null; +Onyx.connect({ + key: ONYXKEYS.NVP_TRY_FOCUS_MODE, + callback: (val) => { + hasTriedFocusMode = val; + + // eslint-disable-next-line @typescript-eslint/no-use-before-define + checkRequiredData(); + }, +}); + +function checkRequiredData() { + if (allReports === undefined || hasTriedFocusMode === undefined || isInFocusMode === undefined || isLoadingReportData) { + return; + } + + resolveIsReadyPromise(); +} + +function tryFocusModeUpdate() { + isReadyPromise.then(() => { + // User is signed out so do not try to switch them + if (!currentUserAccountID) { + return; + } + + // Check to see if the user is using #focus mode, has tried it before, or we have already switched them over automatically. + if (isInFocusMode ?? hasTriedFocusMode) { + Log.info('Not switching user to optimized focus mode.', false, {isInFocusMode, hasTriedFocusMode}); + return; + } + + const reportCount = Object.keys(allReports ?? {}).length; + if (reportCount < CONST.REPORT.MAX_COUNT_BEFORE_FOCUS_UPDATE) { + Log.info('Not switching user to optimized focus mode as they do not have enough reports', false, {reportCount}); + return; + } + + Log.info('Switching user to optimized focus mode', false, {reportCount, hasTriedFocusMode, isInFocusMode}); + + // Record that we automatically switched them so we don't ask again. + hasTriedFocusMode = true; + + // Setting this triggers a modal to open and notify the user. + Onyx.set(ONYXKEYS.FOCUS_MODE_NOTIFICATION, true); + }); +} + +/** + * Debounce the prompt to promote focus mode as many reports updates could happen in a short burst + */ +const autoSwitchToFocusMode = debounce(tryFocusModeUpdate, 300, {leading: true}); + +export { + resetHasReadRequiredDataFromStorage, + autoSwitchToFocusMode, +}; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index db3d49714ba5..6cb282585626 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -28,6 +28,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import * as Session from './Session'; import * as Welcome from './Welcome'; +import * as PriorityMode from './PriorityMode'; let currentUserAccountID; Onyx.connect({ @@ -101,18 +102,6 @@ Onyx.connect({ }, }); -let isInFocusMode = false; -Onyx.connect({ - key: ONYXKEYS.NVP_PRIORITY_MODE, - callback: (priorityMode) => (isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD), -}); - -let hasTriedFocusMode; -Onyx.connect({ - key: ONYXKEYS.NVP_TRY_FOCUS_MODE, - callback: (val) => (hasTriedFocusMode = val), -}); - const allReports = {}; let conciergeChatReportID; const typingWatchTimers = {}; @@ -1026,11 +1015,7 @@ function broadcastUserIsLeavingRoom(reportID) { Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, leavingStatus); } -/** - * Debounce the prompt to promote focus mode as many reports updates could happen in a short burst - */ -// eslint-disable-next-line no-use-before-define -const autoSwitchToFocusMode = _.debounce(tryFocusModeUpdate, 300, true); + /** * When a report changes in Onyx, this fetches the report from the API if the report doesn't have a name @@ -1071,7 +1056,7 @@ function handleReportChanged(report) { } // Try to switch the user to focus mode - autoSwitchToFocusMode(); + PriorityMode.autoSwitchToFocusMode(); } Onyx.connect({ @@ -1549,35 +1534,6 @@ function navigateToConciergeChat(ignoreConciergeReportID = false) { } } -function tryFocusModeUpdate() { - Welcome.serverDataIsReadyPromise().then(() => { - // User is signed out so do not try to switch them - if (!currentUserAccountID) { - return; - } - - // Check to see if the user is using #focus mode, has tried it before, or we have already switched them over automatically. - if (isInFocusMode || hasTriedFocusMode) { - Log.info('Not switching user to optimized focus mode.', false, {isInFocusMode, hasTriedFocusMode}); - return; - } - - const reportCount = _.keys(allReports).length; - if (reportCount < CONST.REPORT.MAX_COUNT_BEFORE_FOCUS_UPDATE) { - Log.info('Not switching user to optimized focus mode as they do not have enough reports', false, {reportCount}); - return; - } - - Log.info('Switching user to optimized focus mode', false, {reportCount, hasTriedFocusMode, isInFocusMode}); - - // Record that we automatically switched them so we don't ask again. - hasTriedFocusMode = true; - - // Setting this triggers a modal to open and notify the user. - Onyx.set(ONYXKEYS.FOCUS_MODE_NOTIFICATION, true); - }); -} - /** * Add a policy report (workspace room) optimistically and navigate to it. * @@ -2590,7 +2546,6 @@ export { getCurrentUserAccountID, setLastOpenedPublicRoom, flagComment, - autoSwitchToFocusMode, openLastOpenedPublicRoom, updatePrivateNotes, getReportPrivateNote, From 53762c1688617683eba9392ba5df4072c1ec6457 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 1 Nov 2023 11:21:20 -1000 Subject: [PATCH 26/39] Move the things we actually need to wait for into a separate file to avoid mixing concerns --- src/libs/actions/PriorityMode.ts | 14 ++++++++------ src/libs/actions/Session/index.ts | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/PriorityMode.ts b/src/libs/actions/PriorityMode.ts index db9692223182..061f3c44d92c 100644 --- a/src/libs/actions/PriorityMode.ts +++ b/src/libs/actions/PriorityMode.ts @@ -10,12 +10,6 @@ let resolveIsReadyPromise: (args?: unknown[]) => void; let isReadyPromise = new Promise((resolve) => { resolveIsReadyPromise = resolve; }); -function resetHasReadRequiredDataFromStorage() { - // Create a new promise and a new resolve function - isReadyPromise = new Promise((resolve) => { - resolveIsReadyPromise = resolve; - }); -} let currentUserAccountID: number | undefined | null; Onyx.connect({ @@ -70,6 +64,14 @@ Onyx.connect({ }, }); +function resetHasReadRequiredDataFromStorage() { + // Create a new promise and a new resolve function + isReadyPromise = new Promise((resolve) => { + resolveIsReadyPromise = resolve; + }); + isLoadingReportData = true; +} + function checkRequiredData() { if (allReports === undefined || hasTriedFocusMode === undefined || isInFocusMode === undefined || isLoadingReportData) { return; diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 74d2f609ab9b..6783f7d62248 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -18,6 +18,7 @@ import * as Device from '@userActions/Device'; import redirectToSignIn from '@userActions/SignInRedirect'; import Timing from '@userActions/Timing'; import * as Welcome from '@userActions/Welcome'; +import * as PriorityMode from '@userActions/PriorityMode'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -589,6 +590,7 @@ function cleanupSession() { Pusher.disconnect(); Timers.clearAll(); Welcome.resetReadyCheck(); + PriorityMode.resetHasReadRequiredDataFromStorage(); } function clearAccountMessages() { From 8125cef9f3720652513181e45e0955a88e9efef0 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 1 Nov 2023 11:22:57 -1000 Subject: [PATCH 27/39] Prettier --- .../Navigation/AppNavigator/AuthScreens.js | 2 +- src/libs/actions/PriorityMode.ts | 21 +++++++++++-------- src/libs/actions/Report.js | 4 +--- src/libs/actions/Session/index.ts | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index fa653354ad44..a9de68bf8778 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -20,9 +20,9 @@ import * as App from '@userActions/App'; import * as Download from '@userActions/Download'; import * as Modal from '@userActions/Modal'; import * as PersonalDetails from '@userActions/PersonalDetails'; +import * as PriorityMode from '@userActions/PriorityMode'; import * as Report from '@userActions/Report'; import * as Session from '@userActions/Session'; -import * as PriorityMode from '@userActions/PriorityMode'; import Timing from '@userActions/Timing'; import * as User from '@userActions/User'; import CONFIG from '@src/CONFIG'; diff --git a/src/libs/actions/PriorityMode.ts b/src/libs/actions/PriorityMode.ts index 061f3c44d92c..ccecd058d98c 100644 --- a/src/libs/actions/PriorityMode.ts +++ b/src/libs/actions/PriorityMode.ts @@ -1,10 +1,10 @@ -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; import debounce from 'lodash/debounce'; -import Onyx, { OnyxCollection } from 'react-native-onyx'; +import Onyx, {OnyxCollection} from 'react-native-onyx'; import * as CollectionUtils from '@libs/CollectionUtils'; -import { Report } from '@src/types/onyx'; import Log from '@libs/Log'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import {Report} from '@src/types/onyx'; let resolveIsReadyPromise: (args?: unknown[]) => void; let isReadyPromise = new Promise((resolve) => { @@ -42,6 +42,7 @@ Onyx.connect({ initWithStoredValues: false, callback: (value) => { isLoadingReportData = value ?? false; + // eslint-disable-next-line @typescript-eslint/no-use-before-define checkRequiredData(); }, @@ -50,7 +51,12 @@ Onyx.connect({ let isInFocusMode: boolean | undefined; Onyx.connect({ key: ONYXKEYS.NVP_PRIORITY_MODE, - callback: (priorityMode) => (isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD), + callback: (priorityMode) => { + isInFocusMode = priorityMode === CONST.PRIORITY_MODE.GSD; + + // eslint-disable-next-line @typescript-eslint/no-use-before-define + checkRequiredData(); + }, }); let hasTriedFocusMode: boolean | undefined | null; @@ -114,7 +120,4 @@ function tryFocusModeUpdate() { */ const autoSwitchToFocusMode = debounce(tryFocusModeUpdate, 300, {leading: true}); -export { - resetHasReadRequiredDataFromStorage, - autoSwitchToFocusMode, -}; +export {resetHasReadRequiredDataFromStorage, autoSwitchToFocusMode}; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 6cb282585626..9917be9fb590 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -26,9 +26,9 @@ import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import * as PriorityMode from './PriorityMode'; import * as Session from './Session'; import * as Welcome from './Welcome'; -import * as PriorityMode from './PriorityMode'; let currentUserAccountID; Onyx.connect({ @@ -1015,8 +1015,6 @@ function broadcastUserIsLeavingRoom(reportID) { Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, leavingStatus); } - - /** * When a report changes in Onyx, this fetches the report from the API if the report doesn't have a name * diff --git a/src/libs/actions/Session/index.ts b/src/libs/actions/Session/index.ts index 6783f7d62248..5188db096206 100644 --- a/src/libs/actions/Session/index.ts +++ b/src/libs/actions/Session/index.ts @@ -15,10 +15,10 @@ import * as ReportUtils from '@libs/ReportUtils'; import Timers from '@libs/Timers'; import {hideContextMenu} from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import * as Device from '@userActions/Device'; +import * as PriorityMode from '@userActions/PriorityMode'; import redirectToSignIn from '@userActions/SignInRedirect'; import Timing from '@userActions/Timing'; import * as Welcome from '@userActions/Welcome'; -import * as PriorityMode from '@userActions/PriorityMode'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; From c228931fcc2d9efbd351fd5e552d6859aac1448a Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 1 Nov 2023 12:07:38 -1000 Subject: [PATCH 28/39] Cant use ?? because it breaks the logic --- src/libs/actions/PriorityMode.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/PriorityMode.ts b/src/libs/actions/PriorityMode.ts index ccecd058d98c..9242ea12c1e2 100644 --- a/src/libs/actions/PriorityMode.ts +++ b/src/libs/actions/PriorityMode.ts @@ -94,7 +94,7 @@ function tryFocusModeUpdate() { } // Check to see if the user is using #focus mode, has tried it before, or we have already switched them over automatically. - if (isInFocusMode ?? hasTriedFocusMode) { + if ((isInFocusMode ?? false) || hasTriedFocusMode) { Log.info('Not switching user to optimized focus mode.', false, {isInFocusMode, hasTriedFocusMode}); return; } From 03205bb64bd8893e0a6ef26e59842f05fd0de845 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 1 Nov 2023 12:15:16 -1000 Subject: [PATCH 29/39] Clear reports on sign out --- src/libs/actions/PriorityMode.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/actions/PriorityMode.ts b/src/libs/actions/PriorityMode.ts index 9242ea12c1e2..3821dbb74320 100644 --- a/src/libs/actions/PriorityMode.ts +++ b/src/libs/actions/PriorityMode.ts @@ -76,6 +76,7 @@ function resetHasReadRequiredDataFromStorage() { resolveIsReadyPromise = resolve; }); isLoadingReportData = true; + allReports = {}; } function checkRequiredData() { From b87d95560220329f282f8320b9a666ccf6f73202 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 1 Nov 2023 13:05:29 -1000 Subject: [PATCH 30/39] Run prettier --- src/components/FocusModeNotification.js | 19 ++++++- .../HTMLRenderers/AnchorRenderer.js | 46 +--------------- src/components/TextLink.js | 4 +- src/languages/en.ts | 2 +- src/libs/actions/Link.js | 53 ++++++++++++++++++- 5 files changed, 75 insertions(+), 49 deletions(-) diff --git a/src/components/FocusModeNotification.js b/src/components/FocusModeNotification.js index 272e7a99f3b9..62b0ec39aad2 100644 --- a/src/components/FocusModeNotification.js +++ b/src/components/FocusModeNotification.js @@ -1,21 +1,38 @@ import React, {useEffect} from 'react'; +import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; +import styles from '@styles/styles'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ConfirmModal from './ConfirmModal'; +import Text from './Text'; +import TextLinkWithRef from './TextLink'; function FocusModeNotification() { + const {environmentURL} = useEnvironment(); const {translate} = useLocalize(); useEffect(() => { User.updateChatPriorityMode(CONST.PRIORITY_MODE.GSD, true); }, []); + return ( + {translate('focusModeUpdateModal.prompt')} + + {translate('common.here')} + + . + + } isVisible /> ); diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js index be70af0adb4f..f5679e37fab8 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js @@ -6,14 +6,10 @@ import AnchorForCommentsOnly from '@components/AnchorForCommentsOnly'; import * as HTMLEngineUtils from '@components/HTMLEngineProvider/htmlEngineUtils'; import Text from '@components/Text'; import useEnvironment from '@hooks/useEnvironment'; -import Navigation from '@libs/Navigation/Navigation'; import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; -import * as Url from '@libs/Url'; import styles from '@styles/styles'; import * as Link from '@userActions/Link'; -import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; -import ROUTES from '@src/ROUTES'; import htmlRendererPropTypes from './htmlRendererPropTypes'; function AnchorRenderer(props) { @@ -24,46 +20,6 @@ function AnchorRenderer(props) { const displayName = lodashGet(props.tnode, 'domNode.children[0].data', ''); const parentStyle = lodashGet(props.tnode, 'parent.styles.nativeTextRet', {}); const attrHref = htmlAttribs.href || ''; - const attrPath = Url.getPathFromURL(attrHref); - const hasSameOrigin = Url.hasSameExpensifyOrigin(attrHref, environmentURL); - const hasExpensifyOrigin = Url.hasSameExpensifyOrigin(attrHref, CONFIG.EXPENSIFY.EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(attrHref, CONFIG.EXPENSIFY.STAGING_API_ROOT); - const internalNewExpensifyPath = - (Url.hasSameExpensifyOrigin(attrHref, CONST.NEW_EXPENSIFY_URL) || - Url.hasSameExpensifyOrigin(attrHref, CONST.STAGING_NEW_EXPENSIFY_URL) || - attrHref.startsWith(CONST.DEV_NEW_EXPENSIFY_URL)) && - !CONST.PATHS_TO_TREAT_AS_EXTERNAL.includes(attrPath) - ? attrPath - : ''; - const internalExpensifyPath = - hasExpensifyOrigin && !attrPath.startsWith(CONFIG.EXPENSIFY.CONCIERGE_URL_PATHNAME) && !attrPath.startsWith(CONFIG.EXPENSIFY.DEVPORTAL_URL_PATHNAME) && attrPath; - const navigateToLink = () => { - // There can be messages from Concierge with links to specific NewDot reports. Those URLs look like this: - // https://www.expensify.com.dev/newdotreport?reportID=3429600449838908 and they have a target="_blank" attribute. This is so that when a user is on OldDot, - // clicking on the link will open the chat in NewDot. However, when a user is in NewDot and clicks on the concierge link, the link needs to be handled differently. - // Normally, the link would be sent to Link.openOldDotLink() and opened in a new tab, and that's jarring to the user. Since the intention is to link to a specific NewDot chat, - // the reportID is extracted from the URL and then opened as an internal link, taking the user straight to the chat in the same tab. - if (hasExpensifyOrigin && attrHref.indexOf('newdotreport?reportID=') > -1) { - const reportID = attrHref.split('newdotreport?reportID=').pop(); - const reportRoute = ROUTES.REPORT_WITH_ID.getRoute(reportID); - Navigation.navigate(reportRoute); - return; - } - - // If we are handling a New Expensify link then we will assume this should be opened by the app internally. This ensures that the links are opened internally via react-navigation - // instead of in a new tab or with a page refresh (which is the default behavior of an anchor tag) - if (internalNewExpensifyPath && hasSameOrigin) { - Navigation.navigate(internalNewExpensifyPath); - return; - } - - // If we are handling an old dot Expensify link we need to open it with openOldDotLink() so we can navigate to it with the user already logged in. - // As attachments also use expensify.com we don't want it working the same as links. - if (internalExpensifyPath && !isAttachment) { - Link.openOldDotLink(internalExpensifyPath); - return; - } - Link.openExternalLink(attrHref); - }; if (!HTMLEngineUtils.isInsideComment(props.tnode)) { // This is not a comment from a chat, the AnchorForCommentsOnly uses a Pressable to create a context menu on right click. @@ -72,7 +28,7 @@ function AnchorRenderer(props) { return ( Link.openLink(attrHref, environmentURL, isAttachment)} suppressHighlighting > diff --git a/src/components/TextLink.js b/src/components/TextLink.js index 79f3d43a7743..579e115e0f5a 100644 --- a/src/components/TextLink.js +++ b/src/components/TextLink.js @@ -1,6 +1,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import _ from 'underscore'; +import useEnvironment from '@hooks/useEnvironment'; import stylePropTypes from '@styles/stylePropTypes'; import styles from '@styles/styles'; import * as Link from '@userActions/Link'; @@ -37,6 +38,7 @@ const defaultProps = { }; function TextLink(props) { + const {environmentURL} = useEnvironment(); const rest = _.omit(props, _.keys(propTypes)); const additionalStyles = _.isArray(props.style) ? props.style : [props.style]; @@ -50,7 +52,7 @@ function TextLink(props) { return; } - Link.openExternalLink(props.href); + Link.openLink(props.href, environmentURL); }; /** diff --git a/src/languages/en.ts b/src/languages/en.ts index c4bd20d39648..e3a02090f010 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1098,7 +1098,7 @@ export default { }, focusModeUpdateModal: { title: 'Welcome to #focus mode!', - prompt: "Read messages will be hidden, unless they have a green dot, which means it's waiting on you. You can change this in your account settings.", + prompt: "Read messages will be hidden, unless they have a green dot, which means it's waiting on you. You can change this in your account settings ", }, notFound: { chatYouLookingForCannotBeFound: 'The chat you are looking for cannot be found.', diff --git a/src/libs/actions/Link.js b/src/libs/actions/Link.js index 0a50bb62ddc8..23684cf24100 100644 --- a/src/libs/actions/Link.js +++ b/src/libs/actions/Link.js @@ -4,8 +4,12 @@ import _ from 'underscore'; import * as API from '@libs/API'; import asyncOpenURL from '@libs/asyncOpenURL'; import * as Environment from '@libs/Environment/Environment'; +import Navigation from '@libs/Navigation/Navigation'; import * as Url from '@libs/Url'; +import CONFIG from '@src/CONFIG'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; let isNetworkOffline = false; Onyx.connect({ @@ -68,4 +72,51 @@ function openOldDotLink(url) { (oldDotURL) => oldDotURL, ); } -export {buildOldDotURL, openOldDotLink, openExternalLink}; + +/** + * @param {string} href + * @param {string} environmentURL + * @param {boolean} [isAttachment] + */ +function openLink(href, environmentURL, isAttachment = false) { + const attrPath = Url.getPathFromURL(href); + const hasSameOrigin = Url.hasSameExpensifyOrigin(href, environmentURL); + const hasExpensifyOrigin = Url.hasSameExpensifyOrigin(href, CONFIG.EXPENSIFY.EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(href, CONFIG.EXPENSIFY.STAGING_API_ROOT); + const internalNewExpensifyPath = + (Url.hasSameExpensifyOrigin(href, CONST.NEW_EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(href, CONST.STAGING_NEW_EXPENSIFY_URL) || href.startsWith(CONST.DEV_NEW_EXPENSIFY_URL)) && + !CONST.PATHS_TO_TREAT_AS_EXTERNAL.includes(attrPath) + ? attrPath + : ''; + const internalExpensifyPath = + hasExpensifyOrigin && !attrPath.startsWith(CONFIG.EXPENSIFY.CONCIERGE_URL_PATHNAME) && !attrPath.startsWith(CONFIG.EXPENSIFY.DEVPORTAL_URL_PATHNAME) && attrPath; + + // There can be messages from Concierge with links to specific NewDot reports. Those URLs look like this: + // https://www.expensify.com.dev/newdotreport?reportID=3429600449838908 and they have a target="_blank" attribute. This is so that when a user is on OldDot, + // clicking on the link will open the chat in NewDot. However, when a user is in NewDot and clicks on the concierge link, the link needs to be handled differently. + // Normally, the link would be sent to Link.openOldDotLink() and opened in a new tab, and that's jarring to the user. Since the intention is to link to a specific NewDot chat, + // the reportID is extracted from the URL and then opened as an internal link, taking the user straight to the chat in the same tab. + if (hasExpensifyOrigin && href.indexOf('newdotreport?reportID=') > -1) { + const reportID = href.split('newdotreport?reportID=').pop(); + const reportRoute = ROUTES.REPORT_WITH_ID.getRoute(reportID); + Navigation.navigate(reportRoute); + return; + } + + // If we are handling a New Expensify link then we will assume this should be opened by the app internally. This ensures that the links are opened internally via react-navigation + // instead of in a new tab or with a page refresh (which is the default behavior of an anchor tag) + if (internalNewExpensifyPath && hasSameOrigin) { + Navigation.navigate(internalNewExpensifyPath); + return; + } + + // If we are handling an old dot Expensify link we need to open it with openOldDotLink() so we can navigate to it with the user already logged in. + // As attachments also use expensify.com we don't want it working the same as links. + if (internalExpensifyPath && !isAttachment) { + openOldDotLink(internalExpensifyPath); + return; + } + + openExternalLink(href); +} + +export {buildOldDotURL, openOldDotLink, openExternalLink, openLink}; From c2f7fd04fc6b745b2fb0ddf4a715d06d338ffa5c Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 1 Nov 2023 13:10:36 -1000 Subject: [PATCH 31/39] prettier --- src/components/FocusModeNotification.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/FocusModeNotification.js b/src/components/FocusModeNotification.js index 62b0ec39aad2..37d8e4848b98 100644 --- a/src/components/FocusModeNotification.js +++ b/src/components/FocusModeNotification.js @@ -2,6 +2,7 @@ import React, {useEffect} from 'react'; import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import styles from '@styles/styles'; +import * as Link from '@userActions/Link'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ConfirmModal from './ConfirmModal'; @@ -14,7 +15,7 @@ function FocusModeNotification() { useEffect(() => { User.updateChatPriorityMode(CONST.PRIORITY_MODE.GSD, true); }, []); - + const href = `${environmentURL}/settings/preferences/priority-mode`; return ( {translate('focusModeUpdateModal.prompt')} { + User.clearFocusModeNotification(); + Link.openLink(href, environmentURL); + }} > {translate('common.here')} From 4e6aefbde3d8989c3f5fe1ef058540e26b00f570 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 1 Nov 2023 13:16:48 -1000 Subject: [PATCH 32/39] Remove all logic from Report.js --- src/libs/actions/PriorityMode.ts | 24 +++++++++++++++++++----- src/libs/actions/Report.js | 4 ---- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/libs/actions/PriorityMode.ts b/src/libs/actions/PriorityMode.ts index 3821dbb74320..f71f682f3db6 100644 --- a/src/libs/actions/PriorityMode.ts +++ b/src/libs/actions/PriorityMode.ts @@ -6,6 +6,15 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {Report} from '@src/types/onyx'; +/** + * This actions file 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) + * - We are not waiting for reports to load. We check the count of the reports to determine whether the user is eligible to be automatically switched. + */ + let resolveIsReadyPromise: (args?: unknown[]) => void; let isReadyPromise = new Promise((resolve) => { resolveIsReadyPromise = resolve; @@ -19,6 +28,12 @@ Onyx.connect({ }, }); +/** + * Debounce the prompt to promote focus mode as many reports updates could happen in a short burst + */ +// eslint-disable-next-line @typescript-eslint/no-use-before-define +const autoSwitchToFocusMode = debounce(tryFocusModeUpdate, 300, {leading: true}); + let allReports: OnyxCollection | undefined; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, @@ -32,7 +47,11 @@ Onyx.connect({ } const reportID = CollectionUtils.extractCollectionItemID(key); + allReports[reportID] = report; + + // Each time a new report is added we will check to see if the user should be switched + autoSwitchToFocusMode(); }, }); @@ -116,9 +135,4 @@ function tryFocusModeUpdate() { }); } -/** - * Debounce the prompt to promote focus mode as many reports updates could happen in a short burst - */ -const autoSwitchToFocusMode = debounce(tryFocusModeUpdate, 300, {leading: true}); - export {resetHasReadRequiredDataFromStorage, autoSwitchToFocusMode}; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 9917be9fb590..3f7dc76b174d 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -26,7 +26,6 @@ import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import * as PriorityMode from './PriorityMode'; import * as Session from './Session'; import * as Welcome from './Welcome'; @@ -1052,9 +1051,6 @@ function handleReportChanged(report) { if (report.reportID && report.reportName === undefined) { reconnect(report.reportID); } - - // Try to switch the user to focus mode - PriorityMode.autoSwitchToFocusMode(); } Onyx.connect({ From e52cb603ad9dbae1ad3e08c616c81e7fa3be7403 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 1 Nov 2023 13:18:04 -1000 Subject: [PATCH 33/39] Add some documentation as the waiting for Onyx logic might be confusing --- src/libs/actions/PriorityMode.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/PriorityMode.ts b/src/libs/actions/PriorityMode.ts index f71f682f3db6..d528c31d7453 100644 --- a/src/libs/actions/PriorityMode.ts +++ b/src/libs/actions/PriorityMode.ts @@ -12,7 +12,7 @@ import {Report} from '@src/types/onyx'; * * - 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) - * - We are not waiting for reports to load. We check the count of the reports to determine whether the user is eligible to be automatically switched. + * - 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. */ let resolveIsReadyPromise: (args?: unknown[]) => void; From f696795dde5ad3d66c6966d36bbd434a669d0837 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Wed, 1 Nov 2023 13:38:37 -1000 Subject: [PATCH 34/39] Dont break links in comments --- .../HTMLRenderers/AnchorRenderer.js | 4 ++- src/libs/actions/Link.js | 34 ++++++++++++++----- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js index f5679e37fab8..4fc26c3f054d 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js @@ -20,6 +20,8 @@ function AnchorRenderer(props) { const displayName = lodashGet(props.tnode, 'domNode.children[0].data', ''); const parentStyle = lodashGet(props.tnode, 'parent.styles.nativeTextRet', {}); const attrHref = htmlAttribs.href || ''; + const internalNewExpensifyPath = Link.getInternalNewExpensifyPath(attrHref); + const internalExpensifyPath = Link.getInternalExpensifyPath(attrHref); if (!HTMLEngineUtils.isInsideComment(props.tnode)) { // This is not a comment from a chat, the AnchorForCommentsOnly uses a Pressable to create a context menu on right click. @@ -59,7 +61,7 @@ function AnchorRenderer(props) { key={props.key} displayName={displayName} // Only pass the press handler for internal links. For public links or whitelisted internal links fallback to default link handling - onPress={internalNewExpensifyPath || internalExpensifyPath ? navigateToLink : undefined} + onPress={internalNewExpensifyPath || internalExpensifyPath ? Link.openLink : undefined} > diff --git a/src/libs/actions/Link.js b/src/libs/actions/Link.js index 23684cf24100..72f4db2d88d9 100644 --- a/src/libs/actions/Link.js +++ b/src/libs/actions/Link.js @@ -73,22 +73,38 @@ function openOldDotLink(url) { ); } +/** + * @param {string} href + * @returns {string} + */ +function getInternalNewExpensifyPath(href) { + const attrPath = Url.getPathFromURL(href); + return (Url.hasSameExpensifyOrigin(href, CONST.NEW_EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(href, CONST.STAGING_NEW_EXPENSIFY_URL) || href.startsWith(CONST.DEV_NEW_EXPENSIFY_URL)) && + !CONST.PATHS_TO_TREAT_AS_EXTERNAL.includes(attrPath) + ? attrPath + : ''; +} + +/** + * @param {string} href + * @returns {string} + */ +function getInternalExpensifyPath(href) { + const attrPath = Url.getPathFromURL(href); + const hasExpensifyOrigin = Url.hasSameExpensifyOrigin(href, CONFIG.EXPENSIFY.EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(href, CONFIG.EXPENSIFY.STAGING_API_ROOT); + return hasExpensifyOrigin && !attrPath.startsWith(CONFIG.EXPENSIFY.CONCIERGE_URL_PATHNAME) && !attrPath.startsWith(CONFIG.EXPENSIFY.DEVPORTAL_URL_PATHNAME) && attrPath; +} + /** * @param {string} href * @param {string} environmentURL * @param {boolean} [isAttachment] */ function openLink(href, environmentURL, isAttachment = false) { - const attrPath = Url.getPathFromURL(href); const hasSameOrigin = Url.hasSameExpensifyOrigin(href, environmentURL); const hasExpensifyOrigin = Url.hasSameExpensifyOrigin(href, CONFIG.EXPENSIFY.EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(href, CONFIG.EXPENSIFY.STAGING_API_ROOT); - const internalNewExpensifyPath = - (Url.hasSameExpensifyOrigin(href, CONST.NEW_EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(href, CONST.STAGING_NEW_EXPENSIFY_URL) || href.startsWith(CONST.DEV_NEW_EXPENSIFY_URL)) && - !CONST.PATHS_TO_TREAT_AS_EXTERNAL.includes(attrPath) - ? attrPath - : ''; - const internalExpensifyPath = - hasExpensifyOrigin && !attrPath.startsWith(CONFIG.EXPENSIFY.CONCIERGE_URL_PATHNAME) && !attrPath.startsWith(CONFIG.EXPENSIFY.DEVPORTAL_URL_PATHNAME) && attrPath; + const internalNewExpensifyPath = getInternalNewExpensifyPath(href); + const internalExpensifyPath = getInternalExpensifyPath(href); // There can be messages from Concierge with links to specific NewDot reports. Those URLs look like this: // https://www.expensify.com.dev/newdotreport?reportID=3429600449838908 and they have a target="_blank" attribute. This is so that when a user is on OldDot, @@ -119,4 +135,4 @@ function openLink(href, environmentURL, isAttachment = false) { openExternalLink(href); } -export {buildOldDotURL, openOldDotLink, openExternalLink, openLink}; +export {buildOldDotURL, openOldDotLink, openExternalLink, openLink, getInternalNewExpensifyPath, getInternalExpensifyPath}; From d838394ffeb085d6f0671e75ab9bd516ce9774a6 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Thu, 2 Nov 2023 16:10:01 -1000 Subject: [PATCH 35/39] use correct espanol translation --- src/languages/en.ts | 2 +- src/languages/es.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index e3a02090f010..0bfee0659668 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1098,7 +1098,7 @@ export default { }, focusModeUpdateModal: { title: 'Welcome to #focus mode!', - prompt: "Read messages will be hidden, unless they have a green dot, which means it's waiting on you. You can change this in your account settings ", + prompt: "Read chats will be hidden, unless they have a green dot, which means there's an action you need to take on them. You can change this in your account settings ", }, notFound: { chatYouLookingForCannotBeFound: 'The chat you are looking for cannot be found.', diff --git a/src/languages/es.ts b/src/languages/es.ts index d8b677680305..27bd0f300add 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1096,8 +1096,8 @@ export default { selectYear: 'Por favor, selecciona un año', }, focusModeUpdateModal: { - title: 'Welcome to #focus mode!', - prompt: "Read messages will be hidden, unless they have a green dot, which means it's waiting on you. You can change this in your account settings.", + title: '¡Bienvenido al modo #concentración!', + prompt: 'Los mensajes leídos se ocultarán, a menos que tengan un punto verde, lo que significa que está esperándote. Puedes cambiar esto en la configuración de tu cuenta ', }, notFound: { chatYouLookingForCannotBeFound: 'El chat que estás buscando no se pudo encontrar.', From 7f25042d484dcb3c5c5603ccbb2b85f4e220df80 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Fri, 3 Nov 2023 10:07:14 -1000 Subject: [PATCH 36/39] Update espanol copy --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 27bd0f300add..feacb721794d 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1097,7 +1097,7 @@ export default { }, focusModeUpdateModal: { title: '¡Bienvenido al modo #concentración!', - prompt: 'Los mensajes leídos se ocultarán, a menos que tengan un punto verde, lo que significa que está esperándote. Puedes cambiar esto en la configuración de tu cuenta ', + prompt: 'Los mensajes leídos se ocultarán, a menos que tengan un punto verde, lo que significa que tienes que tomar una acción en ellos. Puedes cambiar esto en la configuración de tu cuenta ', }, notFound: { chatYouLookingForCannotBeFound: 'El chat que estás buscando no se pudo encontrar.', From 14caac351f28075ac1a0b0b4da260790a67828d6 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 21 Nov 2023 08:38:09 -1000 Subject: [PATCH 37/39] Conflicts somehow got in --- .../HTMLRenderers/AnchorRenderer.js | 55 ------------------- 1 file changed, 55 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js index 7a0574db5b13..8f1406439be9 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.js @@ -7,16 +7,8 @@ import * as HTMLEngineUtils from '@components/HTMLEngineProvider/htmlEngineUtils import Text from '@components/Text'; import useEnvironment from '@hooks/useEnvironment'; import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; -<<<<<<< HEAD -import styles from '@styles/styles'; -import * as Link from '@userActions/Link'; -======= -import * as Url from '@libs/Url'; import useThemeStyles from '@styles/useThemeStyles'; import * as Link from '@userActions/Link'; -import * as Session from '@userActions/Session'; -import CONFIG from '@src/CONFIG'; ->>>>>>> origin import CONST from '@src/CONST'; import htmlRendererPropTypes from './htmlRendererPropTypes'; @@ -29,55 +21,8 @@ function AnchorRenderer(props) { const displayName = lodashGet(props.tnode, 'domNode.children[0].data', ''); const parentStyle = lodashGet(props.tnode, 'parent.styles.nativeTextRet', {}); const attrHref = htmlAttribs.href || ''; -<<<<<<< HEAD const internalNewExpensifyPath = Link.getInternalNewExpensifyPath(attrHref); const internalExpensifyPath = Link.getInternalExpensifyPath(attrHref); -======= - const attrPath = Url.getPathFromURL(attrHref); - const hasSameOrigin = Url.hasSameExpensifyOrigin(attrHref, environmentURL); - const hasExpensifyOrigin = Url.hasSameExpensifyOrigin(attrHref, CONFIG.EXPENSIFY.EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(attrHref, CONFIG.EXPENSIFY.STAGING_API_ROOT); - const internalNewExpensifyPath = - (Url.hasSameExpensifyOrigin(attrHref, CONST.NEW_EXPENSIFY_URL) || - Url.hasSameExpensifyOrigin(attrHref, CONST.STAGING_NEW_EXPENSIFY_URL) || - attrHref.startsWith(CONST.DEV_NEW_EXPENSIFY_URL)) && - !CONST.PATHS_TO_TREAT_AS_EXTERNAL.includes(attrPath) - ? attrPath - : ''; - const internalExpensifyPath = - hasExpensifyOrigin && !attrPath.startsWith(CONFIG.EXPENSIFY.CONCIERGE_URL_PATHNAME) && !attrPath.startsWith(CONFIG.EXPENSIFY.DEVPORTAL_URL_PATHNAME) && attrPath; - const navigateToLink = () => { - // There can be messages from Concierge with links to specific NewDot reports. Those URLs look like this: - // https://www.expensify.com.dev/newdotreport?reportID=3429600449838908 and they have a target="_blank" attribute. This is so that when a user is on OldDot, - // clicking on the link will open the chat in NewDot. However, when a user is in NewDot and clicks on the concierge link, the link needs to be handled differently. - // Normally, the link would be sent to Link.openOldDotLink() and opened in a new tab, and that's jarring to the user. Since the intention is to link to a specific NewDot chat, - // the reportID is extracted from the URL and then opened as an internal link, taking the user straight to the chat in the same tab. - if (hasExpensifyOrigin && attrHref.indexOf('newdotreport?reportID=') > -1) { - const reportID = attrHref.split('newdotreport?reportID=').pop(); - const reportRoute = ROUTES.REPORT_WITH_ID.getRoute(reportID); - Navigation.navigate(reportRoute); - return; - } - - // If we are handling a New Expensify link then we will assume this should be opened by the app internally. This ensures that the links are opened internally via react-navigation - // instead of in a new tab or with a page refresh (which is the default behavior of an anchor tag) - if (internalNewExpensifyPath && hasSameOrigin) { - if (Session.isAnonymousUser() && !Session.canAccessRouteByAnonymousUser(internalNewExpensifyPath)) { - Session.signOutAndRedirectToSignIn(); - return; - } - Navigation.navigate(internalNewExpensifyPath); - return; - } - - // If we are handling an old dot Expensify link we need to open it with openOldDotLink() so we can navigate to it with the user already logged in. - // As attachments also use expensify.com we don't want it working the same as links. - if (internalExpensifyPath && !isAttachment) { - Link.openOldDotLink(internalExpensifyPath); - return; - } - Link.openExternalLink(attrHref); - }; ->>>>>>> origin if (!HTMLEngineUtils.isChildOfComment(props.tnode)) { // This is not a comment from a chat, the AnchorForCommentsOnly uses a Pressable to create a context menu on right click. From 81408216e9431c3d0b93d536d1fde989adb53043 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 21 Nov 2023 08:52:36 -1000 Subject: [PATCH 38/39] Fix typescript issues --- src/libs/actions/Link.ts | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/libs/actions/Link.ts b/src/libs/actions/Link.ts index 37b2291838fd..65dd3519c736 100644 --- a/src/libs/actions/Link.ts +++ b/src/libs/actions/Link.ts @@ -61,34 +61,25 @@ function openOldDotLink(url: string) { ); } -/** - * @param {string} href - * @returns {string} - */ -function getInternalNewExpensifyPath(href) { +function getInternalNewExpensifyPath(href: string) { const attrPath = Url.getPathFromURL(href); return (Url.hasSameExpensifyOrigin(href, CONST.NEW_EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(href, CONST.STAGING_NEW_EXPENSIFY_URL) || href.startsWith(CONST.DEV_NEW_EXPENSIFY_URL)) && - !CONST.PATHS_TO_TREAT_AS_EXTERNAL.includes(attrPath) + !CONST.PATHS_TO_TREAT_AS_EXTERNAL.find(path => path === attrPath) ? attrPath : ''; } -/** - * @param {string} href - * @returns {string} - */ -function getInternalExpensifyPath(href) { +function getInternalExpensifyPath(href: string) { const attrPath = Url.getPathFromURL(href); const hasExpensifyOrigin = Url.hasSameExpensifyOrigin(href, CONFIG.EXPENSIFY.EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(href, CONFIG.EXPENSIFY.STAGING_API_ROOT); - return hasExpensifyOrigin && !attrPath.startsWith(CONFIG.EXPENSIFY.CONCIERGE_URL_PATHNAME) && !attrPath.startsWith(CONFIG.EXPENSIFY.DEVPORTAL_URL_PATHNAME) && attrPath; + if (!hasExpensifyOrigin || attrPath.startsWith(CONFIG.EXPENSIFY.CONCIERGE_URL_PATHNAME) || attrPath.startsWith(CONFIG.EXPENSIFY.DEVPORTAL_URL_PATHNAME)) { + return ''; + } + + return attrPath; } -/** - * @param {string} href - * @param {string} environmentURL - * @param {boolean} [isAttachment] - */ -function openLink(href, environmentURL, isAttachment = false) { +function openLink(href: string, environmentURL: string, isAttachment = false) { const hasSameOrigin = Url.hasSameExpensifyOrigin(href, environmentURL); const hasExpensifyOrigin = Url.hasSameExpensifyOrigin(href, CONFIG.EXPENSIFY.EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(href, CONFIG.EXPENSIFY.STAGING_API_ROOT); const internalNewExpensifyPath = getInternalNewExpensifyPath(href); @@ -101,7 +92,7 @@ function openLink(href, environmentURL, isAttachment = false) { // the reportID is extracted from the URL and then opened as an internal link, taking the user straight to the chat in the same tab. if (hasExpensifyOrigin && href.indexOf('newdotreport?reportID=') > -1) { const reportID = href.split('newdotreport?reportID=').pop(); - const reportRoute = ROUTES.REPORT_WITH_ID.getRoute(reportID); + const reportRoute = ROUTES.REPORT_WITH_ID.getRoute(reportID ?? ''); Navigation.navigate(reportRoute); return; } From eb99338321da2002cc4d6fa29e6fafe3a396c411 Mon Sep 17 00:00:00 2001 From: Marc Glasser Date: Tue, 21 Nov 2023 08:57:17 -1000 Subject: [PATCH 39/39] Run prettier --- src/libs/actions/Link.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Link.ts b/src/libs/actions/Link.ts index 65dd3519c736..5ee15c1cd1d0 100644 --- a/src/libs/actions/Link.ts +++ b/src/libs/actions/Link.ts @@ -64,7 +64,7 @@ function openOldDotLink(url: string) { function getInternalNewExpensifyPath(href: string) { const attrPath = Url.getPathFromURL(href); return (Url.hasSameExpensifyOrigin(href, CONST.NEW_EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(href, CONST.STAGING_NEW_EXPENSIFY_URL) || href.startsWith(CONST.DEV_NEW_EXPENSIFY_URL)) && - !CONST.PATHS_TO_TREAT_AS_EXTERNAL.find(path => path === attrPath) + !CONST.PATHS_TO_TREAT_AS_EXTERNAL.find((path) => path === attrPath) ? attrPath : ''; }