diff --git a/src/components/ExceededCommentLength.js b/src/components/ExceededCommentLength.js index 2aa50779e10f..97ce63cc4144 100644 --- a/src/components/ExceededCommentLength.js +++ b/src/components/ExceededCommentLength.js @@ -54,5 +54,6 @@ ExceededCommentLength.displayName = 'ExceededCommentLength'; export default withOnyx({ comment: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, + initialValue: null, }, })(ExceededCommentLength); diff --git a/src/components/ReportActionItem/TaskPreview.js b/src/components/ReportActionItem/TaskPreview.js index ca4103624440..47716c0ec65a 100644 --- a/src/components/ReportActionItem/TaskPreview.js +++ b/src/components/ReportActionItem/TaskPreview.js @@ -116,9 +116,11 @@ export default compose( withOnyx({ taskReport: { key: ({taskReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${taskReportID}`, + initialValue: {}, }, personalDetailsList: { key: ONYXKEYS.PERSONAL_DETAILS_LIST, + initialValue: {}, }, }), )(TaskPreview); diff --git a/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.js b/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.js new file mode 100644 index 000000000000..85e34ae36aa3 --- /dev/null +++ b/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.js @@ -0,0 +1,129 @@ +import {useEffect} from 'react'; +import PropTypes from 'prop-types'; +import lodashGet from 'lodash/get'; +import {withOnyx} from 'react-native-onyx'; +import ONYXKEYS from '../../../ONYXKEYS'; +import * as ReportUtils from '../../ReportUtils'; +import reportPropTypes from '../../../pages/reportPropTypes'; +import {withNavigationPropTypes} from '../../../components/withNavigation'; +import * as App from '../../actions/App'; +import usePermissions from '../../../hooks/usePermissions'; +import CONST from '../../../CONST'; +import Navigation from '../Navigation'; + +const propTypes = { + /** Available reports that would be displayed in this navigator */ + reports: PropTypes.objectOf(reportPropTypes), + + /** The policies which the user has access to */ + policies: PropTypes.objectOf( + PropTypes.shape({ + /** The policy name */ + name: PropTypes.string, + + /** The type of the policy */ + type: PropTypes.string, + }), + ), + + isFirstTimeNewExpensifyUser: PropTypes.bool, + + /** Navigation route context info provided by react navigation */ + route: PropTypes.shape({ + /** Route specific parameters used on this screen */ + params: PropTypes.shape({ + /** If the admin room should be opened */ + openOnAdminRoom: PropTypes.bool, + + /** The ID of the report this screen should display */ + reportID: PropTypes.string, + }), + }).isRequired, + + ...withNavigationPropTypes, +}; + +const defaultProps = { + reports: {}, + policies: {}, + isFirstTimeNewExpensifyUser: false, +}; + +/** + * Get the most recently accessed report for the user + * + * @param {Object} reports + * @param {Boolean} [ignoreDefaultRooms] + * @param {Object} policies + * @param {Boolean} isFirstTimeNewExpensifyUser + * @param {Boolean} openOnAdminRoom + * @returns {Number} + */ +const getLastAccessedReportID = (reports, ignoreDefaultRooms, policies, isFirstTimeNewExpensifyUser, openOnAdminRoom) => { + // If deeplink url is of an attachment, we should show the report that the attachment comes from. + const currentRoute = Navigation.getActiveRoute(); + const matches = CONST.REGEX.ATTACHMENT_ROUTE.exec(currentRoute); + const reportID = lodashGet(matches, 1, null); + if (reportID) { + return reportID; + } + + const lastReport = ReportUtils.findLastAccessedReport(reports, ignoreDefaultRooms, policies, isFirstTimeNewExpensifyUser, openOnAdminRoom); + + return lodashGet(lastReport, 'reportID'); +}; + +// This wrapper is reponsible for opening the last accessed report if there is no reportID specified in the route params +function ReportScreenIDSetter(props) { + const {canUseDefaultRooms} = usePermissions(); + + useEffect(() => { + // Don't update if there is a reportID in the params already + if (lodashGet(props.route, 'params.reportID', null)) { + App.confirmReadyToOpenApp(); + return; + } + + // If there is no reportID in route, try to find last accessed and use it for setParams + const reportID = getLastAccessedReportID( + props.reports, + !canUseDefaultRooms, + props.policies, + props.isFirstTimeNewExpensifyUser, + lodashGet(props.route, 'params.openOnAdminRoom', false), + ); + + // It's possible that props.reports aren't fully loaded yet + // in that case the reportID is undefined + if (reportID) { + performance.mark('ReportScreenConfirmer_hook'); + performance.measure('ReportScreenConfirmer_hook_meassure', {start: 'ReportScreenConfirmer_hook', duration: 100}); + props.navigation.setParams({reportID: String(reportID)}); + } else { + App.confirmReadyToOpenApp(); + } + }, [props.route, props.navigation, props.reports, canUseDefaultRooms, props.policies, props.isFirstTimeNewExpensifyUser]); + + // The ReportScreen without the reportID set will display a skeleton + // until the reportID is loaded and set in the route param + return null; +} + +ReportScreenIDSetter.propTypes = propTypes; +ReportScreenIDSetter.defaultProps = defaultProps; +ReportScreenIDSetter.displayName = 'ReportScreenIDSetter'; + +export default withOnyx({ + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + allowStaleData: true, + }, + policies: { + key: ONYXKEYS.COLLECTION.POLICY, + allowStaleData: true, + }, + isFirstTimeNewExpensifyUser: { + key: ONYXKEYS.NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER, + initialValue: false, + }, +})(ReportScreenIDSetter); diff --git a/src/libs/Navigation/AppNavigator/ReportScreenWrapper.js b/src/libs/Navigation/AppNavigator/ReportScreenWrapper.js index f1743e1a2269..767bd9793ac2 100644 --- a/src/libs/Navigation/AppNavigator/ReportScreenWrapper.js +++ b/src/libs/Navigation/AppNavigator/ReportScreenWrapper.js @@ -1,36 +1,10 @@ -import React, {useEffect} from 'react'; import PropTypes from 'prop-types'; -import lodashGet from 'lodash/get'; -import {withOnyx} from 'react-native-onyx'; - -import ONYXKEYS from '../../../ONYXKEYS'; - -import ReportScreen from '../../../pages/home/ReportScreen'; -import * as ReportUtils from '../../ReportUtils'; -import reportPropTypes from '../../../pages/reportPropTypes'; +import React from 'react'; import {withNavigationPropTypes} from '../../../components/withNavigation'; -import * as App from '../../actions/App'; -import usePermissions from '../../../hooks/usePermissions'; -import CONST from '../../../CONST'; -import Navigation from '../Navigation'; +import ReportScreen from '../../../pages/home/ReportScreen'; +import ReportScreenIDSetter from './ReportScreenIDSetter'; const propTypes = { - /** Available reports that would be displayed in this navigator */ - reports: PropTypes.objectOf(reportPropTypes), - - /** The policies which the user has access to */ - policies: PropTypes.objectOf( - PropTypes.shape({ - /** The policy name */ - name: PropTypes.string, - - /** The type of the policy */ - type: PropTypes.string, - }), - ), - - isFirstTimeNewExpensifyUser: PropTypes.bool, - /** Navigation route context info provided by react navigation */ route: PropTypes.shape({ /** Route specific parameters used on this screen */ @@ -46,82 +20,24 @@ const propTypes = { ...withNavigationPropTypes, }; -const defaultProps = { - reports: {}, - policies: {}, - isFirstTimeNewExpensifyUser: false, -}; - -/** - * Get the most recently accessed report for the user - * - * @param {Object} reports - * @param {Boolean} [ignoreDefaultRooms] - * @param {Object} policies - * @param {Boolean} isFirstTimeNewExpensifyUser - * @param {Boolean} openOnAdminRoom - * @returns {Number} - */ -const getLastAccessedReportID = (reports, ignoreDefaultRooms, policies, isFirstTimeNewExpensifyUser, openOnAdminRoom) => { - // If deeplink url is of an attachment, we should show the report that the attachment comes from. - const currentRoute = Navigation.getActiveRoute(); - const matches = CONST.REGEX.ATTACHMENT_ROUTE.exec(currentRoute); - const reportID = lodashGet(matches, 1, null); - if (reportID) { - return reportID; - } - - const lastReport = ReportUtils.findLastAccessedReport(reports, ignoreDefaultRooms, policies, isFirstTimeNewExpensifyUser, openOnAdminRoom); - - return lodashGet(lastReport, 'reportID'); -}; +const defaultProps = {}; -// This wrapper is reponsible for opening the last accessed report if there is no reportID specified in the route params function ReportScreenWrapper(props) { - const {canUseDefaultRooms} = usePermissions(); - - useEffect(() => { - // Don't update if there is a reportID in the params already - if (lodashGet(props.route, 'params.reportID', null)) { - App.confirmReadyToOpenApp(); - return; - } - - // If there is no reportID in route, try to find last accessed and use it for setParams - const reportID = getLastAccessedReportID( - props.reports, - !canUseDefaultRooms, - props.policies, - props.isFirstTimeNewExpensifyUser, - lodashGet(props.route, 'params.openOnAdminRoom', false), - ); - - // It's possible that props.reports aren't fully loaded yet - // in that case the reportID is undefined - if (reportID) { - props.navigation.setParams({reportID: String(reportID)}); - } else { - App.confirmReadyToOpenApp(); - } - }, [props.route, props.navigation, props.reports, canUseDefaultRooms, props.policies, props.isFirstTimeNewExpensifyUser]); - // The ReportScreen without the reportID set will display a skeleton // until the reportID is loaded and set in the route param - return ; + return ( + <> + + + + ); } ReportScreenWrapper.propTypes = propTypes; ReportScreenWrapper.defaultProps = defaultProps; ReportScreenWrapper.displayName = 'ReportScreenWrapper'; -export default withOnyx({ - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - isFirstTimeNewExpensifyUser: { - key: ONYXKEYS.NVP_IS_FIRST_TIME_NEW_EXPENSIFY_USER, - }, -})(ReportScreenWrapper); +export default ReportScreenWrapper; diff --git a/src/libs/Navigation/NavigationRoot.js b/src/libs/Navigation/NavigationRoot.js index 42d6627d6699..b992961f60ed 100644 --- a/src/libs/Navigation/NavigationRoot.js +++ b/src/libs/Navigation/NavigationRoot.js @@ -116,7 +116,10 @@ function NavigationRoot(props) { if (!state) { return; } - updateCurrentReportID(state); + // Performance optimization to avoid context consumers to delay first render + setTimeout(() => { + updateCurrentReportID(state); + }, 0); parseAndLogRoute(state); animateStatusBarBackgroundColor(); }; diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 74994e4dc9d0..d9251a89af3b 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -25,7 +25,6 @@ import reportPropTypes from '../reportPropTypes'; import ONYXKEYS from '../../ONYXKEYS'; import ThreeDotsMenu from '../../components/ThreeDotsMenu'; import * as Task from '../../libs/actions/Task'; -import reportActionPropTypes from './report/reportActionPropTypes'; import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback'; import PinButton from '../../components/PinButton'; import TaskHeaderActionButton from '../../components/TaskHeaderActionButton'; @@ -44,33 +43,22 @@ const propTypes = { /** Onyx Props */ parentReport: reportPropTypes, - /** The details about the account that the user is signing in with */ - account: PropTypes.shape({ - /** URL to the assigned guide's appointment booking calendar */ - guideCalendarLink: PropTypes.string, - }), + /** URL to the assigned guide's appointment booking calendar */ + guideCalendarLink: PropTypes.string, /** Current user session */ session: PropTypes.shape({ accountID: PropTypes.number, }), - /** The report actions from the parent report */ - // TO DO: Replace with HOC https://github.com/Expensify/App/issues/18769. - // eslint-disable-next-line react/no-unused-prop-types - parentReportActions: PropTypes.objectOf(PropTypes.shape(reportActionPropTypes)), - ...windowDimensionsPropTypes, ...withLocalizePropTypes, }; const defaultProps = { personalDetails: {}, - parentReportActions: {}, report: null, - account: { - guideCalendarLink: null, - }, + guideCalendarLink: null, parentReport: {}, session: { accountID: 0, @@ -92,11 +80,10 @@ function HeaderView(props) { const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(reportHeaderData); const isConcierge = ReportUtils.hasSingleParticipant(props.report) && _.contains(participants, CONST.ACCOUNT_ID.CONCIERGE); const isAutomatedExpensifyAccount = ReportUtils.hasSingleParticipant(props.report) && ReportUtils.hasAutomatedExpensifyAccountIDs(participants); - const guideCalendarLink = lodashGet(props.account, 'guideCalendarLink'); // We hide the button when we are chatting with an automated Expensify account since it's not possible to contact // these users via alternative means. It is possible to request a call with Concierge so we leave the option for them. - const shouldShowCallButton = (isConcierge && guideCalendarLink) || (!isAutomatedExpensifyAccount && !isTaskReport); + const shouldShowCallButton = (isConcierge && props.guideCalendarLink) || (!isAutomatedExpensifyAccount && !isTaskReport); const threeDotMenuItems = []; if (isTaskReport) { const canModifyTask = Task.canModifyTask(props.report, props.session.accountID); @@ -219,7 +206,7 @@ function HeaderView(props) { {shouldShowCallButton && ( )} @@ -244,17 +231,10 @@ export default compose( withWindowDimensions, withLocalize, withOnyx({ - account: { + guideCalendarLink: { key: ONYXKEYS.ACCOUNT, - selector: (account) => - account && { - guideCalendarLink: account.guideCalendarLink, - primaryLogin: account.primaryLogin, - }, - }, - parentReportActions: { - key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, - canEvict: false, + selector: (account) => (account && account.guideCalendarLink ? account.guideCalendarLink : null), + initialValue: null, }, parentReport: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID || report.reportID}`, diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index c6370ea92e3f..80f4cf8b42ef 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -90,6 +90,9 @@ const propTypes = { /** All of the personal details for everyone */ personalDetails: PropTypes.objectOf(personalDetailsPropType), + /** Onyx function that marks the component ready for hydration */ + allowOnyxUpdates: PropTypes.func, + ...windowDimensionsPropTypes, ...viewportOffsetTopPropTypes, }; @@ -109,6 +112,7 @@ const defaultProps = { policies: {}, accountManagerReportID: null, personalDetails: {}, + allowOnyxUpdates: null, }; /** @@ -131,6 +135,7 @@ function ReportScreen({ reportActions, accountManagerReportID, personalDetails, + allowOnyxUpdates, policies, translate, network, @@ -157,7 +162,7 @@ function ReportScreen({ const shouldHideReport = !ReportUtils.canAccessReport(report, policies, betas); - const isLoading = !reportID || !isSidebarLoaded || _.isEmpty(personalDetails) || firstRenderRef.current; + const isLoading = !reportID || !isSidebarLoaded || _.isEmpty(personalDetails); const parentReportAction = ReportActionsUtils.getParentReportAction(report); const isDeletedParentAction = ReportActionsUtils.isDeletedParentAction(parentReportAction); @@ -304,6 +309,14 @@ function ReportScreen({ ComposerActions.setShouldShowComposeInput(true); }, [route, report, errors, fetchReportIfNeeded, prevReport.reportID]); + const onListLayout = useCallback(() => { + if (!allowOnyxUpdates) { + return; + } + allowOnyxUpdates(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return ( @@ -336,7 +349,7 @@ function ReportScreen({ )} - {Boolean(accountManagerReportID) && ReportUtils.isConciergeChatReport(report) && isBannerVisible && ( + {!!accountManagerReportID && ReportUtils.isConciergeChatReport(report) && isBannerVisible && ( `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getReportID(route)}`, - canEvict: false, - selector: ReportActionsUtils.getSortedReportActionsForDisplay, + withOnyx( + { + isSidebarLoaded: { + key: ONYXKEYS.IS_SIDEBAR_LOADED, + }, + reportActions: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getReportID(route)}`, + canEvict: false, + selector: ReportActionsUtils.getSortedReportActionsForDisplay, + }, + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${getReportID(route)}`, + allowStaleData: true, + }, + reportMetadata: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_METADATA}${getReportID(route)}`, + initialValue: { + isLoadingReportActions: false, + isLoadingMoreReportActions: false, + }, + }, + isComposerFullSize: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${getReportID(route)}`, + initialValue: false, + }, + betas: { + key: ONYXKEYS.BETAS, + }, + policies: { + key: ONYXKEYS.COLLECTION.POLICY, + allowStaleData: true, + }, + accountManagerReportID: { + key: ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, + initialValue: null, + }, + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, }, - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${getReportID(route)}`, + { + delayUpdates: true, }, - reportMetadata: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_METADATA}${getReportID(route)}`, - }, - isComposerFullSize: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${getReportID(route)}`, - }, - betas: { - key: ONYXKEYS.BETAS, - }, - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - accountManagerReportID: { - key: ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, - }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - }), + ), )(ReportScreen); diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater.js b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater.js index da5dc326d421..90b9fcaa6668 100644 --- a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater.js +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater.js @@ -68,5 +68,6 @@ SilentCommentUpdater.displayName = 'SilentCommentUpdater'; export default withOnyx({ comment: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, + initialValue: null, }, })(SilentCommentUpdater); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 324d47080f88..9962409dd6da 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -632,12 +632,14 @@ export default compose( withOnyx({ preferredSkinTone: { key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, + initialValue: CONST.EMOJI_DEFAULT_SKIN_TONE, }, iouReport: { key: ({action}) => `${ONYXKEYS.COLLECTION.REPORT}${ReportActionsUtils.getIOUReportIDFromReportActionPreview(action)}`, }, emojiReactions: { key: ({action}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${action.reportActionID}`, + initialValue: {}, }, }), )( diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index febf9d809d7f..52916fca7655 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -119,6 +119,7 @@ function ReportActionsList({ const scrollingVerticalOffset = useRef(0); const readActionSkipped = useRef(false); const reportActionSize = useRef(sortedReportActions.length); + const firstRenderRef = useRef(true); // This state is used to force a re-render when the user manually marks a message as unread // by using a timestamp you can force re-renders without having to worry about if another message was marked as unread before @@ -286,6 +287,40 @@ function ReportActionsList({ const hideComposer = ReportUtils.shouldDisableWriteActions(report); const shouldShowReportRecipientLocalTime = ReportUtils.canShowReportRecipientLocalTime(personalDetailsList, report, currentUserPersonalDetails.accountID) && !isComposerFullSize; + const renderFooter = useCallback(() => { + if (firstRenderRef.current) { + firstRenderRef.current = false; + return null; + } + + if (isLoadingMoreReportActions) { + return ; + } + + // Make sure the oldest report action loaded is not the first. This is so we do not show the + // skeleton view above the created action in a newly generated optimistic chat or one with not + // that many comments. + const lastReportAction = _.last(sortedReportActions) || {}; + if (isLoadingReportActions && lastReportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED) { + return ( + + ); + } + + return null; + }, [isLoadingMoreReportActions, isLoadingReportActions, sortedReportActions, isOffline, skeletonViewHeight]); + + const onLayoutInner = useCallback( + (event) => { + setSkeletonViewHeight(event.nativeEvent.layout.height); + onLayout(event); + }, + [onLayout], + ); + return ( <> { - if (isLoadingMoreReportActions) { - return ; - } - - // Make sure the oldest report action loaded is not the first. This is so we do not show the - // skeleton view above the created action in a newly generated optimistic chat or one with not - // that many comments. - const lastReportAction = _.last(sortedReportActions) || {}; - if (isLoadingReportActions && lastReportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED) { - return ( - - ); - } - - return null; - }} + ListFooterComponent={renderFooter} keyboardShouldPersistTaps="handled" - onLayout={(event) => { - setSkeletonViewHeight(event.nativeEvent.layout.height); - onLayout(event); - }} + onLayout={onLayoutInner} onScroll={trackVerticalScrolling} extraData={extraData} /> diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 73f4e37e152f..f21884fe4770 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -50,6 +50,9 @@ const propTypes = { avatar: PropTypes.string, }), + /** Used by parent component to tell Onyx when to hydrate data */ + onLayout: PropTypes.func.isRequired, + ...windowDimensionsPropTypes, ...withLocalizePropTypes, }; @@ -163,6 +166,8 @@ function ReportActionsView(props) { * Runs when the FlatList finishes laying out */ const recordTimeToMeasureItemLayout = () => { + props.onLayout(); + if (didLayout.current) { return; } diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index 8d92c09b7a6e..80486b514106 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -102,5 +102,6 @@ export default compose( withWindowDimensions, withOnyx({ shouldShowComposeInput: {key: ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT}, + initialValue: false, }), )(ReportFooter); diff --git a/src/pages/home/report/ReportTypingIndicator.js b/src/pages/home/report/ReportTypingIndicator.js index 4de649c7eb49..db97f712d65f 100755 --- a/src/pages/home/report/ReportTypingIndicator.js +++ b/src/pages/home/report/ReportTypingIndicator.js @@ -74,6 +74,7 @@ export default compose( withOnyx({ userTypingStatuses: { key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, + initialValue: {}, }, }), )(ReportTypingIndicator);