From 2cde96c0d49f2679704855b635ad0e20f95757bf Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 29 Sep 2023 16:56:37 +0500 Subject: [PATCH 01/21] feat: add new key in onyx for draft report ids --- src/ONYXKEYS.ts | 4 +++ src/libs/actions/DraftReports.ts | 29 ++++++++++++++++++++ src/pages/home/sidebar/SidebarLinksData.js | 32 ++++++++++++++++++---- 3 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 src/libs/actions/DraftReports.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index a1afc4fef2c1..c9b04c4c217a 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -10,6 +10,9 @@ const ONYXKEYS = { /** Holds information about the users account that is logging in */ ACCOUNT: 'account', + /** Holds the reportIDs which are currently in draft */ + DRAFT_REPORT_IDS: 'draftReportIDs', + /** Holds the reportID for the report between the user and their account manager */ ACCOUNT_MANAGER_REPORT_ID: 'accountManagerReportID', @@ -297,6 +300,7 @@ type OnyxKey = DeepValueOf>; type OnyxValues = { [ONYXKEYS.ACCOUNT]: OnyxTypes.Account; + [ONYXKEYS.DRAFT_REPORT_IDS]: Record; [ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID]: string; [ONYXKEYS.NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER]: boolean; [ONYXKEYS.ACTIVE_CLIENTS]: string[]; diff --git a/src/libs/actions/DraftReports.ts b/src/libs/actions/DraftReports.ts new file mode 100644 index 000000000000..dc1e7a8066f8 --- /dev/null +++ b/src/libs/actions/DraftReports.ts @@ -0,0 +1,29 @@ +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '../../ONYXKEYS'; +import DraftReportUtils from '../DraftReportUtils'; + +const draftReportUtils = DraftReportUtils.getInstance(); + +/** + * Immediate indication whether the report has a draft. + * + * @param reportID + * @param draft + */ +function setDraftStatusForReportID(reportID: string, draft: boolean) { + const draftReportIDs = {...draftReportUtils.getDraftReportIDs()}; + + if (draftReportIDs[reportID] && draft) { + return; + } + + if (draftReportIDs[reportID] && !draft) { + delete draftReportIDs[reportID]; + Onyx.set(ONYXKEYS.DRAFT_REPORT_IDS, draftReportIDs); + } else { + draftReportIDs[reportID] = draft; + Onyx.merge(ONYXKEYS.DRAFT_REPORT_IDS, {[reportID]: draft}); + } +} + +export default setDraftStatusForReportID; diff --git a/src/pages/home/sidebar/SidebarLinksData.js b/src/pages/home/sidebar/SidebarLinksData.js index 243ba24cdd00..c204dddf872a 100644 --- a/src/pages/home/sidebar/SidebarLinksData.js +++ b/src/pages/home/sidebar/SidebarLinksData.js @@ -52,6 +52,10 @@ const propTypes = { /** The policies which the user has access to */ // eslint-disable-next-line react/forbid-prop-types policies: PropTypes.object, + + /** Holds the reportIDs which are in draft */ + // eslint-disable-next-line react/forbid-prop-types + draftReportIDs: PropTypes.object, }; const defaultProps = { @@ -61,15 +65,29 @@ const defaultProps = { priorityMode: CONST.PRIORITY_MODE.DEFAULT, betas: [], policies: [], + draftReportIDs: {}, }; -function SidebarLinksData({isFocused, allReportActions, betas, chatReports, currentReportID, insets, isLoadingReportData, isSmallScreenWidth, onLinkClick, policies, priorityMode}) { +function SidebarLinksData({ + isFocused, + allReportActions, + draftReportIDs, + betas, + chatReports, + currentReportID, + insets, + isLoadingReportData, + isSmallScreenWidth, + onLinkClick, + policies, + priorityMode, +}) { const {translate} = useLocalize(); const reportIDsRef = useRef(null); const isLoading = SessionUtils.didUserLogInDuringSession() && isLoadingReportData; const optionListItems = useMemo(() => { - const reportIDs = SidebarUtils.getOrderedReportIDs(null, chatReports, betas, policies, priorityMode, allReportActions); + const reportIDs = SidebarUtils.getOrderedReportIDs(null, draftReportIDs, chatReports, betas, policies, priorityMode, allReportActions); if (deepEqual(reportIDsRef.current, reportIDs)) { return reportIDsRef.current; } @@ -79,7 +97,7 @@ function SidebarLinksData({isFocused, allReportActions, betas, chatReports, curr reportIDsRef.current = reportIDs; } return reportIDsRef.current || []; - }, [allReportActions, betas, chatReports, policies, priorityMode, isLoading]); + }, [allReportActions, betas, chatReports, draftReportIDs, policies, priorityMode, isLoading]); // 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 @@ -88,10 +106,10 @@ function SidebarLinksData({isFocused, allReportActions, betas, chatReports, curr // case we re-generate the list a 2nd time with the current report included. const optionListItemsWithCurrentReport = useMemo(() => { if (currentReportID && !_.contains(optionListItems, currentReportID)) { - return SidebarUtils.getOrderedReportIDs(currentReportID, chatReports, betas, policies, priorityMode, allReportActions); + return SidebarUtils.getOrderedReportIDs(currentReportID, draftReportIDs, chatReports, betas, policies, priorityMode, allReportActions); } return optionListItems; - }, [currentReportID, optionListItems, chatReports, betas, policies, priorityMode, allReportActions]); + }, [currentReportID, optionListItems, chatReports, betas, draftReportIDs, policies, priorityMode, allReportActions]); const currentReportIDRef = useRef(currentReportID); currentReportIDRef.current = currentReportID; @@ -133,7 +151,6 @@ const chatReportSelector = (report) => reportID: report.reportID, participants: report.participants, participantAccountIDs: report.participantAccountIDs, - hasDraft: report.hasDraft, isPinned: report.isPinned, isHidden: report.isHidden, errorFields: { @@ -202,6 +219,9 @@ export default compose( isLoadingReportData: { key: ONYXKEYS.IS_LOADING_REPORT_DATA, }, + draftReportIDs: { + key: ONYXKEYS.DRAFT_REPORT_IDS, + }, priorityMode: { key: ONYXKEYS.NVP_PRIORITY_MODE, }, From 877a5f94828aaab64a45d0fe1c9d228afaa223e0 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 29 Sep 2023 17:01:58 +0500 Subject: [PATCH 02/21] feat: add singleton class for draft report utils --- src/libs/DraftReportUtils.ts | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/libs/DraftReportUtils.ts diff --git a/src/libs/DraftReportUtils.ts b/src/libs/DraftReportUtils.ts new file mode 100644 index 000000000000..71d16a1fce66 --- /dev/null +++ b/src/libs/DraftReportUtils.ts @@ -0,0 +1,40 @@ +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '../ONYXKEYS'; + +class DraftReportUtils { + private static instance: DraftReportUtils; + + private draftReportIDs: Record; + + private constructor() { + DraftReportUtils.instance = this; + + this.draftReportIDs = {}; + + this.subscribeToDraftReportIDs(); + } + + public static getInstance(): DraftReportUtils { + // Ensure singleton instance + return DraftReportUtils.instance ?? new DraftReportUtils(); + } + + private subscribeToDraftReportIDs() { + Onyx.connect({ + key: ONYXKEYS.DRAFT_REPORT_IDS, + callback: (val) => { + if (!val) { + return; + } + + this.draftReportIDs = val; + }, + }); + } + + getDraftReportIDs() { + return this.draftReportIDs; + } +} + +export default DraftReportUtils; From 6903d5bb79e066ba53d32c3c2143ba92ecf43b86 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 29 Sep 2023 17:11:22 +0500 Subject: [PATCH 03/21] feat: confirm to new draft repord id onyx key --- src/components/LHNOptionsList/OptionRowLHN.js | 17 +++++++++-- .../LHNOptionsList/OptionRowLHNData.js | 4 +-- src/libs/ReportActionsUtils.js | 19 +++++++++---- src/libs/ReportUtils.js | 28 +++++++++++++++---- src/libs/actions/Policy.js | 11 +++++--- .../ComposerWithSuggestions.js | 19 ++++++++----- src/types/onyx/Report.ts | 1 - 7 files changed, 72 insertions(+), 27 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js index 3cfd7c4c4138..dddee8586eb8 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.js +++ b/src/components/LHNOptionsList/OptionRowLHN.js @@ -3,6 +3,7 @@ import React, {useState, useRef} from 'react'; import PropTypes from 'prop-types'; import {View, StyleSheet} from 'react-native'; import lodashGet from 'lodash/get'; +import {withOnyx} from 'react-native-onyx'; import * as optionRowStyles from '../../styles/optionRowStyles'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; @@ -25,6 +26,7 @@ import * as ReportUtils from '../../libs/ReportUtils'; import useLocalize from '../../hooks/useLocalize'; import Permissions from '../../libs/Permissions'; import Tooltip from '../Tooltip'; +import ONYXKEYS from '../../ONYXKEYS'; const propTypes = { /** Style for hovered state */ @@ -51,6 +53,9 @@ const propTypes = { /** The item that should be rendered */ // eslint-disable-next-line react/forbid-prop-types optionItem: PropTypes.object, + + // eslint-disable-next-line react/forbid-prop-types + draftReportIDs: PropTypes.object, }; const defaultProps = { @@ -61,6 +66,7 @@ const defaultProps = { optionItem: null, isFocused: false, betas: [], + draftReportIDs: {}, }; function OptionRowLHN(props) { @@ -135,6 +141,7 @@ function OptionRowLHN(props) { const formattedDate = DateUtils.getStatusUntilDate(statusClearAfterDate); const statusContent = formattedDate ? `${statusText} (${formattedDate})` : statusText; const isStatusVisible = Permissions.canUseCustomStatus(props.betas) && !!emojiCode && ReportUtils.isOneOnOneChat(optionItem); + const isDraft = props.draftReportIDs[props.reportID]; return ( )} - {optionItem.hasDraftComment && optionItem.isAllowedToComment && ( + {isDraft && optionItem.isAllowedToComment && ( { if (!key || !report) { return; @@ -47,7 +48,7 @@ Onyx.connect({ * @returns {Boolean} */ function isCreatedAction(reportAction) { - return lodashGet(reportAction, 'actionName') === CONST.REPORT.ACTIONS.TYPE.CREATED; + return reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED; } /** @@ -421,7 +422,10 @@ function getLastVisibleAction(reportID, actionsToMerge = {}) { */ function getLastVisibleMessage(reportID, actionsToMerge = {}) { const lastVisibleAction = getLastVisibleAction(reportID, actionsToMerge); - const message = lodashGet(lastVisibleAction, ['message', 0], {}); + let message = {}; + if (lastVisibleAction.message) { + message = lastVisibleAction.message[0]; + } if (isReportMessageAttachment(message)) { return { @@ -437,9 +441,14 @@ function getLastVisibleMessage(reportID, actionsToMerge = {}) { }; } - const messageText = lodashGet(message, 'text', ''); + let messageText = message.text || ''; + + if (messageText) { + messageText = String(messageText).replace(CONST.REGEX.AFTER_FIRST_LINE_BREAK, '').substring(0, CONST.REPORT.LAST_MESSAGE_TEXT_MAX_LENGTH).trim(); + } + return { - lastMessageText: String(messageText).replace(CONST.REGEX.AFTER_FIRST_LINE_BREAK, '').substring(0, CONST.REPORT.LAST_MESSAGE_TEXT_MAX_LENGTH).trim(), + lastMessageText: messageText, }; } @@ -638,7 +647,7 @@ function isTaskAction(reportAction) { * @returns {[Object]} */ function getAllReportActions(reportID) { - return lodashGet(allReportActions, reportID, []); + return allReportActions[reportID] || []; } /** diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index f41ad0b75b42..431fb05a1bb7 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -23,6 +23,7 @@ import isReportMessageAttachment from './isReportMessageAttachment'; import * as defaultWorkspaceAvatars from '../components/Icon/WorkspaceDefaultAvatars'; import * as CurrencyUtils from './CurrencyUtils'; import * as UserUtils from './UserUtils'; +import DraftReportUtils from './DraftReportUtils'; let currentUserEmail; let currentUserAccountID; @@ -52,13 +53,16 @@ Onyx.connect({ }, }); -let allReports; +let allReports = {}; + Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, waitForCollectionCallback: true, callback: (val) => (allReports = val), }); +const draftReportUtils = DraftReportUtils.getInstance(); + let doesDomainHaveApprovedAccountant; Onyx.connect({ key: ONYXKEYS.ACCOUNT, @@ -1201,8 +1205,11 @@ function getDisplayNamesWithTooltips(personalDetailsList, isMultipleParticipantR * @returns {Object} */ function getReport(reportID) { - // Deleted reports are set to null and lodashGet will still return null in that case, so we need to add an extra check - return lodashGet(allReports, `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {}) || {}; + /** + * using typical string concatenation here due to performance issues + * with template literals. + */ + return allReports[ONYXKEYS.COLLECTION.REPORT + reportID] || {}; } /** @@ -1295,14 +1302,23 @@ function getMoneyRequestTotal(report, allReportsDict = null) { * @returns {String} */ function getPolicyExpenseChatName(report, policy = undefined) { - const reportOwnerDisplayName = getDisplayNameForParticipant(report.ownerAccountID) || lodashGet(allPersonalDetails, [report.ownerAccountID, 'login']) || report.reportName; + const ownerAccountID = report.ownerAccountID; + const reportOwnerDisplayName = getDisplayNameForParticipant(report.ownerAccountID) || allPersonalDetails[ownerAccountID].login || report.reportName; // If the policy expense chat is owned by this user, use the name of the policy as the report name. if (report.isOwnPolicyExpenseChat) { return getPolicyName(report, false, policy); } - const policyExpenseChatRole = lodashGet(allPolicies, [`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, 'role']) || 'user'; + let policyExpenseChatRole = 'user'; + /** + * using typical string concatenation here due to performance issues + * with template literals. + */ + const policyItem = allPolicies[ONYXKEYS.COLLECTION.POLICY + report.policyID]; + if (policyItem) { + policyExpenseChatRole = policyItem.role || 'user'; + } // If this user is not admin and this policy expense chat has been archived because of account merging, this must be an old workspace chat // of the account which was merged into the current user's account. Use the name of the policy as the name of the report. @@ -2971,7 +2987,7 @@ function shouldReportBeInOptionList(report, currentReportId, isInGSDMode, betas, } // Include reports that are relevant to the user in any view mode. Criteria include having a draft, having an outstanding IOU, or being assigned to an open task. - if (report.hasDraft || isWaitingForIOUActionFromCurrentUser(report) || isWaitingForTaskCompleteFromAssignee(report)) { + if (draftReportUtils.getDraftReportIDs()[report.reportID] || isWaitingForIOUActionFromCurrentUser(report) || isWaitingForTaskCompleteFromAssignee(report)) { return true; } const lastVisibleMessage = ReportActionsUtils.getLastVisibleMessage(report.reportID); diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index fcce909c5582..b3d68ff4395d 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -13,6 +13,9 @@ import * as ErrorUtils from '../ErrorUtils'; import * as ReportUtils from '../ReportUtils'; import * as PersonalDetailsUtils from '../PersonalDetailsUtils'; import Log from '../Log'; +import DraftReportUtils from '../DraftReportUtils'; + +const draftReportUtils = DraftReportUtils.getInstance(); const allPolicies = {}; Onyx.connect({ @@ -29,12 +32,14 @@ Onyx.connect({ const policyReports = ReportUtils.getAllPolicyReports(policyID); const cleanUpMergeQueries = {}; const cleanUpSetQueries = {}; + const draftReportIDs = {...draftReportUtils.getDraftReportIDs()}; _.each(policyReports, ({reportID}) => { - cleanUpMergeQueries[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] = {hasDraft: false}; cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; + delete draftReportIDs[reportID]; }); Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); Onyx.multiSet(cleanUpSetQueries); + Onyx.set(ONYXKEYS.DRAFT_REPORT_IDS, draftReportIDs); delete allPolicies[key]; return; } @@ -96,7 +101,6 @@ function deleteWorkspace(policyID, reports, policyName) { value: { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS.CLOSED, - hasDraft: false, oldPolicyName: allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`].name, }, })), @@ -121,13 +125,12 @@ function deleteWorkspace(policyID, reports, policyName) { // Restore the old report stateNum and statusNum const failureData = [ - ..._.map(reports, ({reportID, stateNum, statusNum, hasDraft, oldPolicyName}) => ({ + ..._.map(reports, ({reportID, stateNum, statusNum, oldPolicyName}) => ({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { stateNum, statusNum, - hasDraft, oldPolicyName, }, })), diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index d04983dc2f75..1d7d1fbcb001 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -34,6 +34,7 @@ import withKeyboardState from '../../../../components/withKeyboardState'; import {propTypes, defaultProps} from './composerWithSuggestionsProps'; import focusWithDelay from '../../../../libs/focusWithDelay'; import useDebounce from '../../../../hooks/useDebounce'; +import setDraftStatusForReportID from '../../../../libs/actions/DraftReports'; const {RNTextInputReset} = NativeModules; @@ -203,7 +204,13 @@ function ComposerWithSuggestions({ debouncedUpdateFrequentlyUsedEmojis(); } - setIsCommentEmpty(!!newComment.match(/^(\s)*$/)); + const isNewCommentEmpty = !!newComment.match(/^(\s)*$/); + const isPrevCommentEmpty = !!commentRef.current.match(/^(\s)*$/); + + /** Only update isCommentEmpty state if it's different from previous one */ + if (isNewCommentEmpty !== isPrevCommentEmpty) { + setIsCommentEmpty(isNewCommentEmpty); + } setValue(newComment); if (commentValue !== newComment) { // Ensure emoji suggestions are hidden even when the selection is not changed (so calculateEmojiSuggestion would not be called). @@ -220,12 +227,11 @@ function ComposerWithSuggestions({ // Indicate that draft has been created. if (commentRef.current.length === 0 && newComment.length !== 0) { - Report.setReportWithDraft(reportID, true); + setDraftStatusForReportID(reportID, true); } - // The draft has been deleted. - if (newComment.length === 0) { - Report.setReportWithDraft(reportID, false); + else if (newComment.length === 0) { + setDraftStatusForReportID(reportID, false); } commentRef.current = newComment; @@ -469,8 +475,7 @@ function ComposerWithSuggestions({ if (value.length === 0) { return; } - Report.setReportWithDraft(reportID, true); - + setDraftStatusForReportID(reportID, true); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 46e51fe41238..1e24a5805084 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -66,7 +66,6 @@ type Report = { parentReportID?: string; parentReportActionID?: string; isOptimisticReport?: boolean; - hasDraft?: boolean; managerID?: number; lastVisibleActionLastModified?: string; displayName?: string; From 64b095fd30844b5fb7d2c8951a07289e9277787f Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 29 Sep 2023 17:11:46 +0500 Subject: [PATCH 04/21] feat: add waitForCollectionCallback --- src/libs/OptionsListUtils.js | 2 +- src/libs/actions/Report.js | 14 ++------------ src/libs/actions/Welcome.js | 1 + 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index e0f334ca36af..6382def58648 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -83,6 +83,7 @@ Onyx.connect({ const policyExpenseReports = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, callback: (report, key) => { if (!ReportUtils.isPolicyExpenseChat(report)) { return; @@ -486,7 +487,6 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { result.ownerAccountID = report.ownerAccountID; result.reportID = report.reportID; result.isUnread = ReportUtils.isUnread(report); - result.hasDraftComment = report.hasDraft; result.isPinned = report.isPinned; result.iouReportID = report.iouReportID; result.keyForList = String(report.reportID); diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 66008ae5ae2a..941d7dce7b49 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -64,6 +64,7 @@ Onyx.connect({ const currentReportData = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, callback: (data, key) => { if (!key || !data) { return; @@ -919,17 +920,6 @@ function saveReportCommentNumberOfLines(reportID, numberOfLines) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}`, numberOfLines); } -/** - * Immediate indication whether the report has a draft comment. - * - * @param {String} reportID - * @param {Boolean} hasDraft - * @returns {Promise} - */ -function setReportWithDraft(reportID, hasDraft) { - return Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {hasDraft}); -} - /** * Broadcasts whether or not a user is typing on a report over the report's private pusher channel. * @@ -994,6 +984,7 @@ function handleReportChanged(report) { Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, callback: handleReportChanged, }); @@ -2189,7 +2180,6 @@ export { saveReportActionDraftNumberOfLines, deleteReportComment, navigateToConciergeChat, - setReportWithDraft, addPolicyReport, deleteReport, navigateToConciergeChatAndDeleteReport, diff --git a/src/libs/actions/Welcome.js b/src/libs/actions/Welcome.js index 8e1832edb9a7..fac2d031fe1e 100644 --- a/src/libs/actions/Welcome.js +++ b/src/libs/actions/Welcome.js @@ -58,6 +58,7 @@ Onyx.connect({ const allReports = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, + waitForCollectionCallback: true, initWithStoredValues: false, callback: (val, key) => { if (!val || !key) { From 268e57af777a3a61d9b0bb10a46596952e8d3f60 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 29 Sep 2023 17:12:27 +0500 Subject: [PATCH 05/21] perf: call updateUnread method if count is changed --- src/libs/UnreadIndicatorUpdater/index.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/libs/UnreadIndicatorUpdater/index.js b/src/libs/UnreadIndicatorUpdater/index.js index 09fa82612314..dcfedce83d75 100644 --- a/src/libs/UnreadIndicatorUpdater/index.js +++ b/src/libs/UnreadIndicatorUpdater/index.js @@ -1,14 +1,26 @@ import _ from 'underscore'; import Onyx from 'react-native-onyx'; +import {InteractionManager} from 'react-native'; import ONYXKEYS from '../../ONYXKEYS'; import updateUnread from './updateUnread/index'; import * as ReportUtils from '../ReportUtils'; +let previousUnreadCount = 0; + Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, waitForCollectionCallback: true, callback: (reportsFromOnyx) => { - const unreadReports = _.filter(reportsFromOnyx, ReportUtils.isUnread); - updateUnread(_.size(unreadReports)); + if (!reportsFromOnyx) { + return; + } + + InteractionManager.runAfterInteractions(() => { + const unreadReportsCount = _.filter(reportsFromOnyx, ReportUtils.isUnread).length || 0; + if (previousUnreadCount !== unreadReportsCount) { + previousUnreadCount = unreadReportsCount; + updateUnread(unreadReportsCount); + } + }); }, }); From 740700d7386a2489b88f03f95b20ca2798f6ad15 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 29 Sep 2023 17:13:24 +0500 Subject: [PATCH 06/21] perf: use dict keys instead of whole dict and refactor the foreach loop --- src/libs/SidebarUtils.js | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.js index 7a32db660021..75f1782670b2 100644 --- a/src/libs/SidebarUtils.js +++ b/src/libs/SidebarUtils.js @@ -103,6 +103,7 @@ let hasInitialReportActions = false; /** * @param {String} currentReportId + * @param {Object} draftReportIDs * @param {Object} allReportsDict * @param {Object} betas * @param {String[]} policies @@ -110,11 +111,12 @@ let hasInitialReportActions = false; * @param {Object} allReportActions * @returns {String[]} An array of reportIDs sorted in the proper order */ -function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, priorityMode, allReportActions) { +function getOrderedReportIDs(currentReportId, draftReportIDs, allReportsDict, betas, policies, priorityMode, allReportActions) { + const allReportsDictKeys = Object.keys(allReportsDict); // Generate a unique cache key based on the function arguments const cachedReportsKey = JSON.stringify( // eslint-disable-next-line es/no-optional-chaining - [currentReportId, allReportsDict, betas, policies, priorityMode, allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentReportId}`]?.length || 1], + [currentReportId, allReportsDictKeys, betas, draftReportIDs, policies, priorityMode, allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentReportId}`]?.length || 1], (key, value) => { /** * Exclude 'participantAccountIDs', 'participants' and 'lastMessageText' not to overwhelm a cached key value with huge data, @@ -149,18 +151,6 @@ function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, p } } - // There are a few properties that need to be calculated for the report which are used when sorting reports. - reportsToDisplay.forEach((report) => { - // Normally, the spread operator would be used here to clone the report and prevent the need to reassign the params. - // However, this code needs to be very performant to handle thousands of reports, so in the interest of speed, we're just going to disable this lint rule and add - // the reportDisplayName property to the report object directly. - // eslint-disable-next-line no-param-reassign - report.displayName = ReportUtils.getReportName(report); - - // eslint-disable-next-line no-param-reassign - report.iouReportAmount = ReportUtils.getMoneyRequestTotal(report, allReportsDict); - }); - // The LHN is split into five distinct groups, and each group is sorted a little differently. The groups will ALWAYS be in this order: // 1. Pinned - Always sorted by reportDisplayName // 2. Outstanding IOUs - Always sorted by iouReportAmount with the largest amounts at the top of the group @@ -171,17 +161,29 @@ function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, p // 5. Archived reports // - Sorted by lastVisibleActionCreated in default (most recent) view mode // - Sorted by reportDisplayName in GSD (focus) view mode + const pinnedReports = []; const outstandingIOUReports = []; const draftReports = []; const nonArchivedReports = []; const archivedReports = []; + + // There are a few properties that need to be calculated for the report which are used when sorting reports. reportsToDisplay.forEach((report) => { + // Normally, the spread operator would be used here to clone the report and prevent the need to reassign the params. + // However, this code needs to be very performant to handle thousands of reports, so in the interest of speed, we're just going to disable this lint rule and add + // the reportDisplayName property to the report object directly. + // eslint-disable-next-line no-param-reassign + report.displayName = ReportUtils.getReportName(report); + + // eslint-disable-next-line no-param-reassign + report.iouReportAmount = ReportUtils.getMoneyRequestTotal(report, allReportsDict); + if (report.isPinned) { pinnedReports.push(report); } else if (ReportUtils.isWaitingForIOUActionFromCurrentUser(report)) { outstandingIOUReports.push(report); - } else if (report.hasDraft) { + } else if (draftReportIDs[report.reportID]) { draftReports.push(report); } else if (ReportUtils.isArchivedRoom(report)) { archivedReports.push(report); @@ -296,7 +298,6 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale, result.statusNum = report.statusNum; result.isUnread = ReportUtils.isUnread(report); result.isUnreadWithMention = ReportUtils.isUnreadWithMention(report); - result.hasDraftComment = report.hasDraft; result.isPinned = report.isPinned; result.iouReportID = report.iouReportID; result.keyForList = String(report.reportID); From cd0346e0e9dffecf7d11fa0d13586a2c7e8824b3 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 2 Oct 2023 11:04:17 +0500 Subject: [PATCH 07/21] fix: prettier fixes --- src/libs/ReportUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 431fb05a1bb7..a4554197c584 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1208,7 +1208,7 @@ function getReport(reportID) { /** * using typical string concatenation here due to performance issues * with template literals. - */ + */ return allReports[ONYXKEYS.COLLECTION.REPORT + reportID] || {}; } @@ -1314,7 +1314,7 @@ function getPolicyExpenseChatName(report, policy = undefined) { /** * using typical string concatenation here due to performance issues * with template literals. - */ + */ const policyItem = allPolicies[ONYXKEYS.COLLECTION.POLICY + report.policyID]; if (policyItem) { policyExpenseChatRole = policyItem.role || 'user'; From 03b62212e1ae0f97c92019f8585ffca275a67999 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 3 Oct 2023 10:40:54 +0500 Subject: [PATCH 08/21] test: fix unit tests --- src/components/LHNOptionsList/OptionRowLHN.js | 17 ++++------------- .../LHNOptionsList/OptionRowLHNData.js | 8 +++++++- src/components/optionPropTypes.js | 3 --- src/libs/OptionsListUtils.js | 1 - src/libs/ReportUtils.js | 4 ++++ tests/unit/OptionsListUtilsTest.js | 1 - tests/utils/LHNTestUtils.js | 4 +--- 7 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js index dddee8586eb8..0f0b196d715a 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.js +++ b/src/components/LHNOptionsList/OptionRowLHN.js @@ -3,7 +3,6 @@ import React, {useState, useRef} from 'react'; import PropTypes from 'prop-types'; import {View, StyleSheet} from 'react-native'; import lodashGet from 'lodash/get'; -import {withOnyx} from 'react-native-onyx'; import * as optionRowStyles from '../../styles/optionRowStyles'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; @@ -26,7 +25,6 @@ import * as ReportUtils from '../../libs/ReportUtils'; import useLocalize from '../../hooks/useLocalize'; import Permissions from '../../libs/Permissions'; import Tooltip from '../Tooltip'; -import ONYXKEYS from '../../ONYXKEYS'; const propTypes = { /** Style for hovered state */ @@ -54,8 +52,7 @@ const propTypes = { // eslint-disable-next-line react/forbid-prop-types optionItem: PropTypes.object, - // eslint-disable-next-line react/forbid-prop-types - draftReportIDs: PropTypes.object, + hasDraft: PropTypes.bool, }; const defaultProps = { @@ -66,7 +63,7 @@ const defaultProps = { optionItem: null, isFocused: false, betas: [], - draftReportIDs: {}, + hasDraft: false, }; function OptionRowLHN(props) { @@ -141,7 +138,7 @@ function OptionRowLHN(props) { const formattedDate = DateUtils.getStatusUntilDate(statusClearAfterDate); const statusContent = formattedDate ? `${statusText} (${formattedDate})` : statusText; const isStatusVisible = Permissions.canUseCustomStatus(props.betas) && !!emojiCode && ReportUtils.isOneOnOneChat(optionItem); - const isDraft = props.draftReportIDs[props.reportID]; + const isDraft = props.hasDraft; return ( { - if (!optionItem || optionItem.hasDraftComment || !comment || comment.length <= 0 || isFocused) { + if (!optionItem || hasDraft || !comment || comment.length <= 0 || isFocused) { return; } setDraftStatusForReportID(reportID, true); @@ -131,6 +133,7 @@ function OptionRowLHNData({ {...propsToForward} isFocused={isFocused} optionItem={optionItem} + hasDraft={hasDraft} /> ); } @@ -181,6 +184,9 @@ export default React.memo( }, }), withOnyx({ + draftReportIDs: { + key: ONYXKEYS.DRAFT_REPORT_IDS, + }, fullReport: { key: (props) => ONYXKEYS.COLLECTION.REPORT + props.reportID, }, diff --git a/src/components/optionPropTypes.js b/src/components/optionPropTypes.js index 709298036f07..6f84fff24a52 100644 --- a/src/components/optionPropTypes.js +++ b/src/components/optionPropTypes.js @@ -25,9 +25,6 @@ export default PropTypes.shape({ // reportID (only present when there is a matching report) reportID: PropTypes.string, - // Whether the report has a draft comment or not - hasDraftComment: PropTypes.bool, - // Key used internally by React keyForList: PropTypes.string, diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 6382def58648..eacbb1648122 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -444,7 +444,6 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { login: null, reportID: null, phoneNumber: null, - hasDraftComment: false, keyForList: null, searchText: null, isDefaultRoom: false, diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index a4554197c584..f25bb0ef0fa5 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1209,6 +1209,10 @@ function getReport(reportID) { * using typical string concatenation here due to performance issues * with template literals. */ + if (!allReports) { + return {}; + } + return allReports[ONYXKEYS.COLLECTION.REPORT + reportID] || {}; } diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index 6f20e48835fd..79e2cb79077a 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -16,7 +16,6 @@ describe('OptionsListUtils', () => { reportID: 1, participantAccountIDs: [2, 1], reportName: 'Iron Man, Mister Fantastic', - hasDraft: true, }, 2: { lastReadTime: '2021-01-14 11:25:39.296', diff --git a/tests/utils/LHNTestUtils.js b/tests/utils/LHNTestUtils.js index 7cb69b23a578..2dbc390cb283 100644 --- a/tests/utils/LHNTestUtils.js +++ b/tests/utils/LHNTestUtils.js @@ -149,10 +149,9 @@ function getFakeReportAction(actor = 'email1@test.com', millisecondsInThePast = * @param {boolean} hasAddWorkspaceError * @param {boolean} isUnread * @param {boolean} isPinned - * @param {boolean} hasDraft * @returns {Object} */ -function getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft) { +function getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned) { return { ...getFakeReport([1, 2], 0, isUnread), type: CONST.REPORT.TYPE.CHAT, @@ -161,7 +160,6 @@ function getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorksp stateNum: isArchived ? CONST.REPORT.STATE_NUM.SUBMITTED : 0, errorFields: hasAddWorkspaceError ? {addWorkspaceRoom: 'blah'} : null, isPinned, - hasDraft, }; } From dbeef9830a3b7bfdfee9f63a63000d77deceb549 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 3 Oct 2023 11:43:52 +0500 Subject: [PATCH 09/21] test: fix Sidebar tests --- src/libs/ReportUtils.js | 4 +- tests/unit/SidebarFilterTest.js | 21 ++++++----- tests/unit/SidebarOrderTest.js | 66 ++++++++++++++------------------- 3 files changed, 41 insertions(+), 50 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index f25bb0ef0fa5..529bee905924 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1307,7 +1307,9 @@ function getMoneyRequestTotal(report, allReportsDict = null) { */ function getPolicyExpenseChatName(report, policy = undefined) { const ownerAccountID = report.ownerAccountID; - const reportOwnerDisplayName = getDisplayNameForParticipant(report.ownerAccountID) || allPersonalDetails[ownerAccountID].login || report.reportName; + const personalDetails = allPersonalDetails[ownerAccountID]; + const login = personalDetails ? personalDetails.login : null; + const reportOwnerDisplayName = getDisplayNameForParticipant(report.ownerAccountID) || login || report.reportName; // If the policy expense chat is owned by this user, use the name of the policy as the report name. if (report.isOwnPolicyExpenseChat) { diff --git a/tests/unit/SidebarFilterTest.js b/tests/unit/SidebarFilterTest.js index 18e499d89293..982393b3e191 100644 --- a/tests/unit/SidebarFilterTest.js +++ b/tests/unit/SidebarFilterTest.js @@ -23,6 +23,7 @@ const ONYXKEYS = { POLICY: 'policy_', }, NETWORK: 'network', + DRAFT_REPORT_IDS: 'draftReportIDs', }; describe('Sidebar', () => { @@ -100,12 +101,7 @@ describe('Sidebar', () => { it('includes an empty chat report if it has a draft', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); - // Given a new report with a draft text - const report = { - ...LHNTestUtils.getFakeReport([1, 2], 0), - hasDraft: true, - }; - + const report = LHNTestUtils.getFakeReport([1, 2], 0); return ( waitForBatchedUpdates() // When Onyx is updated to contain that report @@ -114,6 +110,8 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, + // Set the draft status for the given reportID + [ONYXKEYS.DRAFT_REPORT_IDS]: {[report.reportID]: true}, }), ) @@ -341,6 +339,8 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, + // Set the draft status for the given reportID + [ONYXKEYS.DRAFT_REPORT_IDS]: {[report2.reportID]: boolArr[boolArr.length - 1]}, }), ) // Then depending on the outcome, either one or two reports are visible @@ -435,10 +435,7 @@ describe('Sidebar', () => { it('always shows pinned and draft chats', () => { // Given a draft report and a pinned report - const draftReport = { - ...LHNTestUtils.getFakeReport([1, 2]), - hasDraft: true, - }; + const draftReport = LHNTestUtils.getFakeReport([1, 2]); const pinnedReport = { ...LHNTestUtils.getFakeReport([3, 4]), isPinned: true, @@ -455,6 +452,8 @@ describe('Sidebar', () => { [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, [`${ONYXKEYS.COLLECTION.REPORT}${draftReport.reportID}`]: draftReport, [`${ONYXKEYS.COLLECTION.REPORT}${pinnedReport.reportID}`]: pinnedReport, + // Set the draft status for the given reportID + [ONYXKEYS.DRAFT_REPORT_IDS]: {[draftReport.reportID]: true}, }), ) @@ -666,6 +665,8 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, + // Set the draft status for the given reportID + [ONYXKEYS.DRAFT_REPORT_IDS]: {[report2.reportID]: boolArr[boolArr.length - 1]}, }), ) diff --git a/tests/unit/SidebarOrderTest.js b/tests/unit/SidebarOrderTest.js index 4a693d679b86..0503d8f33dff 100644 --- a/tests/unit/SidebarOrderTest.js +++ b/tests/unit/SidebarOrderTest.js @@ -24,6 +24,7 @@ const ONYXKEYS = { REPORT_ACTIONS: 'reportActions_', }, NETWORK: 'network', + DRAFT_REPORT_IDS: 'draftReportIDs', }; describe('Sidebar', () => { @@ -148,12 +149,8 @@ describe('Sidebar', () => { it('changes the order when adding a draft to the active report', () => { // Given three reports in the recently updated order of 3, 2, 1 - // And the first report has a draft // And the currently viewed report is the first report - const report1 = { - ...LHNTestUtils.getFakeReport([1, 2], 3), - hasDraft: true, - }; + const report1 = LHNTestUtils.getFakeReport([1, 2], 3); const report2 = LHNTestUtils.getFakeReport([3, 4], 2); const report3 = LHNTestUtils.getFakeReport([5, 6], 1); @@ -176,6 +173,8 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + // Setting the draft status for first report + [ONYXKEYS.DRAFT_REPORT_IDS]: {[report1.reportID]: true}, }), ) @@ -188,7 +187,7 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(lodashGet(displayNames, [0, 'props', 'children'])).toBe('One, Two'); // this has `hasDraft` flag enabled so it will be on top + expect(lodashGet(displayNames, [0, 'props', 'children'])).toBe('One, Two'); // this has a `reportID` in `draftReportID` so it will be on top expect(lodashGet(displayNames, [1, 'props', 'children'])).toBe('Five, Six'); expect(lodashGet(displayNames, [2, 'props', 'children'])).toBe('Three, Four'); }) @@ -244,13 +243,9 @@ describe('Sidebar', () => { it('reorders the reports to keep draft reports on top', () => { // Given three reports in the recently updated order of 3, 2, 1 - // And the second report has a draft // And the currently viewed report is the second report const report1 = LHNTestUtils.getFakeReport([1, 2], 3); - const report2 = { - ...LHNTestUtils.getFakeReport([3, 4], 2), - hasDraft: true, - }; + const report2 = LHNTestUtils.getFakeReport([3, 4], 2); const report3 = LHNTestUtils.getFakeReport([5, 6], 1); // Each report has at least one ADDCOMMENT action so should be rendered in the LNH @@ -272,6 +267,8 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + // Setting the draft status for second report + [ONYXKEYS.DRAFT_REPORT_IDS]: {[report2.reportID]: true}, }), ) @@ -300,11 +297,7 @@ describe('Sidebar', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); // Given a single report - // And the report has a draft - const report = { - ...LHNTestUtils.getFakeReport([1, 2]), - hasDraft: true, - }; + const report = LHNTestUtils.getFakeReport([1, 2]); return ( waitForBatchedUpdates() @@ -315,6 +308,8 @@ describe('Sidebar', () => { [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + // Setting the draft status for the report + [ONYXKEYS.DRAFT_REPORT_IDS]: {[report.reportID]: true}, }), ) @@ -324,7 +319,7 @@ describe('Sidebar', () => { }) // When the draft is removed - .then(() => Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, {hasDraft: null})) + .then(() => Onyx.set(ONYXKEYS.DRAFT_REPORT_IDS, {})) // Then the pencil icon goes away .then(() => { @@ -373,16 +368,13 @@ describe('Sidebar', () => { it('sorts chats by pinned > IOU > draft', () => { // Given three reports in the recently updated order of 3, 2, 1 // with the current user set to email9@ (someone not participating in any of the chats) - // with a report that has a draft, a report that is pinned, and + // with a report that is pinned, and // an outstanding IOU report that doesn't belong to the current user const report1 = { ...LHNTestUtils.getFakeReport([1, 2], 3), isPinned: true, }; - const report2 = { - ...LHNTestUtils.getFakeReport([3, 4], 2), - hasDraft: true, - }; + const report2 = LHNTestUtils.getFakeReport([3, 4], 2); const report3 = { ...LHNTestUtils.getFakeReport([5, 6], 1), hasOutstandingIOU: false, @@ -418,6 +410,8 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, [`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport, + // Setting the draft status for second report + [ONYXKEYS.DRAFT_REPORT_IDS]: {[report2.reportID]: true}, }), ) @@ -499,23 +493,10 @@ describe('Sidebar', () => { it('alphabetizes all the chats that have drafts', () => { // Given three reports in the recently updated order of 3, 2, 1 - // and they all have drafts - const report1 = { - ...LHNTestUtils.getFakeReport([1, 2], 3), - hasDraft: true, - }; - const report2 = { - ...LHNTestUtils.getFakeReport([3, 4], 2), - hasDraft: true, - }; - const report3 = { - ...LHNTestUtils.getFakeReport([5, 6], 1), - hasDraft: true, - }; - const report4 = { - ...LHNTestUtils.getFakeReport([7, 8], 0), - hasDraft: true, - }; + const report1 = LHNTestUtils.getFakeReport([1, 2], 3); + const report2 = LHNTestUtils.getFakeReport([3, 4], 2); + const report3 = LHNTestUtils.getFakeReport([5, 6], 1); + const report4 = LHNTestUtils.getFakeReport([7, 8], 0); LHNTestUtils.getDefaultRenderedSidebarLinks('0'); return ( waitForBatchedUpdates() @@ -528,6 +509,13 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + // Setting the draft status for all reports + [ONYXKEYS.DRAFT_REPORT_IDS]: { + [report1.reportID]: true, + [report2.reportID]: true, + [report3.reportID]: true, + [report4.reportID]: true, + }, }), ) From fcad1ec9cb4fccd0807bba81f820dc165b27eb0f Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 3 Oct 2023 11:45:00 +0500 Subject: [PATCH 10/21] fix: use allReportsDict in getordeeredReportIDs --- src/libs/SidebarUtils.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.js index 75f1782670b2..78839060ee5e 100644 --- a/src/libs/SidebarUtils.js +++ b/src/libs/SidebarUtils.js @@ -112,11 +112,10 @@ let hasInitialReportActions = false; * @returns {String[]} An array of reportIDs sorted in the proper order */ function getOrderedReportIDs(currentReportId, draftReportIDs, allReportsDict, betas, policies, priorityMode, allReportActions) { - const allReportsDictKeys = Object.keys(allReportsDict); // Generate a unique cache key based on the function arguments const cachedReportsKey = JSON.stringify( // eslint-disable-next-line es/no-optional-chaining - [currentReportId, allReportsDictKeys, betas, draftReportIDs, policies, priorityMode, allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentReportId}`]?.length || 1], + [currentReportId, draftReportIDs, allReportsDict, betas, policies, priorityMode, allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentReportId}`]?.length || 1], (key, value) => { /** * Exclude 'participantAccountIDs', 'participants' and 'lastMessageText' not to overwhelm a cached key value with huge data, @@ -254,7 +253,6 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale, phoneNumber: null, isUnread: null, isUnreadWithMention: null, - hasDraftComment: false, keyForList: null, searchText: null, isPinned: false, From 8fbd7a91fddf5b8de0d93a7ab77865a8b256a254 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 3 Oct 2023 12:21:44 +0500 Subject: [PATCH 11/21] test: fix unit test --- src/libs/actions/Report.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 941d7dce7b49..1ae0b70fed24 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -984,7 +984,6 @@ function handleReportChanged(report) { Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, callback: handleReportChanged, }); From 713a35cde701f3c631100fce1e24272b0dfb47c6 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 3 Oct 2023 12:57:05 +0500 Subject: [PATCH 12/21] test: add test for draft report utils --- tests/unit/DraftReportUtilsTest.js | 59 ++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 tests/unit/DraftReportUtilsTest.js diff --git a/tests/unit/DraftReportUtilsTest.js b/tests/unit/DraftReportUtilsTest.js new file mode 100644 index 000000000000..0ac8a77dc907 --- /dev/null +++ b/tests/unit/DraftReportUtilsTest.js @@ -0,0 +1,59 @@ +import Onyx from 'react-native-onyx'; +import {cleanup} from '@testing-library/react-native'; +import DraftReportUtils from '../../src/libs/DraftReportUtils'; + +const ONYXKEYS = { + DRAFT_REPORT_IDS: 'draftReportIDs', +}; + +const reportID = 1; + +describe('DraftReportUtils', () => { + beforeAll(() => + Onyx.init({ + keys: ONYXKEYS, + }), + ); + + // Clear out Onyx after each test so that each test starts with a clean slate + afterEach(() => { + cleanup(); + Onyx.clear(); + }); + + describe('Singleton', () => { + it('should return the same instance when called multiple times', () => { + // Call getInstance multiple times + const instance1 = DraftReportUtils.getInstance(); + const instance2 = DraftReportUtils.getInstance(); + const instance3 = DraftReportUtils.getInstance(); + + // Ensure that all instances are the same + expect(instance1).toBe(instance2); + expect(instance2).toBe(instance3); + }); + }); + + it('should return an empty object when there are no draft reports', () => { + const draftReportIDs = DraftReportUtils.getInstance().getDraftReportIDs(); + expect(draftReportIDs).toEqual({}); + }); + + it('should return an object of draft report IDs when draft is set through onyx', async () => { + await Onyx.merge(ONYXKEYS.DRAFT_REPORT_IDS, {[reportID]: true}); + const draftReportIDs = DraftReportUtils.getInstance().getDraftReportIDs(); + expect(draftReportIDs).toEqual({[`${reportID}`]: true}); + }); + + it('should return an empty object of draft report IDs when draft is unset through onyx', async () => { + const draftReportUtils = DraftReportUtils.getInstance(); + + await Onyx.merge(ONYXKEYS.DRAFT_REPORT_IDS, {[reportID]: true}); + let draftReportIDs = draftReportUtils.getDraftReportIDs(); + expect(draftReportIDs).toEqual({[`${reportID}`]: true}); + + await Onyx.set(ONYXKEYS.DRAFT_REPORT_IDS, {}); + draftReportIDs = draftReportUtils.getDraftReportIDs(); + expect(draftReportIDs).toEqual({}); + }); +}); From f7e50d653862e67ddd1bcfc1c222869d500b3458 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 3 Oct 2023 15:33:42 +0500 Subject: [PATCH 13/21] fix: linting --- tests/unit/DraftReportUtilsTest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/DraftReportUtilsTest.js b/tests/unit/DraftReportUtilsTest.js index 0ac8a77dc907..ec13b9c69de7 100644 --- a/tests/unit/DraftReportUtilsTest.js +++ b/tests/unit/DraftReportUtilsTest.js @@ -27,7 +27,7 @@ describe('DraftReportUtils', () => { const instance1 = DraftReportUtils.getInstance(); const instance2 = DraftReportUtils.getInstance(); const instance3 = DraftReportUtils.getInstance(); - + // Ensure that all instances are the same expect(instance1).toBe(instance2); expect(instance2).toBe(instance3); @@ -47,7 +47,7 @@ describe('DraftReportUtils', () => { it('should return an empty object of draft report IDs when draft is unset through onyx', async () => { const draftReportUtils = DraftReportUtils.getInstance(); - + await Onyx.merge(ONYXKEYS.DRAFT_REPORT_IDS, {[reportID]: true}); let draftReportIDs = draftReportUtils.getDraftReportIDs(); expect(draftReportIDs).toEqual({[`${reportID}`]: true}); From 359a003b771c55a5f50e09bb85dfd713c8e17f7b Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 3 Oct 2023 15:34:06 +0500 Subject: [PATCH 14/21] fix: replace lodashGet --- src/libs/ReportActionsUtils.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index 18bc28e19d9a..51cfa35ea120 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -57,8 +57,16 @@ function isCreatedAction(reportAction) { */ function isDeletedAction(reportAction) { // A deleted comment has either an empty array or an object with html field with empty string as value - const message = lodashGet(reportAction, 'message', []); - return message.length === 0 || lodashGet(message, [0, 'html']) === ''; + let message = []; + if (reportAction.message) { + message = reportAction.message; + } + + if (message.length === 0) { + return true; + } + + return message[0].html === ''; } /** @@ -66,7 +74,10 @@ function isDeletedAction(reportAction) { * @returns {Boolean} */ function isDeletedParentAction(reportAction) { - return lodashGet(reportAction, ['message', 0, 'isDeletedParentAction'], false) && lodashGet(reportAction, 'childVisibleActionCount', 0) > 0; + const isDeleted = (reportAction && reportAction.message && reportAction.message[0] && reportAction.message[0].isDeletedParentAction) || false; + const childVisibleActionCount = (reportAction && reportAction.childVisibleActionCount) || 0; + + return isDeleted && childVisibleActionCount > 0; } /** @@ -74,7 +85,7 @@ function isDeletedParentAction(reportAction) { * @returns {Boolean} */ function isPendingRemove(reportAction) { - return lodashGet(reportAction, 'message[0].moderationDecision.decision') === CONST.MODERATION.MODERATOR_DECISION_PENDING_REMOVE; + return reportAction['message[0].moderationDecision.decision'] === CONST.MODERATION.MODERATOR_DECISION_PENDING_REMOVE; } /** From fb569663c9ff5f3772c849410f308ddae984bd54 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 3 Oct 2023 16:46:24 +0500 Subject: [PATCH 15/21] docs: add js doc to DraftReportUtil --- src/libs/DraftReportUtils.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libs/DraftReportUtils.ts b/src/libs/DraftReportUtils.ts index 71d16a1fce66..214b92404e1b 100644 --- a/src/libs/DraftReportUtils.ts +++ b/src/libs/DraftReportUtils.ts @@ -1,6 +1,11 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../ONYXKEYS'; +/** + * A singleton class to manage the draft report IDs + * @class DraftReportUtils + * @singleton + */ class DraftReportUtils { private static instance: DraftReportUtils; @@ -14,11 +19,17 @@ class DraftReportUtils { this.subscribeToDraftReportIDs(); } + /** + * @returns The singleton instance + */ public static getInstance(): DraftReportUtils { // Ensure singleton instance return DraftReportUtils.instance ?? new DraftReportUtils(); } + /** + * Subscribe to the draft report IDs + */ private subscribeToDraftReportIDs() { Onyx.connect({ key: ONYXKEYS.DRAFT_REPORT_IDS, @@ -32,6 +43,9 @@ class DraftReportUtils { }); } + /** + * @returns The draft report IDs + */ getDraftReportIDs() { return this.draftReportIDs; } From fbfe6d2e8405fa6b19492b60a6ae599c61cddd0d Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 4 Oct 2023 19:31:29 +0500 Subject: [PATCH 16/21] fix: revert changes due to unit test fixes --- src/components/LHNOptionsList/OptionRowLHN.js | 17 +++++++++++++---- .../LHNOptionsList/OptionRowLHNData.js | 10 ++++------ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js index 15568b99a6f9..c3a847144a55 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.js +++ b/src/components/LHNOptionsList/OptionRowLHN.js @@ -3,6 +3,7 @@ import React, {useState, useRef} from 'react'; import PropTypes from 'prop-types'; import {View, StyleSheet} from 'react-native'; import lodashGet from 'lodash/get'; +import {withOnyx} from 'react-native-onyx'; import * as optionRowStyles from '../../styles/optionRowStyles'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; @@ -25,6 +26,7 @@ import * as ReportUtils from '../../libs/ReportUtils'; import useLocalize from '../../hooks/useLocalize'; import Permissions from '../../libs/Permissions'; import Tooltip from '../Tooltip'; +import ONYXKEYS from '../../ONYXKEYS'; const propTypes = { /** Style for hovered state */ @@ -52,7 +54,8 @@ const propTypes = { // eslint-disable-next-line react/forbid-prop-types optionItem: PropTypes.object, - hasDraft: PropTypes.bool, + // eslint-disable-next-line react/forbid-prop-types + draftReportIDs: PropTypes.object, }; const defaultProps = { @@ -63,7 +66,7 @@ const defaultProps = { optionItem: null, isFocused: false, betas: [], - hasDraft: false, + draftReportIDs: {}, }; function OptionRowLHN(props) { @@ -143,7 +146,7 @@ function OptionRowLHN(props) { const formattedDate = DateUtils.getStatusUntilDate(statusClearAfterDate); const statusContent = formattedDate ? `${statusText} (${formattedDate})` : statusText; const isStatusVisible = Permissions.canUseCustomStatus(props.betas) && !!emojiCode && ReportUtils.isOneOnOneChat(optionItem); - const isDraft = props.hasDraft; + const isDraft = props.draftReportIDs[props.reportID]; return ( { + const draftReportIDs = DraftReportUtils.getInstance().getDraftReportIDs(); + const hasDraft = draftReportIDs[reportID]; + if (!optionItem || hasDraft || !comment || comment.length <= 0 || isFocused) { return; } @@ -133,7 +135,6 @@ function OptionRowLHNData({ {...propsToForward} isFocused={isFocused} optionItem={optionItem} - hasDraft={hasDraft} /> ); } @@ -184,9 +185,6 @@ export default React.memo( }, }), withOnyx({ - draftReportIDs: { - key: ONYXKEYS.DRAFT_REPORT_IDS, - }, fullReport: { key: (props) => ONYXKEYS.COLLECTION.REPORT + props.reportID, }, From 5c1b12b64f05662f499a41d3a193358eb2a70462 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 16 Oct 2023 16:28:24 +0500 Subject: [PATCH 17/21] fix: add initialValue to reduce re-renders --- src/components/LHNOptionsList/OptionRowLHN.js | 1 + src/components/LHNOptionsList/OptionRowLHNData.js | 7 ++++++- src/pages/home/sidebar/SidebarLinksData.js | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js index e6cac266c10d..fcd13758d442 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.js +++ b/src/components/LHNOptionsList/OptionRowLHN.js @@ -318,6 +318,7 @@ export default React.memo( withOnyx({ draftReportIDs: { key: ONYXKEYS.DRAFT_REPORT_IDS, + initialValue: {}, }, })(OptionRowLHN), ); diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js index 883a29fb3117..a510edf9fc59 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.js +++ b/src/components/LHNOptionsList/OptionRowLHNData.js @@ -178,14 +178,17 @@ export default React.memo( withOnyx({ fullReport: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + initialValue: {}, }, reportActions: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, canEvict: false, + initialValue: {}, }, personalDetails: { key: ONYXKEYS.PERSONAL_DETAILS_LIST, selector: personalDetailsSelector, + initialValue: {}, }, preferredLocale: { key: ONYXKEYS.NVP_PREFERRED_LOCALE, @@ -196,15 +199,17 @@ export default React.memo( parentReportActions: { key: ({fullReport}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${fullReport.parentReportID}`, canEvict: false, + initialValue: {}, }, policy: { key: ({fullReport}) => `${ONYXKEYS.COLLECTION.POLICY}${fullReport.policyID}`, + initialValue: {}, }, // Ideally, we aim to access only the last transaction for the current report by listening to changes in reportActions. // In some scenarios, a transaction might be created after reportActions have been modified. // This can lead to situations where `lastTransaction` doesn't update and retains the previous value. // However, performance overhead of this is minimized by using memos inside the component. - receiptTransactions: {key: ONYXKEYS.COLLECTION.TRANSACTION}, + receiptTransactions: {key: ONYXKEYS.COLLECTION.TRANSACTION, initialValue: {}}, }), // eslint-disable-next-line rulesdir/no-multiple-onyx-in-file withOnyx({ diff --git a/src/pages/home/sidebar/SidebarLinksData.js b/src/pages/home/sidebar/SidebarLinksData.js index e67c3d4de89f..d96245919b84 100644 --- a/src/pages/home/sidebar/SidebarLinksData.js +++ b/src/pages/home/sidebar/SidebarLinksData.js @@ -200,26 +200,32 @@ export default compose( chatReports: { key: ONYXKEYS.COLLECTION.REPORT, selector: chatReportSelector, + initialValue: {}, }, isLoadingReportData: { key: ONYXKEYS.IS_LOADING_REPORT_DATA, }, draftReportIDs: { key: ONYXKEYS.DRAFT_REPORT_IDS, + initialValue: {}, }, priorityMode: { key: ONYXKEYS.NVP_PRIORITY_MODE, + initialValue: CONST.PRIORITY_MODE.DEFAULT, }, betas: { key: ONYXKEYS.BETAS, + initialValue: [], }, allReportActions: { key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, selector: reportActionsSelector, + initialValue: {}, }, policies: { key: ONYXKEYS.COLLECTION.POLICY, selector: policySelector, + initialValue: {}, }, }), )(SidebarLinksData); From 30a906496814f4023fbb11bdaff3a69c789b7772 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 20 Oct 2023 12:14:24 +0500 Subject: [PATCH 18/21] fix: PR feedbacks --- src/components/LHNOptionsList/OptionRowLHN.js | 20 ++++++- .../LHNOptionsList/OptionRowLHNData.js | 18 +----- src/libs/DraftReportUtils.ts | 54 ----------------- src/libs/ReportUtils.js | 21 +++++-- src/libs/UnreadIndicatorUpdater/index.js | 10 ++++ src/libs/actions/DraftReports.ts | 17 +----- src/libs/actions/Policy.js | 14 ++++- .../ComposerWithSuggestions.js | 3 +- tests/unit/DraftReportUtilsTest.js | 59 ------------------- tests/unit/SidebarFilterTest.js | 4 ++ tests/unit/SidebarOrderTest.js | 4 ++ 11 files changed, 66 insertions(+), 158 deletions(-) delete mode 100644 src/libs/DraftReportUtils.ts delete mode 100644 tests/unit/DraftReportUtilsTest.js diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js index 6e886ba6e360..0a2a5d26f10d 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.js +++ b/src/components/LHNOptionsList/OptionRowLHN.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React, {useState, useRef, useCallback} from 'react'; +import React, {useState, useRef, useCallback, useEffect} from 'react'; import PropTypes from 'prop-types'; import {View, StyleSheet} from 'react-native'; import lodashGet from 'lodash/get'; @@ -31,6 +31,7 @@ import ONYXKEYS from '../../ONYXKEYS'; import DomUtils from '../../libs/DomUtils'; import useWindowDimensions from '../../hooks/useWindowDimensions'; import ReportActionComposeFocusManager from '../../libs/ReportActionComposeFocusManager'; +import setDraftStatusForReportID from '../../libs/actions/DraftReports'; const propTypes = { /** Style for hovered state */ @@ -60,6 +61,8 @@ const propTypes = { // eslint-disable-next-line react/forbid-prop-types draftReportIDs: PropTypes.object, + + shouldUpdateDraftStatus: PropTypes.bool, }; const defaultProps = { @@ -71,6 +74,7 @@ const defaultProps = { isFocused: false, betas: [], draftReportIDs: {}, + shouldUpdateDraftStatus: false, }; function OptionRowLHN(props) { @@ -83,6 +87,17 @@ function OptionRowLHN(props) { const optionItem = props.optionItem; const [isContextMenuActive, setIsContextMenuActive] = useState(false); + const hasDraft = props.draftReportIDs[props.reportID]; + + useEffect(() => { + if (props.shouldUpdateDraftStatus || hasDraft) { + return; + } + + setDraftStatusForReportID(props.reportID, true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useFocusEffect( useCallback(() => { isFocusedRef.current = true; @@ -159,7 +174,6 @@ function OptionRowLHN(props) { const formattedDate = DateUtils.getStatusUntilDate(statusClearAfterDate); const statusContent = formattedDate ? `${statusText} (${formattedDate})` : statusText; const isStatusVisible = Permissions.canUseCustomStatus(props.betas) && !!emojiCode && ReportUtils.isOneOnOneChat(optionItem); - const isDraft = props.draftReportIDs[props.reportID]; return ( )} - {isDraft && optionItem.isAllowedToComment && ( + {hasDraft && optionItem.isAllowedToComment && ( { - const draftReportIDs = DraftReportUtils.getInstance().getDraftReportIDs(); - const hasDraft = draftReportIDs[reportID]; - - if (!optionItem || hasDraft || !comment || comment.length <= 0 || isFocused) { - return; - } - setDraftStatusForReportID(reportID, true); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return ( ); } diff --git a/src/libs/DraftReportUtils.ts b/src/libs/DraftReportUtils.ts deleted file mode 100644 index 214b92404e1b..000000000000 --- a/src/libs/DraftReportUtils.ts +++ /dev/null @@ -1,54 +0,0 @@ -import Onyx from 'react-native-onyx'; -import ONYXKEYS from '../ONYXKEYS'; - -/** - * A singleton class to manage the draft report IDs - * @class DraftReportUtils - * @singleton - */ -class DraftReportUtils { - private static instance: DraftReportUtils; - - private draftReportIDs: Record; - - private constructor() { - DraftReportUtils.instance = this; - - this.draftReportIDs = {}; - - this.subscribeToDraftReportIDs(); - } - - /** - * @returns The singleton instance - */ - public static getInstance(): DraftReportUtils { - // Ensure singleton instance - return DraftReportUtils.instance ?? new DraftReportUtils(); - } - - /** - * Subscribe to the draft report IDs - */ - private subscribeToDraftReportIDs() { - Onyx.connect({ - key: ONYXKEYS.DRAFT_REPORT_IDS, - callback: (val) => { - if (!val) { - return; - } - - this.draftReportIDs = val; - }, - }); - } - - /** - * @returns The draft report IDs - */ - getDraftReportIDs() { - return this.draftReportIDs; - } -} - -export default DraftReportUtils; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index a12b4a48f393..30c375c2e574 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -23,7 +23,6 @@ import isReportMessageAttachment from './isReportMessageAttachment'; import * as defaultWorkspaceAvatars from '../components/Icon/WorkspaceDefaultAvatars'; import * as CurrencyUtils from './CurrencyUtils'; import * as UserUtils from './UserUtils'; -import DraftReportUtils from './DraftReportUtils'; let currentUserEmail; let currentUserAccountID; @@ -53,7 +52,7 @@ Onyx.connect({ }, }); -let allReports = {}; +let allReports; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, @@ -61,7 +60,17 @@ Onyx.connect({ callback: (val) => (allReports = val), }); -const draftReportUtils = DraftReportUtils.getInstance(); +let draftReportIDs = {}; +Onyx.connect({ + key: ONYXKEYS.DRAFT_REPORT_IDS, + callback: (val) => { + if (!val) { + return; + } + + draftReportIDs = val; + }, +}); let doesDomainHaveApprovedAccountant; Onyx.connect({ @@ -767,7 +776,7 @@ function isMoneyRequestReport(reportOrID) { */ function getReport(reportID) { /** - * using typical string concatenation here due to performance issues + * Using typical string concatenation here due to performance issues * with template literals. */ if (!allReports) { @@ -1414,7 +1423,7 @@ function getPolicyExpenseChatName(report, policy = undefined) { let policyExpenseChatRole = 'user'; /** - * using typical string concatenation here due to performance issues + * Using typical string concatenation here due to performance issues * with template literals. */ const policyItem = allPolicies[ONYXKEYS.COLLECTION.POLICY + report.policyID]; @@ -3198,7 +3207,7 @@ function shouldReportBeInOptionList(report, currentReportId, isInGSDMode, betas, } // Include reports that are relevant to the user in any view mode. Criteria include having a draft, having an outstanding IOU, or being assigned to an open task. - if (draftReportUtils.getDraftReportIDs()[report.reportID] || isWaitingForIOUActionFromCurrentUser(report) || isWaitingForTaskCompleteFromAssignee(report)) { + if (draftReportIDs[report.reportID] || isWaitingForIOUActionFromCurrentUser(report) || isWaitingForTaskCompleteFromAssignee(report)) { return true; } const lastVisibleMessage = ReportActionsUtils.getLastVisibleMessage(report.reportID); diff --git a/src/libs/UnreadIndicatorUpdater/index.js b/src/libs/UnreadIndicatorUpdater/index.js index dcfedce83d75..e67b5b1feb06 100644 --- a/src/libs/UnreadIndicatorUpdater/index.js +++ b/src/libs/UnreadIndicatorUpdater/index.js @@ -15,6 +15,16 @@ Onyx.connect({ return; } + /** + * We need to wait until after interactions have finished to update the unread count because otherwise + * the unread count will be updated while the interactions/animations are in progress and we don't want + * to put more work on the main thread. + * + * For eg. On web we are manipulating DOM and it makes it a better candidate to wait until after interactions + * have finished. + * + * More info: https://reactnative.dev/docs/interactionmanager + */ InteractionManager.runAfterInteractions(() => { const unreadReportsCount = _.filter(reportsFromOnyx, ReportUtils.isUnread).length || 0; if (previousUnreadCount !== unreadReportsCount) { diff --git a/src/libs/actions/DraftReports.ts b/src/libs/actions/DraftReports.ts index dc1e7a8066f8..97e5073030ca 100644 --- a/src/libs/actions/DraftReports.ts +++ b/src/libs/actions/DraftReports.ts @@ -1,8 +1,5 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; -import DraftReportUtils from '../DraftReportUtils'; - -const draftReportUtils = DraftReportUtils.getInstance(); /** * Immediate indication whether the report has a draft. @@ -11,19 +8,7 @@ const draftReportUtils = DraftReportUtils.getInstance(); * @param draft */ function setDraftStatusForReportID(reportID: string, draft: boolean) { - const draftReportIDs = {...draftReportUtils.getDraftReportIDs()}; - - if (draftReportIDs[reportID] && draft) { - return; - } - - if (draftReportIDs[reportID] && !draft) { - delete draftReportIDs[reportID]; - Onyx.set(ONYXKEYS.DRAFT_REPORT_IDS, draftReportIDs); - } else { - draftReportIDs[reportID] = draft; - Onyx.merge(ONYXKEYS.DRAFT_REPORT_IDS, {[reportID]: draft}); - } + Onyx.merge(ONYXKEYS.DRAFT_REPORT_IDS, {[reportID]: draft}); } export default setDraftStatusForReportID; diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index fffe71a5c931..1fef24a92b11 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -14,9 +14,18 @@ import * as ErrorUtils from '../ErrorUtils'; import * as ReportUtils from '../ReportUtils'; import * as PersonalDetailsUtils from '../PersonalDetailsUtils'; import Log from '../Log'; -import DraftReportUtils from '../DraftReportUtils'; -const draftReportUtils = DraftReportUtils.getInstance(); +let draftReportIDs = {}; +Onyx.connect({ + key: ONYXKEYS.DRAFT_REPORT_IDS, + callback: (val) => { + if (!val) { + return; + } + + draftReportIDs = val; + }, +}); const allPolicies = {}; Onyx.connect({ @@ -33,7 +42,6 @@ Onyx.connect({ const policyReports = ReportUtils.getAllPolicyReports(policyID); const cleanUpMergeQueries = {}; const cleanUpSetQueries = {}; - const draftReportIDs = {...draftReportUtils.getDraftReportIDs()}; _.each(policyReports, ({reportID}) => { cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; delete draftReportIDs[reportID]; diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index 91471d8fe145..b470528eede5 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -247,8 +247,9 @@ function ComposerWithSuggestions({ if (commentRef.current.length === 0 && newComment.length !== 0) { setDraftStatusForReportID(reportID, true); } + // The draft has been deleted. - else if (newComment.length === 0) { + if (newComment.length === 0) { setDraftStatusForReportID(reportID, false); } diff --git a/tests/unit/DraftReportUtilsTest.js b/tests/unit/DraftReportUtilsTest.js deleted file mode 100644 index ec13b9c69de7..000000000000 --- a/tests/unit/DraftReportUtilsTest.js +++ /dev/null @@ -1,59 +0,0 @@ -import Onyx from 'react-native-onyx'; -import {cleanup} from '@testing-library/react-native'; -import DraftReportUtils from '../../src/libs/DraftReportUtils'; - -const ONYXKEYS = { - DRAFT_REPORT_IDS: 'draftReportIDs', -}; - -const reportID = 1; - -describe('DraftReportUtils', () => { - beforeAll(() => - Onyx.init({ - keys: ONYXKEYS, - }), - ); - - // Clear out Onyx after each test so that each test starts with a clean slate - afterEach(() => { - cleanup(); - Onyx.clear(); - }); - - describe('Singleton', () => { - it('should return the same instance when called multiple times', () => { - // Call getInstance multiple times - const instance1 = DraftReportUtils.getInstance(); - const instance2 = DraftReportUtils.getInstance(); - const instance3 = DraftReportUtils.getInstance(); - - // Ensure that all instances are the same - expect(instance1).toBe(instance2); - expect(instance2).toBe(instance3); - }); - }); - - it('should return an empty object when there are no draft reports', () => { - const draftReportIDs = DraftReportUtils.getInstance().getDraftReportIDs(); - expect(draftReportIDs).toEqual({}); - }); - - it('should return an object of draft report IDs when draft is set through onyx', async () => { - await Onyx.merge(ONYXKEYS.DRAFT_REPORT_IDS, {[reportID]: true}); - const draftReportIDs = DraftReportUtils.getInstance().getDraftReportIDs(); - expect(draftReportIDs).toEqual({[`${reportID}`]: true}); - }); - - it('should return an empty object of draft report IDs when draft is unset through onyx', async () => { - const draftReportUtils = DraftReportUtils.getInstance(); - - await Onyx.merge(ONYXKEYS.DRAFT_REPORT_IDS, {[reportID]: true}); - let draftReportIDs = draftReportUtils.getDraftReportIDs(); - expect(draftReportIDs).toEqual({[`${reportID}`]: true}); - - await Onyx.set(ONYXKEYS.DRAFT_REPORT_IDS, {}); - draftReportIDs = draftReportUtils.getDraftReportIDs(); - expect(draftReportIDs).toEqual({}); - }); -}); diff --git a/tests/unit/SidebarFilterTest.js b/tests/unit/SidebarFilterTest.js index 982393b3e191..bf839b0b36d8 100644 --- a/tests/unit/SidebarFilterTest.js +++ b/tests/unit/SidebarFilterTest.js @@ -110,6 +110,7 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, + // Set the draft status for the given reportID [ONYXKEYS.DRAFT_REPORT_IDS]: {[report.reportID]: true}, }), @@ -339,6 +340,7 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, + // Set the draft status for the given reportID [ONYXKEYS.DRAFT_REPORT_IDS]: {[report2.reportID]: boolArr[boolArr.length - 1]}, }), @@ -452,6 +454,7 @@ describe('Sidebar', () => { [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, [`${ONYXKEYS.COLLECTION.REPORT}${draftReport.reportID}`]: draftReport, [`${ONYXKEYS.COLLECTION.REPORT}${pinnedReport.reportID}`]: pinnedReport, + // Set the draft status for the given reportID [ONYXKEYS.DRAFT_REPORT_IDS]: {[draftReport.reportID]: true}, }), @@ -665,6 +668,7 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, + // Set the draft status for the given reportID [ONYXKEYS.DRAFT_REPORT_IDS]: {[report2.reportID]: boolArr[boolArr.length - 1]}, }), diff --git a/tests/unit/SidebarOrderTest.js b/tests/unit/SidebarOrderTest.js index 0503d8f33dff..1b8f231d7b0e 100644 --- a/tests/unit/SidebarOrderTest.js +++ b/tests/unit/SidebarOrderTest.js @@ -267,6 +267,7 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + // Setting the draft status for second report [ONYXKEYS.DRAFT_REPORT_IDS]: {[report2.reportID]: true}, }), @@ -308,6 +309,7 @@ describe('Sidebar', () => { [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, + // Setting the draft status for the report [ONYXKEYS.DRAFT_REPORT_IDS]: {[report.reportID]: true}, }), @@ -410,6 +412,7 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, [`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport, + // Setting the draft status for second report [ONYXKEYS.DRAFT_REPORT_IDS]: {[report2.reportID]: true}, }), @@ -509,6 +512,7 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + // Setting the draft status for all reports [ONYXKEYS.DRAFT_REPORT_IDS]: { [report1.reportID]: true, From c846ff11670c8b9d10c890f4e0319a73e4f062c8 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 20 Oct 2023 12:18:00 +0500 Subject: [PATCH 19/21] fix: PR feedbacks --- tests/unit/SidebarOrderTest.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/SidebarOrderTest.js b/tests/unit/SidebarOrderTest.js index 1b8f231d7b0e..6b89b8a94179 100644 --- a/tests/unit/SidebarOrderTest.js +++ b/tests/unit/SidebarOrderTest.js @@ -173,6 +173,7 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, + // Setting the draft status for first report [ONYXKEYS.DRAFT_REPORT_IDS]: {[report1.reportID]: true}, }), From a3304673f1afa82fc163eceecb20a15e5c01ee96 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 25 Oct 2023 12:19:10 +0500 Subject: [PATCH 20/21] revert: new onyx key for draft status --- src/ONYXKEYS.ts | 4 -- src/components/LHNOptionsList/OptionRowLHN.js | 34 +-------- .../LHNOptionsList/OptionRowLHNData.js | 14 +++- src/components/optionPropTypes.js | 3 + src/libs/OptionsListUtils.js | 2 + src/libs/ReportUtils.js | 15 +--- src/libs/SidebarUtils.js | 9 +-- src/libs/actions/DraftReports.ts | 14 ---- src/libs/actions/Policy.js | 19 ++--- src/libs/actions/Report.js | 12 ++++ .../ComposerWithSuggestions.js | 7 +- src/pages/home/sidebar/SidebarLinksData.js | 20 ++---- src/types/onyx/Report.ts | 1 + tests/unit/OptionsListUtilsTest.js | 1 + tests/unit/SidebarFilterTest.js | 25 +++---- tests/unit/SidebarOrderTest.js | 71 ++++++++++--------- tests/utils/LHNTestUtils.js | 4 +- 17 files changed, 105 insertions(+), 150 deletions(-) delete mode 100644 src/libs/actions/DraftReports.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index d395eb7a89a6..8a3df3153326 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -11,9 +11,6 @@ const ONYXKEYS = { /** Holds information about the users account that is logging in */ ACCOUNT: 'account', - /** Holds the reportIDs which are currently in draft */ - DRAFT_REPORT_IDS: 'draftReportIDs', - /** Holds the reportID for the report between the user and their account manager */ ACCOUNT_MANAGER_REPORT_ID: 'accountManagerReportID', @@ -314,7 +311,6 @@ type OnyxKey = DeepValueOf>; type OnyxValues = { [ONYXKEYS.ACCOUNT]: OnyxTypes.Account; - [ONYXKEYS.DRAFT_REPORT_IDS]: Record; [ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID]: string; [ONYXKEYS.NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER]: boolean; [ONYXKEYS.ACTIVE_CLIENTS]: string[]; diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js index eac1d27a6789..2b992e462e34 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.js +++ b/src/components/LHNOptionsList/OptionRowLHN.js @@ -1,9 +1,8 @@ import _ from 'underscore'; -import React, {useState, useRef, useCallback, useEffect} from 'react'; +import React, {useState, useRef, useCallback} from 'react'; import PropTypes from 'prop-types'; import {View, StyleSheet} from 'react-native'; import lodashGet from 'lodash/get'; -import {withOnyx} from 'react-native-onyx'; import {useFocusEffect} from '@react-navigation/native'; import * as optionRowStyles from '../../styles/optionRowStyles'; import styles from '../../styles/styles'; @@ -27,11 +26,9 @@ import * as ReportUtils from '../../libs/ReportUtils'; import useLocalize from '../../hooks/useLocalize'; import Permissions from '../../libs/Permissions'; import Tooltip from '../Tooltip'; -import ONYXKEYS from '../../ONYXKEYS'; import DomUtils from '../../libs/DomUtils'; import useWindowDimensions from '../../hooks/useWindowDimensions'; import ReportActionComposeFocusManager from '../../libs/ReportActionComposeFocusManager'; -import setDraftStatusForReportID from '../../libs/actions/DraftReports'; const propTypes = { /** Style for hovered state */ @@ -58,11 +55,6 @@ const propTypes = { /** The item that should be rendered */ // eslint-disable-next-line react/forbid-prop-types optionItem: PropTypes.object, - - // eslint-disable-next-line react/forbid-prop-types - draftReportIDs: PropTypes.object, - - shouldUpdateDraftStatus: PropTypes.bool, }; const defaultProps = { @@ -73,8 +65,6 @@ const defaultProps = { optionItem: null, isFocused: false, betas: [], - draftReportIDs: {}, - shouldUpdateDraftStatus: false, }; function OptionRowLHN(props) { @@ -87,17 +77,6 @@ function OptionRowLHN(props) { const optionItem = props.optionItem; const [isContextMenuActive, setIsContextMenuActive] = useState(false); - const hasDraft = props.draftReportIDs[props.reportID]; - - useEffect(() => { - if (props.shouldUpdateDraftStatus || hasDraft) { - return; - } - - setDraftStatusForReportID(props.reportID, true); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - useFocusEffect( useCallback(() => { isFocusedRef.current = true; @@ -313,7 +292,7 @@ function OptionRowLHN(props) { /> )} - {hasDraft && optionItem.isAllowedToComment && ( + {optionItem.hasDraftComment && optionItem.isAllowedToComment && ( { + if (!optionItem || optionItem.hasDraftComment || !comment || comment.length <= 0 || isFocused) { + return; + } + Report.setReportWithDraft(reportID, true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return ( ); } diff --git a/src/components/optionPropTypes.js b/src/components/optionPropTypes.js index 6f84fff24a52..709298036f07 100644 --- a/src/components/optionPropTypes.js +++ b/src/components/optionPropTypes.js @@ -25,6 +25,9 @@ export default PropTypes.shape({ // reportID (only present when there is a matching report) reportID: PropTypes.string, + // Whether the report has a draft comment or not + hasDraftComment: PropTypes.bool, + // Key used internally by React keyForList: PropTypes.string, diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index e5ba54fc3370..022074a220c1 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -460,6 +460,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { login: null, reportID: null, phoneNumber: null, + hasDraftComment: false, keyForList: null, searchText: null, isDefaultRoom: false, @@ -506,6 +507,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { result.ownerAccountID = report.ownerAccountID; result.reportID = report.reportID; result.isUnread = ReportUtils.isUnread(report); + result.hasDraftComment = report.hasDraft; result.isPinned = report.isPinned; result.iouReportID = report.iouReportID; result.keyForList = String(report.reportID); diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 33f191c3deda..0d6dd34ef47c 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -53,25 +53,12 @@ Onyx.connect({ }); let allReports; - Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, waitForCollectionCallback: true, callback: (val) => (allReports = val), }); -let draftReportIDs = {}; -Onyx.connect({ - key: ONYXKEYS.DRAFT_REPORT_IDS, - callback: (val) => { - if (!val) { - return; - } - - draftReportIDs = val; - }, -}); - let doesDomainHaveApprovedAccountant; Onyx.connect({ key: ONYXKEYS.ACCOUNT, @@ -3314,7 +3301,7 @@ function shouldReportBeInOptionList(report, currentReportId, isInGSDMode, betas, } // Include reports that are relevant to the user in any view mode. Criteria include having a draft, having an outstanding IOU, or being assigned to an open task. - if (draftReportIDs[report.reportID] || isWaitingForIOUActionFromCurrentUser(report) || isWaitingForTaskCompleteFromAssignee(report)) { + if (report.hasDraft || isWaitingForIOUActionFromCurrentUser(report) || isWaitingForTaskCompleteFromAssignee(report)) { return true; } const lastVisibleMessage = ReportActionsUtils.getLastVisibleMessage(report.reportID); diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.js index 0f1bc93ed3b6..1760348ce1ee 100644 --- a/src/libs/SidebarUtils.js +++ b/src/libs/SidebarUtils.js @@ -103,7 +103,6 @@ let hasInitialReportActions = false; /** * @param {String} currentReportId - * @param {Object} draftReportIDs * @param {Object} allReportsDict * @param {Object} betas * @param {String[]} policies @@ -111,11 +110,11 @@ let hasInitialReportActions = false; * @param {Object} allReportActions * @returns {String[]} An array of reportIDs sorted in the proper order */ -function getOrderedReportIDs(currentReportId, draftReportIDs, allReportsDict, betas, policies, priorityMode, allReportActions) { +function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, priorityMode, allReportActions) { // Generate a unique cache key based on the function arguments const cachedReportsKey = JSON.stringify( // eslint-disable-next-line es/no-optional-chaining - [currentReportId, draftReportIDs, allReportsDict, betas, policies, priorityMode, allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentReportId}`]?.length || 1], + [currentReportId, allReportsDict, betas, policies, priorityMode, allReportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${currentReportId}`]?.length || 1], (key, value) => { /** * Exclude 'participantAccountIDs', 'participants' and 'lastMessageText' not to overwhelm a cached key value with huge data, @@ -182,7 +181,7 @@ function getOrderedReportIDs(currentReportId, draftReportIDs, allReportsDict, be pinnedReports.push(report); } else if (ReportUtils.isWaitingForIOUActionFromCurrentUser(report)) { outstandingIOUReports.push(report); - } else if (draftReportIDs[report.reportID]) { + } else if (report.hasDraft) { draftReports.push(report); } else if (ReportUtils.isArchivedRoom(report)) { archivedReports.push(report); @@ -253,6 +252,7 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale, phoneNumber: null, isUnread: null, isUnreadWithMention: null, + hasDraftComment: false, keyForList: null, searchText: null, isPinned: false, @@ -296,6 +296,7 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale, result.statusNum = report.statusNum; result.isUnread = ReportUtils.isUnread(report); result.isUnreadWithMention = ReportUtils.isUnreadWithMention(report); + result.hasDraftComment = report.hasDraft; result.isPinned = report.isPinned; result.iouReportID = report.iouReportID; result.keyForList = String(report.reportID); diff --git a/src/libs/actions/DraftReports.ts b/src/libs/actions/DraftReports.ts deleted file mode 100644 index 97e5073030ca..000000000000 --- a/src/libs/actions/DraftReports.ts +++ /dev/null @@ -1,14 +0,0 @@ -import Onyx from 'react-native-onyx'; -import ONYXKEYS from '../../ONYXKEYS'; - -/** - * Immediate indication whether the report has a draft. - * - * @param reportID - * @param draft - */ -function setDraftStatusForReportID(reportID: string, draft: boolean) { - Onyx.merge(ONYXKEYS.DRAFT_REPORT_IDS, {[reportID]: draft}); -} - -export default setDraftStatusForReportID; diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.js index 247d7a7b2f8f..9044f43eabb9 100644 --- a/src/libs/actions/Policy.js +++ b/src/libs/actions/Policy.js @@ -15,18 +15,6 @@ import * as ReportUtils from '../ReportUtils'; import * as PersonalDetailsUtils from '../PersonalDetailsUtils'; import Log from '../Log'; -let draftReportIDs = {}; -Onyx.connect({ - key: ONYXKEYS.DRAFT_REPORT_IDS, - callback: (val) => { - if (!val) { - return; - } - - draftReportIDs = val; - }, -}); - const allPolicies = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY, @@ -43,12 +31,11 @@ Onyx.connect({ const cleanUpMergeQueries = {}; const cleanUpSetQueries = {}; _.each(policyReports, ({reportID}) => { + cleanUpMergeQueries[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] = {hasDraft: false}; cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; - delete draftReportIDs[reportID]; }); Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); Onyx.multiSet(cleanUpSetQueries); - Onyx.set(ONYXKEYS.DRAFT_REPORT_IDS, draftReportIDs); delete allPolicies[key]; return; } @@ -124,6 +111,7 @@ function deleteWorkspace(policyID, reports, policyName) { value: { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS.CLOSED, + hasDraft: false, oldPolicyName: allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`].name, }, })), @@ -148,12 +136,13 @@ function deleteWorkspace(policyID, reports, policyName) { // Restore the old report stateNum and statusNum const failureData = [ - ..._.map(reports, ({reportID, stateNum, statusNum, oldPolicyName}) => ({ + ..._.map(reports, ({reportID, stateNum, statusNum, hasDraft, oldPolicyName}) => ({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { stateNum, statusNum, + hasDraft, oldPolicyName, }, })), diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 2fa7de2e4434..3ad3f3def7b9 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -981,6 +981,17 @@ function saveReportCommentNumberOfLines(reportID, numberOfLines) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}`, numberOfLines); } +/** + * Immediate indication whether the report has a draft comment. + * + * @param {String} reportID + * @param {Boolean} hasDraft + * @returns {Promise} + */ +function setReportWithDraft(reportID, hasDraft) { + return Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {hasDraft}); +} + /** * Broadcasts whether or not a user is typing on a report over the report's private pusher channel. * @@ -2441,6 +2452,7 @@ export { saveReportActionDraftNumberOfLines, deleteReportComment, navigateToConciergeChat, + setReportWithDraft, addPolicyReport, deleteReport, navigateToConciergeChatAndDeleteReport, diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index f68959c9520b..9f4b990f0bc6 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -34,7 +34,6 @@ import withKeyboardState from '../../../../components/withKeyboardState'; import {propTypes, defaultProps} from './composerWithSuggestionsProps'; import focusWithDelay from '../../../../libs/focusWithDelay'; import useDebounce from '../../../../hooks/useDebounce'; -import setDraftStatusForReportID from '../../../../libs/actions/DraftReports'; import updateMultilineInputRange from '../../../../libs/UpdateMultilineInputRange'; import * as InputFocus from '../../../../libs/actions/InputFocus'; @@ -246,12 +245,12 @@ function ComposerWithSuggestions({ // Indicate that draft has been created. if (commentRef.current.length === 0 && newComment.length !== 0) { - setDraftStatusForReportID(reportID, true); + Report.setReportWithDraft(reportID, true); } // The draft has been deleted. if (newComment.length === 0) { - setDraftStatusForReportID(reportID, false); + Report.setReportWithDraft(reportID, false); } commentRef.current = newComment; @@ -511,7 +510,7 @@ function ComposerWithSuggestions({ if (value.length === 0) { return; } - setDraftStatusForReportID(reportID, true); + Report.setReportWithDraft(reportID, true); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/pages/home/sidebar/SidebarLinksData.js b/src/pages/home/sidebar/SidebarLinksData.js index da7020e74166..74ac80fc099d 100644 --- a/src/pages/home/sidebar/SidebarLinksData.js +++ b/src/pages/home/sidebar/SidebarLinksData.js @@ -52,10 +52,6 @@ const propTypes = { /** The policies which the user has access to */ // eslint-disable-next-line react/forbid-prop-types policies: PropTypes.object, - - /** Holds the reportIDs which are in draft */ - // eslint-disable-next-line react/forbid-prop-types - draftReportIDs: PropTypes.object, }; const defaultProps = { @@ -64,17 +60,16 @@ const defaultProps = { isLoadingReportData: true, priorityMode: CONST.PRIORITY_MODE.DEFAULT, betas: [], - draftReportIDs: {}, policies: {}, }; -function SidebarLinksData({isFocused, draftReportIDs, allReportActions, betas, chatReports, currentReportID, insets, isLoadingReportData, onLinkClick, policies, priorityMode}) { +function SidebarLinksData({isFocused, allReportActions, betas, chatReports, currentReportID, insets, isLoadingReportData, onLinkClick, policies, priorityMode}) { const {translate} = useLocalize(); const reportIDsRef = useRef(null); const isLoading = SessionUtils.didUserLogInDuringSession() && isLoadingReportData; const optionListItems = useMemo(() => { - const reportIDs = SidebarUtils.getOrderedReportIDs(null, draftReportIDs, chatReports, betas, policies, priorityMode, allReportActions); + const reportIDs = SidebarUtils.getOrderedReportIDs(null, chatReports, betas, policies, priorityMode, allReportActions); if (deepEqual(reportIDsRef.current, reportIDs)) { return reportIDsRef.current; } @@ -84,7 +79,7 @@ function SidebarLinksData({isFocused, draftReportIDs, allReportActions, betas, c reportIDsRef.current = reportIDs; } return reportIDsRef.current || []; - }, [allReportActions, betas, chatReports, draftReportIDs, policies, priorityMode, isLoading]); + }, [allReportActions, betas, chatReports, policies, priorityMode, isLoading]); // 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 @@ -93,10 +88,10 @@ function SidebarLinksData({isFocused, draftReportIDs, allReportActions, betas, c // case we re-generate the list a 2nd time with the current report included. const optionListItemsWithCurrentReport = useMemo(() => { if (currentReportID && !_.contains(optionListItems, currentReportID)) { - return SidebarUtils.getOrderedReportIDs(currentReportID, draftReportIDs, chatReports, betas, policies, priorityMode, allReportActions); + return SidebarUtils.getOrderedReportIDs(currentReportID, chatReports, betas, policies, priorityMode, allReportActions); } return optionListItems; - }, [currentReportID, optionListItems, chatReports, betas, draftReportIDs, policies, priorityMode, allReportActions]); + }, [currentReportID, optionListItems, chatReports, betas, policies, priorityMode, allReportActions]); const currentReportIDRef = useRef(currentReportID); currentReportIDRef.current = currentReportID; @@ -136,6 +131,7 @@ const chatReportSelector = (report) => report && { reportID: report.reportID, participantAccountIDs: report.participantAccountIDs, + hasDraft: report.hasDraft, isPinned: report.isPinned, isHidden: report.isHidden, errorFields: { @@ -206,10 +202,6 @@ export default compose( isLoadingReportData: { key: ONYXKEYS.IS_LOADING_REPORT_DATA, }, - draftReportIDs: { - key: ONYXKEYS.DRAFT_REPORT_IDS, - initialValue: {}, - }, priorityMode: { key: ONYXKEYS.NVP_PRIORITY_MODE, initialValue: CONST.PRIORITY_MODE.DEFAULT, diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 44328ccf04cf..8587cf9b7cd5 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -61,6 +61,7 @@ type Report = { parentReportID?: string; parentReportActionID?: string; isOptimisticReport?: boolean; + hasDraft?: boolean; managerID?: number; lastVisibleActionLastModified?: string; displayName?: string; diff --git a/tests/unit/OptionsListUtilsTest.js b/tests/unit/OptionsListUtilsTest.js index 0783303d93af..7a9fbb558455 100644 --- a/tests/unit/OptionsListUtilsTest.js +++ b/tests/unit/OptionsListUtilsTest.js @@ -17,6 +17,7 @@ describe('OptionsListUtils', () => { reportID: 1, participantAccountIDs: [2, 1], reportName: 'Iron Man, Mister Fantastic', + hasDraft: true, type: CONST.REPORT.TYPE.CHAT, }, 2: { diff --git a/tests/unit/SidebarFilterTest.js b/tests/unit/SidebarFilterTest.js index bf839b0b36d8..18e499d89293 100644 --- a/tests/unit/SidebarFilterTest.js +++ b/tests/unit/SidebarFilterTest.js @@ -23,7 +23,6 @@ const ONYXKEYS = { POLICY: 'policy_', }, NETWORK: 'network', - DRAFT_REPORT_IDS: 'draftReportIDs', }; describe('Sidebar', () => { @@ -101,7 +100,12 @@ describe('Sidebar', () => { it('includes an empty chat report if it has a draft', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); - const report = LHNTestUtils.getFakeReport([1, 2], 0); + // Given a new report with a draft text + const report = { + ...LHNTestUtils.getFakeReport([1, 2], 0), + hasDraft: true, + }; + return ( waitForBatchedUpdates() // When Onyx is updated to contain that report @@ -110,9 +114,6 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, - - // Set the draft status for the given reportID - [ONYXKEYS.DRAFT_REPORT_IDS]: {[report.reportID]: true}, }), ) @@ -340,9 +341,6 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, - - // Set the draft status for the given reportID - [ONYXKEYS.DRAFT_REPORT_IDS]: {[report2.reportID]: boolArr[boolArr.length - 1]}, }), ) // Then depending on the outcome, either one or two reports are visible @@ -437,7 +435,10 @@ describe('Sidebar', () => { it('always shows pinned and draft chats', () => { // Given a draft report and a pinned report - const draftReport = LHNTestUtils.getFakeReport([1, 2]); + const draftReport = { + ...LHNTestUtils.getFakeReport([1, 2]), + hasDraft: true, + }; const pinnedReport = { ...LHNTestUtils.getFakeReport([3, 4]), isPinned: true, @@ -454,9 +455,6 @@ describe('Sidebar', () => { [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, [`${ONYXKEYS.COLLECTION.REPORT}${draftReport.reportID}`]: draftReport, [`${ONYXKEYS.COLLECTION.REPORT}${pinnedReport.reportID}`]: pinnedReport, - - // Set the draft status for the given reportID - [ONYXKEYS.DRAFT_REPORT_IDS]: {[draftReport.reportID]: true}, }), ) @@ -668,9 +666,6 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, - - // Set the draft status for the given reportID - [ONYXKEYS.DRAFT_REPORT_IDS]: {[report2.reportID]: boolArr[boolArr.length - 1]}, }), ) diff --git a/tests/unit/SidebarOrderTest.js b/tests/unit/SidebarOrderTest.js index 6b89b8a94179..4a693d679b86 100644 --- a/tests/unit/SidebarOrderTest.js +++ b/tests/unit/SidebarOrderTest.js @@ -24,7 +24,6 @@ const ONYXKEYS = { REPORT_ACTIONS: 'reportActions_', }, NETWORK: 'network', - DRAFT_REPORT_IDS: 'draftReportIDs', }; describe('Sidebar', () => { @@ -149,8 +148,12 @@ describe('Sidebar', () => { it('changes the order when adding a draft to the active report', () => { // Given three reports in the recently updated order of 3, 2, 1 + // And the first report has a draft // And the currently viewed report is the first report - const report1 = LHNTestUtils.getFakeReport([1, 2], 3); + const report1 = { + ...LHNTestUtils.getFakeReport([1, 2], 3), + hasDraft: true, + }; const report2 = LHNTestUtils.getFakeReport([3, 4], 2); const report3 = LHNTestUtils.getFakeReport([5, 6], 1); @@ -173,9 +176,6 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, - - // Setting the draft status for first report - [ONYXKEYS.DRAFT_REPORT_IDS]: {[report1.reportID]: true}, }), ) @@ -188,7 +188,7 @@ describe('Sidebar', () => { const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); const displayNames = screen.queryAllByLabelText(hintText); expect(displayNames).toHaveLength(3); - expect(lodashGet(displayNames, [0, 'props', 'children'])).toBe('One, Two'); // this has a `reportID` in `draftReportID` so it will be on top + expect(lodashGet(displayNames, [0, 'props', 'children'])).toBe('One, Two'); // this has `hasDraft` flag enabled so it will be on top expect(lodashGet(displayNames, [1, 'props', 'children'])).toBe('Five, Six'); expect(lodashGet(displayNames, [2, 'props', 'children'])).toBe('Three, Four'); }) @@ -244,9 +244,13 @@ describe('Sidebar', () => { it('reorders the reports to keep draft reports on top', () => { // Given three reports in the recently updated order of 3, 2, 1 + // And the second report has a draft // And the currently viewed report is the second report const report1 = LHNTestUtils.getFakeReport([1, 2], 3); - const report2 = LHNTestUtils.getFakeReport([3, 4], 2); + const report2 = { + ...LHNTestUtils.getFakeReport([3, 4], 2), + hasDraft: true, + }; const report3 = LHNTestUtils.getFakeReport([5, 6], 1); // Each report has at least one ADDCOMMENT action so should be rendered in the LNH @@ -268,9 +272,6 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, - - // Setting the draft status for second report - [ONYXKEYS.DRAFT_REPORT_IDS]: {[report2.reportID]: true}, }), ) @@ -299,7 +300,11 @@ describe('Sidebar', () => { LHNTestUtils.getDefaultRenderedSidebarLinks(); // Given a single report - const report = LHNTestUtils.getFakeReport([1, 2]); + // And the report has a draft + const report = { + ...LHNTestUtils.getFakeReport([1, 2]), + hasDraft: true, + }; return ( waitForBatchedUpdates() @@ -310,9 +315,6 @@ describe('Sidebar', () => { [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, [`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report, - - // Setting the draft status for the report - [ONYXKEYS.DRAFT_REPORT_IDS]: {[report.reportID]: true}, }), ) @@ -322,7 +324,7 @@ describe('Sidebar', () => { }) // When the draft is removed - .then(() => Onyx.set(ONYXKEYS.DRAFT_REPORT_IDS, {})) + .then(() => Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, {hasDraft: null})) // Then the pencil icon goes away .then(() => { @@ -371,13 +373,16 @@ describe('Sidebar', () => { it('sorts chats by pinned > IOU > draft', () => { // Given three reports in the recently updated order of 3, 2, 1 // with the current user set to email9@ (someone not participating in any of the chats) - // with a report that is pinned, and + // with a report that has a draft, a report that is pinned, and // an outstanding IOU report that doesn't belong to the current user const report1 = { ...LHNTestUtils.getFakeReport([1, 2], 3), isPinned: true, }; - const report2 = LHNTestUtils.getFakeReport([3, 4], 2); + const report2 = { + ...LHNTestUtils.getFakeReport([3, 4], 2), + hasDraft: true, + }; const report3 = { ...LHNTestUtils.getFakeReport([5, 6], 1), hasOutstandingIOU: false, @@ -413,9 +418,6 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, [`${ONYXKEYS.COLLECTION.REPORT}${iouReport.reportID}`]: iouReport, - - // Setting the draft status for second report - [ONYXKEYS.DRAFT_REPORT_IDS]: {[report2.reportID]: true}, }), ) @@ -497,10 +499,23 @@ describe('Sidebar', () => { it('alphabetizes all the chats that have drafts', () => { // Given three reports in the recently updated order of 3, 2, 1 - const report1 = LHNTestUtils.getFakeReport([1, 2], 3); - const report2 = LHNTestUtils.getFakeReport([3, 4], 2); - const report3 = LHNTestUtils.getFakeReport([5, 6], 1); - const report4 = LHNTestUtils.getFakeReport([7, 8], 0); + // and they all have drafts + const report1 = { + ...LHNTestUtils.getFakeReport([1, 2], 3), + hasDraft: true, + }; + const report2 = { + ...LHNTestUtils.getFakeReport([3, 4], 2), + hasDraft: true, + }; + const report3 = { + ...LHNTestUtils.getFakeReport([5, 6], 1), + hasDraft: true, + }; + const report4 = { + ...LHNTestUtils.getFakeReport([7, 8], 0), + hasDraft: true, + }; LHNTestUtils.getDefaultRenderedSidebarLinks('0'); return ( waitForBatchedUpdates() @@ -513,14 +528,6 @@ describe('Sidebar', () => { [`${ONYXKEYS.COLLECTION.REPORT}${report1.reportID}`]: report1, [`${ONYXKEYS.COLLECTION.REPORT}${report2.reportID}`]: report2, [`${ONYXKEYS.COLLECTION.REPORT}${report3.reportID}`]: report3, - - // Setting the draft status for all reports - [ONYXKEYS.DRAFT_REPORT_IDS]: { - [report1.reportID]: true, - [report2.reportID]: true, - [report3.reportID]: true, - [report4.reportID]: true, - }, }), ) diff --git a/tests/utils/LHNTestUtils.js b/tests/utils/LHNTestUtils.js index 15f8a5d723cc..ce4edc75b444 100644 --- a/tests/utils/LHNTestUtils.js +++ b/tests/utils/LHNTestUtils.js @@ -151,9 +151,10 @@ function getFakeReportAction(actor = 'email1@test.com', millisecondsInThePast = * @param {boolean} hasAddWorkspaceError * @param {boolean} isUnread * @param {boolean} isPinned + * @param {boolean} hasDraft * @returns {Object} */ -function getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned) { +function getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorkspaceError, isUnread, isPinned, hasDraft) { return { ...getFakeReport([1, 2], 0, isUnread), type: CONST.REPORT.TYPE.CHAT, @@ -162,6 +163,7 @@ function getAdvancedFakeReport(isArchived, isUserCreatedPolicyRoom, hasAddWorksp stateNum: isArchived ? CONST.REPORT.STATE_NUM.SUBMITTED : 0, errorFields: hasAddWorkspaceError ? {addWorkspaceRoom: 'blah'} : null, isPinned, + hasDraft, }; } From d66e573b8827881e52fb7375b4e0e218de9c9a40 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 31 Oct 2023 12:00:40 +0500 Subject: [PATCH 21/21] fix: typecheck --- src/libs/actions/Welcome.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Welcome.ts b/src/libs/actions/Welcome.ts index b78840c15cc1..7fd7adeafa96 100644 --- a/src/libs/actions/Welcome.ts +++ b/src/libs/actions/Welcome.ts @@ -69,7 +69,6 @@ Onyx.connect({ const allReports: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, - waitForCollectionCallback: true, initWithStoredValues: false, callback: (val, key) => { if (!val || !key) {