From 00a7c235b3c37b96f022ada724f3aa6a0e6f2087 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 23 Oct 2023 15:41:54 +0500 Subject: [PATCH 01/50] perf: memoize components --- src/components/AttachmentModal.js | 4 ++-- src/components/EmojiPicker/EmojiPickerButton.js | 4 ++-- src/components/ExceededCommentLength.js | 4 ++-- src/pages/home/report/ReportActionCompose/SendButton.js | 4 ++-- src/pages/home/report/ReportTypingIndicator.js | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 61b138747950..9cceaab5e63e 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -1,4 +1,4 @@ -import React, {useState, useCallback, useRef, useMemo} from 'react'; +import React, {useState, useCallback, useRef, useMemo, memo} from 'react'; import PropTypes from 'prop-types'; import {View, Animated, Keyboard} from 'react-native'; import Str from 'expensify-common/lib/str'; @@ -534,4 +534,4 @@ export default compose( key: ONYXKEYS.SESSION, }, }), -)(AttachmentModal); +)(memo(AttachmentModal)); diff --git a/src/components/EmojiPicker/EmojiPickerButton.js b/src/components/EmojiPicker/EmojiPickerButton.js index cbfc3517117c..549e7d75005e 100644 --- a/src/components/EmojiPicker/EmojiPickerButton.js +++ b/src/components/EmojiPicker/EmojiPickerButton.js @@ -1,4 +1,4 @@ -import React, {useEffect, useRef} from 'react'; +import React, {memo, useEffect, useRef} from 'react'; import PropTypes from 'prop-types'; import styles from '../../styles/styles'; import * as StyleUtils from '../../styles/StyleUtils'; @@ -64,4 +64,4 @@ function EmojiPickerButton(props) { EmojiPickerButton.propTypes = propTypes; EmojiPickerButton.defaultProps = defaultProps; EmojiPickerButton.displayName = 'EmojiPickerButton'; -export default withLocalize(EmojiPickerButton); +export default withLocalize(memo(EmojiPickerButton)); diff --git a/src/components/ExceededCommentLength.js b/src/components/ExceededCommentLength.js index 9c785cec0395..587ab11cac37 100644 --- a/src/components/ExceededCommentLength.js +++ b/src/components/ExceededCommentLength.js @@ -1,4 +1,4 @@ -import React, {useEffect, useState, useMemo} from 'react'; +import React, {useEffect, useState, useMemo, memo} from 'react'; import PropTypes from 'prop-types'; import {debounce} from 'lodash'; import {withOnyx} from 'react-native-onyx'; @@ -65,4 +65,4 @@ export default withOnyx({ key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, initialValue: '', }, -})(ExceededCommentLength); +})(memo(ExceededCommentLength)); diff --git a/src/pages/home/report/ReportActionCompose/SendButton.js b/src/pages/home/report/ReportActionCompose/SendButton.js index a97dd420e181..0e1d10d44092 100644 --- a/src/pages/home/report/ReportActionCompose/SendButton.js +++ b/src/pages/home/report/ReportActionCompose/SendButton.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {memo} from 'react'; import {View} from 'react-native'; import {Gesture, GestureDetector} from 'react-native-gesture-handler'; import PropTypes from 'prop-types'; @@ -61,4 +61,4 @@ function SendButton({isDisabled: isDisabledProp, handleSendMessage}) { SendButton.propTypes = propTypes; SendButton.displayName = 'SendButton'; -export default SendButton; +export default memo(SendButton); diff --git a/src/pages/home/report/ReportTypingIndicator.js b/src/pages/home/report/ReportTypingIndicator.js index db97f712d65f..b4e5bbe8fe9d 100755 --- a/src/pages/home/report/ReportTypingIndicator.js +++ b/src/pages/home/report/ReportTypingIndicator.js @@ -1,4 +1,4 @@ -import React, {useMemo} from 'react'; +import React, {memo, useMemo} from 'react'; import PropTypes from 'prop-types'; import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; @@ -77,4 +77,4 @@ export default compose( initialValue: {}, }, }), -)(ReportTypingIndicator); +)(memo(ReportTypingIndicator)); From 9c4f2995a3a86c75ea17f039939199396049d379 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 23 Oct 2023 15:43:15 +0500 Subject: [PATCH 02/50] refactor: simplify props --- src/pages/home/ReportScreen.js | 50 ++++++++---- .../SilentCommentUpdater.js | 16 ++-- src/pages/home/report/ReportActionsList.js | 2 +- .../report/ReportActionsListItemRenderer.js | 9 +-- src/pages/home/report/ReportActionsView.js | 10 ++- src/pages/home/report/ReportFooter.js | 81 ++++++++++++------- 6 files changed, 107 insertions(+), 61 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 81000c2dab92..682edb701c14 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -1,10 +1,10 @@ -import React, {useRef, useState, useEffect, useMemo, useCallback} from 'react'; +import React, {useRef, useState, useEffect, useMemo, useCallback, memo} from 'react'; import {withOnyx} from 'react-native-onyx'; import {useFocusEffect} from '@react-navigation/native'; import PropTypes from 'prop-types'; import {View} from 'react-native'; import lodashGet from 'lodash/get'; -import _ from 'underscore'; +import _, {isEqual} from 'underscore'; import styles from '../../styles/styles'; import ScreenWrapper from '../../components/ScreenWrapper'; import HeaderView from './HeaderView'; @@ -163,8 +163,9 @@ function ReportScreen({ const {addWorkspaceRoomOrChatPendingAction, addWorkspaceRoomOrChatErrors} = ReportUtils.getReportOfflinePendingActionAndErrors(report); const screenWrapperStyle = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}]; + const isEmptyChat = useMemo(() => _.isEmpty(reportActions), [reportActions]); // There are no reportActions at all to display and we are still in the process of loading the next set of actions. - const isLoadingInitialReportActions = _.isEmpty(reportActions) && reportMetadata.isLoadingReportActions; + const isLoadingInitialReportActions = isEmptyChat && reportMetadata.isLoadingReportActions; const isOptimisticDelete = lodashGet(report, 'statusNum') === CONST.REPORT.STATUS.CLOSED; @@ -173,6 +174,10 @@ function ReportScreen({ const isLoading = !reportID || !isSidebarLoaded || _.isEmpty(personalDetails); const parentReportAction = ReportActionsUtils.getParentReportAction(report); + const lastReportAction = useMemo( + () => _.find([...reportActions, parentReportAction], (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action)), + [reportActions, parentReportAction], + ); const isSingleTransactionView = ReportUtils.isMoneyRequest(report); const policy = policies[`${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`] || {}; @@ -253,16 +258,6 @@ function ReportScreen({ Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(accountManagerReportID)); }, [accountManagerReportID]); - /** - * @param {String} text - */ - const onSubmitComment = useCallback( - (text) => { - Report.addComment(getReportID(route), text); - }, - [route], - ); - useFocusEffect( useCallback(() => { const unsubscribeVisibilityListener = Visibility.onVisibilityChange(() => { @@ -441,14 +436,15 @@ function ReportScreen({ {isReportReadyForDisplay ? ( ) : ( @@ -515,4 +511,24 @@ export default compose( }, true, ), -)(ReportScreen); +)( + memo( + ReportScreen, + (prevProps, nextProps) => + prevProps.isSidebarLoaded === nextProps.isSidebarLoaded && + isEqual(prevProps.reportActions, nextProps.reportActions) && + isEqual(prevProps.reportMetadata, nextProps.reportMetadata) && + prevProps.isComposerFullSize === nextProps.isComposerFullSize && + isEqual(prevProps.betas, nextProps.betas) && + isEqual(prevProps.policies, nextProps.policies) && + prevProps.accountManagerReportID === nextProps.accountManagerReportID && + isEqual(prevProps.personalDetails, nextProps.personalDetails) && + prevProps.userLeavingStatus === nextProps.userLeavingStatus && + prevProps.report.reportID === nextProps.report.reportID && + prevProps.report.policyID === nextProps.report.policyID && + prevProps.report.isOptimisticReport === nextProps.report.isOptimisticReport && + prevProps.report.statusNum === nextProps.report.statusNum && + isEqual(prevProps.report.pendingFields, nextProps.report.pendingFields) && + prevProps.currentReportID === nextProps.currentReportID, + ), +); diff --git a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater.js b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater.js index 09f9d368bdcc..2d7a65ca4cc7 100644 --- a/src/pages/home/report/ReportActionCompose/SilentCommentUpdater.js +++ b/src/pages/home/report/ReportActionCompose/SilentCommentUpdater.js @@ -9,12 +9,6 @@ const propTypes = { /** The comment of the report */ comment: PropTypes.string, - /** The report associated with the comment */ - report: PropTypes.shape({ - /** The ID of the report */ - reportID: PropTypes.string, - }).isRequired, - /** The value of the comment */ value: PropTypes.string.isRequired, @@ -26,6 +20,8 @@ const propTypes = { /** Updates the comment */ updateComment: PropTypes.func.isRequired, + + reportID: PropTypes.string.isRequired, }; const defaultProps = { @@ -38,9 +34,9 @@ const defaultProps = { * re-rendering a UI component for that. That's why the side effect was moved down to a separate component. * @returns {null} */ -function SilentCommentUpdater({comment, commentRef, report, value, updateComment}) { +function SilentCommentUpdater({comment, commentRef, reportID, value, updateComment}) { const prevCommentProp = usePrevious(comment); - const prevReportId = usePrevious(report.reportID); + const prevReportId = usePrevious(reportID); const {preferredLocale} = useLocalize(); const prevPreferredLocale = usePrevious(preferredLocale); @@ -51,12 +47,12 @@ function SilentCommentUpdater({comment, commentRef, report, value, updateComment // As the report IDs change, make sure to update the composer comment as we need to make sure // we do not show incorrect data in there (ie. draft of message from other report). - if (preferredLocale === prevPreferredLocale && report.reportID === prevReportId && !shouldSyncComment) { + if (preferredLocale === prevPreferredLocale && reportID === prevReportId && !shouldSyncComment) { return; } updateComment(comment); - }, [prevCommentProp, prevPreferredLocale, prevReportId, comment, preferredLocale, report.reportID, updateComment, value, commentRef]); + }, [prevCommentProp, prevPreferredLocale, prevReportId, comment, preferredLocale, reportID, updateComment, value, commentRef]); return null; } diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index febcf3cd3507..a0cdee57f600 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -327,7 +327,7 @@ function ReportActionsList({ report={report} linkedReportActionID={linkedReportActionID} hasOutstandingIOU={hasOutstandingIOU} - sortedReportActions={sortedReportActions} + displayAsGroup={ReportActionsUtils.isConsecutiveActionMadeByPreviousActor(sortedReportActions, index)} mostRecentIOUReportActionID={mostRecentIOUReportActionID} shouldHideThreadDividerLine={shouldHideThreadDividerLine} shouldDisplayNewMarker={shouldDisplayNewMarker(reportAction, index)} diff --git a/src/pages/home/report/ReportActionsListItemRenderer.js b/src/pages/home/report/ReportActionsListItemRenderer.js index 40b9ee9142b7..e8e1f630ceb6 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.js +++ b/src/pages/home/report/ReportActionsListItemRenderer.js @@ -22,9 +22,6 @@ const propTypes = { /** Whether the option has an outstanding IOU */ hasOutstandingIOU: PropTypes.bool, - /** Sorted actions prepared for display */ - sortedReportActions: PropTypes.arrayOf(PropTypes.shape(reportActionPropTypes)).isRequired, - /** The ID of the most recent IOU report action connected with the shown report */ mostRecentIOUReportActionID: PropTypes.string, @@ -36,6 +33,8 @@ const propTypes = { /** Linked report action ID */ linkedReportActionID: PropTypes.string, + + displayAsGroup: PropTypes.bool.isRequired, }; const defaultProps = { @@ -49,7 +48,7 @@ function ReportActionsListItemRenderer({ index, report, hasOutstandingIOU, - sortedReportActions, + displayAsGroup, mostRecentIOUReportActionID, shouldHideThreadDividerLine, shouldDisplayNewMarker, @@ -73,7 +72,7 @@ function ReportActionsListItemRenderer({ report={report} action={reportAction} linkedReportActionID={linkedReportActionID} - displayAsGroup={ReportActionsUtils.isConsecutiveActionMadeByPreviousActor(sortedReportActions, index)} + displayAsGroup={displayAsGroup} shouldDisplayNewMarker={shouldDisplayNewMarker} shouldShowSubscriptAvatar={ (ReportUtils.isPolicyExpenseChat(report) || ReportUtils.isExpenseReport(report)) && diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index a3671faf194c..db9b6e1c3ecf 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -179,6 +179,14 @@ function ReportActionsView(props) { } }; + const report = useMemo( + () => ({ + lastReadTime: props.report.lastReadTime, + reportID: props.report.reportID, + }), + [props.report.lastReadTime, props.report.reportID], + ); + // Comments have not loaded at all yet do nothing if (!_.size(props.reportActions)) { return null; @@ -187,7 +195,7 @@ function ReportActionsView(props) { return ( <> {}, pendingAction: null, personalDetails: {}, - shouldShowComposeInput: true, shouldDisableCompose: false, listHeight: 0, isReportReadyForDisplay: true, + lastReportAction: null, + isEmptyChat: true, }; function ReportFooter(props) { @@ -72,6 +67,33 @@ function ReportFooter(props) { const isSmallSizeLayout = props.windowWidth - (props.isSmallScreenWidth ? 0 : variables.sideBarWidth) < variables.anonymousReportFooterBreakpoint; const hideComposer = ReportUtils.shouldDisableWriteActions(props.report); + const [shouldShowComposeInput, setShouldShowComposeInput] = useState(false); + + useEffect(() => { + // eslint-disable-next-line rulesdir/prefer-onyx-connect-in-libs + const connID = Onyx.connect({ + key: ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, + callback: (val) => { + if (val === shouldShowComposeInput) { + return; + } + setShouldShowComposeInput(val); + }, + }); + + return () => { + Onyx.disconnect(connID); + }; + }, [shouldShowComposeInput]); + + const onSubmitComment = useCallback( + (text) => { + Report.addComment(props.reportID, text); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ); + return ( <> {hideComposer && ( @@ -89,14 +111,14 @@ function ReportFooter(props) { )} )} - {!hideComposer && (props.shouldShowComposeInput || !props.isSmallScreenWidth) && ( + {!hideComposer && (shouldShowComposeInput || !props.isSmallScreenWidth) && ( + isEqual(prevProps.report, nextProps.report) && + isEqual(prevProps.reportActions, nextProps.reportActions) && + isEqual(prevProps.personalDetails, nextProps.personalDetails) && + prevProps.pendingAction === nextProps.pendingAction && + prevProps.shouldDisableCompose === nextProps.shouldDisableCompose && + prevProps.listHeight === nextProps.listHeight && + prevProps.isReportReadyForDisplay === nextProps.isReportReadyForDisplay, + ), +); From 75ad6135c5391135cdd39d1f6e2b23a77825e0eb Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 23 Oct 2023 15:43:42 +0500 Subject: [PATCH 03/50] perf: reduce re-renders --- .../ComposerWithSuggestions.js | 100 +++++++++++------- .../ReportActionCompose.js | 68 ++++-------- 2 files changed, 83 insertions(+), 85 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index e194d0870885..bb5109073cfd 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -1,9 +1,10 @@ -import React, {useEffect, useCallback, useState, useRef, useMemo, useImperativeHandle} from 'react'; +import React, {useEffect, useCallback, useState, useRef, useMemo, useImperativeHandle, memo} from 'react'; import {View, NativeModules, findNodeHandle} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; import lodashGet from 'lodash/get'; import {useIsFocused, useNavigation} from '@react-navigation/native'; +import {runOnJS, useAnimatedRef} from 'react-native-reanimated'; import styles from '../../../../styles/styles'; import themeColors from '../../../../styles/themes/default'; import Composer from '../../../../components/Composer'; @@ -23,7 +24,6 @@ import * as EmojiUtils from '../../../../libs/EmojiUtils'; import * as User from '../../../../libs/actions/User'; import * as ReportUtils from '../../../../libs/ReportUtils'; import * as SuggestionUtils from '../../../../libs/SuggestionUtils'; -import * as ReportActionsUtils from '../../../../libs/ReportActionsUtils'; import canFocusInputOnScreenFocus from '../../../../libs/canFocusInputOnScreenFocus'; import SilentCommentUpdater from './SilentCommentUpdater'; import Suggestions from './Suggestions'; @@ -36,6 +36,10 @@ import focusWithDelay from '../../../../libs/focusWithDelay'; import useDebounce from '../../../../hooks/useDebounce'; import updateMultilineInputRange from '../../../../libs/UpdateMultilineInputRange'; import * as InputFocus from '../../../../libs/actions/InputFocus'; +import SendButton from './SendButton'; +import updatePropsPaperWorklet from '../../../../libs/updatePropsPaperWorklet'; +import EmojiPickerButton from '../../../../components/EmojiPicker/EmojiPickerButton'; +import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities'; const {RNTextInputReset} = NativeModules; @@ -70,14 +74,14 @@ function ComposerWithSuggestions({ // Onyx modal, preferredSkinTone, - parentReportActions, numberOfLines, // HOCs isKeyboardShown, // Props: Report reportID, - report, - reportActions, + includeChronos, + isEmptyChat, + lastReportAction, // Focus onFocus, onBlur, @@ -92,14 +96,13 @@ function ComposerWithSuggestions({ disabled, isFullComposerAvailable, setIsFullComposerAvailable, - setIsCommentEmpty, + isSendDisabled, handleSendMessage, shouldShowComposeInput, measureParentContainer, listHeight, // Refs suggestionsRef, - animatedRef, forwardedRef, isNextModalWillOpenRef, editFocused, @@ -108,19 +111,21 @@ function ComposerWithSuggestions({ const isFocused = useIsFocused(); const navigation = useNavigation(); const emojisPresentBefore = useRef([]); + + const draftComment = getDraftComment(reportID) || ''; + const [isCommentEmpty, setIsCommentEmpty] = useState(() => !draftComment || !!draftComment.match(/^(\s)*$/)); + const animatedRef = useAnimatedRef(); const [value, setValue] = useState(() => { - const draft = getDraftComment(reportID) || ''; - if (draft) { - emojisPresentBefore.current = EmojiUtils.extractEmojis(draft); + if (draftComment) { + emojisPresentBefore.current = EmojiUtils.extractEmojis(draftComment); } - return draft; + return draftComment; }); const commentRef = useRef(value); - const {isSmallScreenWidth} = useWindowDimensions(); + const {isSmallScreenWidth, isMediumScreenWidth} = useWindowDimensions(); const maxComposerLines = isSmallScreenWidth ? CONST.COMPOSER.MAX_LINES_SMALL_SCREEN : CONST.COMPOSER.MAX_LINES; - const isEmptyChat = useMemo(() => _.size(reportActions) === 1, [reportActions]); const shouldAutoFocus = !modal.isVisible && (shouldFocusInputOnScreenFocus || isEmptyChat) && shouldShowComposeInput; const valueRef = useRef(value); @@ -202,6 +207,20 @@ function ComposerWithSuggestions({ [], ); + const sendMessage = useCallback(() => { + 'worklet'; + + if (isCommentEmpty) { + return; + } + + runOnJS(handleSendMessage)(); + const viewTag = animatedRef(); + const viewName = 'RCTMultilineTextInputView'; + const updates = {text: ''}; + updatePropsPaperWorklet(viewTag, viewName, updates); // clears native text input on the UI thread + }, [animatedRef, handleSendMessage, isCommentEmpty]); + /** * Update the value of the comment in Onyx * @@ -225,7 +244,13 @@ function ComposerWithSuggestions({ } } emojisPresentBefore.current = emojis; - 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) { const remainder = ComposerUtils.getCommonSuffixLength(commentValue, newComment); @@ -338,26 +363,20 @@ function ComposerWithSuggestions({ // Submit the form when Enter is pressed if (e.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey && !e.shiftKey) { e.preventDefault(); - handleSendMessage(); + sendMessage(); } // Trigger the edit box for last sent message if ArrowUp is pressed and the comment is empty and Chronos is not in the participants const valueLength = valueRef.current.length; - if (e.key === CONST.KEYBOARD_SHORTCUTS.ARROW_UP.shortcutKey && textInputRef.current.selectionStart === 0 && valueLength === 0 && !ReportUtils.chatIncludesChronos(report)) { + if (e.key === CONST.KEYBOARD_SHORTCUTS.ARROW_UP.shortcutKey && textInputRef.current.selectionStart === 0 && valueLength === 0 && !includeChronos) { e.preventDefault(); - const parentReportActionID = lodashGet(report, 'parentReportActionID', ''); - const parentReportAction = lodashGet(parentReportActions, [parentReportActionID], {}); - const lastReportAction = _.find( - [...reportActions, parentReportAction], - (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action), - ); if (lastReportAction) { Report.saveReportActionDraft(reportID, lastReportAction, _.last(lastReportAction.message).html); } } }, - [isKeyboardShown, isSmallScreenWidth, parentReportActions, report, reportActions, reportID, handleSendMessage, suggestionsRef, valueRef], + [isSmallScreenWidth, isKeyboardShown, suggestionsRef, includeChronos, sendMessage, lastReportAction, reportID], ); const onSelectionChange = useCallback( @@ -561,6 +580,19 @@ function ComposerWithSuggestions({ }} onScroll={hideSuggestionMenu} /> + + {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( + replaceSelectionWithText(...args)} + emojiPickerID={reportID} + /> + )} + `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID}`, - canEvict: false, - initWithStoredValues: false, - }, }), )( - React.forwardRef((props, ref) => ( - - )), + memo( + React.forwardRef((props, ref) => ( + + )), + ), ); diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index dd4d51653546..8796fd1c1b85 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -1,10 +1,10 @@ -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; import PropTypes from 'prop-types'; import {View} from 'react-native'; import _ from 'underscore'; import lodashGet from 'lodash/get'; import {withOnyx} from 'react-native-onyx'; -import {runOnJS, useAnimatedRef} from 'react-native-reanimated'; +import {runOnJS} from 'react-native-reanimated'; import {PortalHost} from '@gorhom/portal'; import styles from '../../../../styles/styles'; import ONYXKEYS from '../../../../ONYXKEYS'; @@ -21,23 +21,17 @@ import ParticipantLocalTime from '../ParticipantLocalTime'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../../components/withCurrentUserPersonalDetails'; import {withNetwork} from '../../../../components/OnyxProvider'; import * as User from '../../../../libs/actions/User'; -import EmojiPickerButton from '../../../../components/EmojiPicker/EmojiPickerButton'; -import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities'; import OfflineIndicator from '../../../../components/OfflineIndicator'; import ExceededCommentLength from '../../../../components/ExceededCommentLength'; import ReportDropUI from '../ReportDropUI'; import reportPropTypes from '../../../reportPropTypes'; import OfflineWithFeedback from '../../../../components/OfflineWithFeedback'; -import SendButton from './SendButton'; import AttachmentPickerWithMenuItems from './AttachmentPickerWithMenuItems'; import ComposerWithSuggestions from './ComposerWithSuggestions'; -import reportActionPropTypes from '../reportActionPropTypes'; import useLocalize from '../../../../hooks/useLocalize'; import getModalState from '../../../../libs/getModalState'; import useWindowDimensions from '../../../../hooks/useWindowDimensions'; import * as EmojiPickerActions from '../../../../libs/actions/EmojiPickerAction'; -import getDraftComment from '../../../../libs/ComposerUtils/getDraftComment'; -import updatePropsPaperWorklet from '../../../../libs/updatePropsPaperWorklet'; const propTypes = { /** A method to call when the form is submitted */ @@ -46,9 +40,6 @@ const propTypes = { /** The ID of the report actions will be created for */ reportID: PropTypes.string.isRequired, - /** Array of report actions for this report */ - reportActions: PropTypes.arrayOf(PropTypes.shape(reportActionPropTypes)), - /** Personal details of all the users */ personalDetails: PropTypes.objectOf(participantPropTypes), @@ -111,14 +102,14 @@ function ReportActionCompose({ personalDetails, report, reportID, - reportActions, + isEmptyChat, + lastReportAction, listHeight, shouldShowComposeInput, isReportReadyForDisplay, }) { const {translate} = useLocalize(); - const {isMediumScreenWidth, isSmallScreenWidth} = useWindowDimensions(); - const animatedRef = useAnimatedRef(); + const {isSmallScreenWidth} = useWindowDimensions(); const actionButtonRef = useRef(null); /** @@ -134,10 +125,6 @@ function ReportActionCompose({ * Updates the should clear state of the composer */ const [textInputShouldClear, setTextInputShouldClear] = useState(false); - const [isCommentEmpty, setIsCommentEmpty] = useState(() => { - const draftComment = getDraftComment(reportID); - return !draftComment || !!draftComment.match(/^(\s)*$/); - }); /** * Updates the visibility state of the menu @@ -164,7 +151,9 @@ function ReportActionCompose({ [personalDetails, report, currentUserPersonalDetails.accountID, isComposerFullSize], ); - const isBlockedFromConcierge = useMemo(() => ReportUtils.chatIncludesConcierge(report) && User.isBlockedFromConcierge(blockedFromConcierge), [report, blockedFromConcierge]); + const includesConcierge = useMemo(() => ReportUtils.chatIncludesConcierge({participantAccountIDs: report.participantAccountIDs}), [report.participantAccountIDs]); + const userBlockedFromConcierge = useMemo(() => User.isBlockedFromConcierge(blockedFromConcierge), [blockedFromConcierge]); + const isBlockedFromConcierge = useMemo(() => includesConcierge && userBlockedFromConcierge, [includesConcierge, userBlockedFromConcierge]); // If we are on a small width device then don't show last 3 items from conciergePlaceholderOptions const conciergePlaceholderRandomIndex = useMemo( @@ -175,8 +164,8 @@ function ReportActionCompose({ // Placeholder to display in the chat input. const inputPlaceholder = useMemo(() => { - if (ReportUtils.chatIncludesConcierge(report)) { - if (User.isBlockedFromConcierge(blockedFromConcierge)) { + if (includesConcierge) { + if (userBlockedFromConcierge) { return translate('reportActionCompose.blockedFromConcierge'); } @@ -184,7 +173,7 @@ function ReportActionCompose({ } return translate('reportActionCompose.writeSomething'); - }, [report, blockedFromConcierge, translate, conciergePlaceholderRandomIndex]); + }, [includesConcierge, translate, userBlockedFromConcierge, conciergePlaceholderRandomIndex]); const focus = () => { if (composerRef === null || composerRef.current === null) { @@ -322,24 +311,16 @@ function ReportActionCompose({ const hasReportRecipient = _.isObject(reportRecipient) && !_.isEmpty(reportRecipient); - const isSendDisabled = isCommentEmpty || isBlockedFromConcierge || disabled || hasExceededMaxCommentLength; + const isSendDisabled = isBlockedFromConcierge || disabled || hasExceededMaxCommentLength; const handleSendMessage = useCallback(() => { - 'worklet'; - if (isSendDisabled || !isReportReadyForDisplay) { return; } - const viewTag = animatedRef(); - const viewName = 'RCTMultilineTextInputView'; - const updates = {text: ''}; - // We are setting the isCommentEmpty flag to true so the status of it will be in sync of the native text input state - runOnJS(setIsCommentEmpty)(true); runOnJS(resetFullComposerSize)(); - updatePropsPaperWorklet(viewTag, viewName, updates); // clears native text input on the UI thread runOnJS(submitForm)(); - }, [isSendDisabled, resetFullComposerSize, submitForm, animatedRef, isReportReadyForDisplay]); + }, [isSendDisabled, resetFullComposerSize, submitForm, isReportReadyForDisplay]); return ( { @@ -427,18 +409,6 @@ function ReportActionCompose({ )} - {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( - composerRef.current.replaceSelectionWithText(...args)} - emojiPickerID={report.reportID} - /> - )} - Date: Tue, 24 Oct 2023 17:04:11 +0500 Subject: [PATCH 04/50] refactor: make action prop lightweight --- .../report/ReportActionsListItemRenderer.js | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionsListItemRenderer.js b/src/pages/home/report/ReportActionsListItemRenderer.js index e8e1f630ceb6..a7ec10a30eea 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.js +++ b/src/pages/home/report/ReportActionsListItemRenderer.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, {memo} from 'react'; +import React, {memo, useMemo} from 'react'; import _ from 'underscore'; import CONST from '../../../CONST'; import * as ReportActionsUtils from '../../../libs/ReportActionsUtils'; @@ -59,6 +59,41 @@ function ReportActionsListItemRenderer({ ReportUtils.isChatThread(report) && !ReportActionsUtils.isTransactionThread(ReportActionsUtils.getParentReportAction(report)); + const action = useMemo( + () => ({ + reportActionID: reportAction.reportActionID, + message: reportAction.message, + pendingAction: reportAction.pendingAction, + actionName: reportAction.actionName, + errors: reportAction.errors, + originalMessage: reportAction.originalMessage, + childCommenterCount: reportAction.childCommenterCount, + linkMetadata: reportAction.linkMetadata, + childReportID: reportAction.childReportID, + childLastVisibleActionCreated: reportAction.childLastVisibleActionCreated, + whisperedToAccountIDs: reportAction.whisperedToAccountIDs, + error: reportAction.error, + created: reportAction.created, + actorAccountID: reportAction.actorAccountID, + }), + [ + reportAction.actionName, + reportAction.childCommenterCount, + reportAction.childLastVisibleActionCreated, + reportAction.childReportID, + reportAction.created, + reportAction.error, + reportAction.errors, + reportAction.linkMetadata, + reportAction.message, + reportAction.originalMessage, + reportAction.pendingAction, + reportAction.reportActionID, + reportAction.whisperedToAccountIDs, + reportAction.actorAccountID, + ], + ); + return shouldDisplayParentAction ? ( Date: Wed, 25 Oct 2023 18:25:16 +0500 Subject: [PATCH 05/50] perf: add Interaction Manager --- src/libs/actions/OnyxUpdates.ts | 29 +++++++++++++++------------ src/libs/actions/PersistedRequests.ts | 19 ++++++++++-------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index 39a20ae9362a..5f13d8133f16 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -1,5 +1,6 @@ import Onyx, {OnyxEntry} from 'react-native-onyx'; import {Merge} from 'type-fest'; +import {InteractionManager} from 'react-native'; import PusherUtils from '../PusherUtils'; import ONYXKEYS from '../../ONYXKEYS'; import * as QueuedOnyxUpdates from './QueuedOnyxUpdates'; @@ -61,19 +62,21 @@ function apply({lastUpdateID, type, request, response, updates}: Merge | undefined { console.debug(`[OnyxUpdateManager] Applying update type: ${type} with lastUpdateID: ${lastUpdateID}`, {request, response, updates}); - if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) { - console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates'); - return Promise.resolve(); - } - if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { - Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID)); - } - if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response) { - return applyHTTPSOnyxUpdates(request, response); - } - if (type === CONST.ONYX_UPDATE_TYPES.PUSHER && updates) { - return applyPusherOnyxUpdates(updates); - } + InteractionManager.runAfterInteractions(() => { + if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) { + console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates'); + return Promise.resolve(); + } + if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { + Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID)); + } + if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response) { + return applyHTTPSOnyxUpdates(request, response); + } + if (type === CONST.ONYX_UPDATE_TYPES.PUSHER && updates) { + return applyPusherOnyxUpdates(updates); + } + }); } /** diff --git a/src/libs/actions/PersistedRequests.ts b/src/libs/actions/PersistedRequests.ts index d9f4ed020109..1e1a147cecd3 100644 --- a/src/libs/actions/PersistedRequests.ts +++ b/src/libs/actions/PersistedRequests.ts @@ -1,5 +1,6 @@ import Onyx from 'react-native-onyx'; import isEqual from 'lodash/isEqual'; +import {InteractionManager} from 'react-native'; import ONYXKEYS from '../../ONYXKEYS'; import {Request} from '../../types/onyx'; @@ -33,14 +34,16 @@ function remove(requestToRemove: Request) { * We only remove the first matching request because the order of requests matters. * If we were to remove all matching requests, we can end up with a final state that is different than what the user intended. */ - const requests = [...persistedRequests]; - const index = requests.findIndex((persistedRequest) => isEqual(persistedRequest, requestToRemove)); - if (index === -1) { - return; - } - requests.splice(index, 1); - persistedRequests = requests; - Onyx.set(ONYXKEYS.PERSISTED_REQUESTS, requests); + InteractionManager.runAfterInteractions(() => { + const requests = [...persistedRequests]; + const index = requests.findIndex((persistedRequest) => isEqual(persistedRequest, requestToRemove)); + if (index === -1) { + return; + } + requests.splice(index, 1); + persistedRequests = requests; + Onyx.set(ONYXKEYS.PERSISTED_REQUESTS, requests); + }); } function update(oldRequestIndex: number, newRequest: Request) { From 60d3034b9c91aaeb31626282f89e61e587416cbd Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 25 Oct 2023 18:30:54 +0500 Subject: [PATCH 06/50] perf: reference bindings of functions --- .../ComposerWithSuggestions.js | 33 ++++++++++++++----- .../composerWithSuggestionsProps.js | 18 ---------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index 4c3136ff6328..97435e510389 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -40,6 +40,7 @@ import SendButton from './SendButton'; import updatePropsPaperWorklet from '../../../../libs/updatePropsPaperWorklet'; import EmojiPickerButton from '../../../../components/EmojiPicker/EmojiPickerButton'; import * as DeviceCapabilities from '../../../../libs/DeviceCapabilities'; +import * as ReportActionsUtils from '../../../../libs/ReportActionsUtils'; const {RNTextInputReset} = NativeModules; @@ -540,6 +541,26 @@ function ComposerWithSuggestions({ [blur, focus, prepareCommentAndResetComposer, replaceSelectionWithText], ); + const onLayout = useCallback( + (e) => { + const composerLayoutHeight = e.nativeEvent.layout.height; + if (composerHeight === composerLayoutHeight) { + return; + } + setComposerHeight(composerLayoutHeight); + }, + [composerHeight], + ); + + const onClear = useCallback(() => { + setTextInputShouldClear(false); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const onChangeText = useCallback((text) => { + updateComment(text, true); + }, []); + return ( <> @@ -551,7 +572,7 @@ function ComposerWithSuggestions({ textAlignVertical="top" placeholder={inputPlaceholder} placeholderTextColor={themeColors.placeholderText} - onChangeText={(commentValue) => updateComment(commentValue, true)} + onChangeText={onChangeText} onKeyPress={triggerHotkeyActions} style={[styles.textInputCompose, isComposerFullSize ? styles.textInputFullCompose : styles.flex4]} maxLines={maxComposerLines} @@ -560,7 +581,7 @@ function ComposerWithSuggestions({ onClick={setShouldBlockSuggestionCalcToFalse} onPasteFile={displayFileInModal} shouldClear={textInputShouldClear} - onClear={() => setTextInputShouldClear(false)} + onClear={onClear} isDisabled={isBlockedFromConcierge || disabled} isReportActionCompose selection={selection} @@ -572,13 +593,7 @@ function ComposerWithSuggestions({ numberOfLines={numberOfLines} onNumberOfLinesChange={updateNumberOfLines} shouldCalculateCaretPosition - onLayout={(e) => { - const composerLayoutHeight = e.nativeEvent.layout.height; - if (composerHeight === composerLayoutHeight) { - return; - } - setComposerHeight(composerLayoutHeight); - }} + onLayout={onLayout} onScroll={hideSuggestionMenu} /> diff --git a/src/pages/home/report/ReportActionCompose/composerWithSuggestionsProps.js b/src/pages/home/report/ReportActionCompose/composerWithSuggestionsProps.js index 017b5aecb4ae..246399d334d6 100644 --- a/src/pages/home/report/ReportActionCompose/composerWithSuggestionsProps.js +++ b/src/pages/home/report/ReportActionCompose/composerWithSuggestionsProps.js @@ -1,5 +1,4 @@ import PropTypes from 'prop-types'; -import reportActionPropTypes from '../reportActionPropTypes'; import CONST from '../../../../CONST'; const propTypes = { @@ -18,20 +17,9 @@ const propTypes = { /** Whether the keyboard is open or not */ isKeyboardShown: PropTypes.bool.isRequired, - /** The actions from the parent report */ - parentReportActions: PropTypes.objectOf(PropTypes.shape(reportActionPropTypes)), - - /** Array of report actions for this report */ - reportActions: PropTypes.arrayOf(PropTypes.shape(reportActionPropTypes)), - /** The ID of the report */ reportID: PropTypes.string.isRequired, - /** The report currently being looked at */ - report: PropTypes.shape({ - parentReportID: PropTypes.string, - }).isRequired, - /** Callback when the input is focused */ onFocus: PropTypes.func.isRequired, @@ -68,9 +56,6 @@ const propTypes = { /** Function to set whether the full composer is available or not */ setIsFullComposerAvailable: PropTypes.func.isRequired, - /** Function to set whether the comment is empty or not */ - setIsCommentEmpty: PropTypes.func.isRequired, - /** A method to call when the form is submitted */ handleSendMessage: PropTypes.func.isRequired, @@ -97,9 +82,6 @@ const propTypes = { }), }).isRequired, - /** Ref for the animated view (text input) */ - animatedRef: PropTypes.func.isRequired, - /** Ref for the composer */ forwardedRef: PropTypes.shape({current: PropTypes.shape({})}), From 851c61f1edc0291d022b3480d22778e97a492ce9 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 26 Oct 2023 15:04:22 +0500 Subject: [PATCH 07/50] perf: optimise rendering --- .../ComposerWithSuggestions.js | 5 ---- .../ReportActionCompose/SuggestionMention.js | 27 +++++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index 97435e510389..c41589fe4b68 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -283,14 +283,9 @@ function ComposerWithSuggestions({ } }, [ - debouncedUpdateFrequentlyUsedEmojis, - preferredLocale, preferredSkinTone, reportID, - setIsCommentEmpty, suggestionsRef, - raiseIsScrollLikelyLayoutTriggered, - debouncedSaveReportComment, ], ); diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js index 67d6ac0632eb..237b1e956966 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js @@ -1,7 +1,7 @@ import React, {useState, useCallback, useRef, useImperativeHandle, useEffect} from 'react'; import PropTypes from 'prop-types'; import _ from 'underscore'; -import {withOnyx} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; import CONST from '../../../../CONST'; import useArrowKeyFocusManager from '../../../../hooks/useArrowKeyFocusManager'; import MentionSuggestions from '../../../../components/MentionSuggestions'; @@ -29,9 +29,6 @@ const defaultSuggestionsValues = { }; const propTypes = { - /** Personal details of all users */ - personalDetails: PropTypes.objectOf(personalDetailsPropType), - /** A ref to this component */ forwardedRef: PropTypes.shape({current: PropTypes.shape({})}), @@ -39,17 +36,28 @@ const propTypes = { }; const defaultProps = { - personalDetails: {}, forwardedRef: null, }; +/** + * We only need the personalDetails once because as long as the + * user is in the ReportScreen, these details won't be changing, + * hence we don't have to use it with `withOnyx`. + */ +let allPersonalDetails = {}; +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (val) => { + allPersonalDetails = val; + }, +}); + function SuggestionMention({ value, setValue, selection, setSelection, isComposerFullSize, - personalDetails, updateComment, composerHeight, forwardedRef, @@ -57,6 +65,7 @@ function SuggestionMention({ measureParentContainer, isComposerFocused, }) { + const personalDetails = allPersonalDetails; const {translate} = useLocalize(); const previousValue = usePrevious(value); const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues); @@ -316,8 +325,4 @@ const SuggestionMentionWithRef = React.forwardRef((props, ref) => ( SuggestionMentionWithRef.displayName = 'SuggestionMentionWithRef'; -export default withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, -})(SuggestionMentionWithRef); +export default SuggestionMentionWithRef; From e613f4278dc5cdfb85cf79670f9f6f575c826918 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 26 Oct 2023 16:28:03 +0500 Subject: [PATCH 08/50] perf: optimise usage of personal detail list --- src/components/AnonymousReportFooter.js | 5 --- src/components/ArchivedReportFooter.js | 13 ++----- src/components/AvatarWithDisplayName.js | 8 +--- src/components/HeaderWithBackButton/index.js | 2 - .../LHNOptionsList/OptionRowLHNData.js | 39 +------------------ src/components/MoneyReportHeader.js | 6 +-- src/components/MoneyRequestHeader.js | 6 +-- .../withCurrentUserPersonalDetails.tsx | 9 +---- src/libs/OptionsListUtils.js | 5 ++- src/libs/PersonalDetailsUtils.js | 27 +++++++++++-- src/libs/ReportUtils.js | 19 +++++---- src/libs/SidebarUtils.js | 10 ++++- src/pages/home/HeaderView.js | 8 +--- src/pages/home/ReportScreen.js | 15 +------ .../ReportActionCompose.js | 17 ++------ src/pages/home/report/ReportActionItem.js | 8 ++-- .../home/report/ReportActionItemCreated.js | 9 +---- .../home/report/ReportActionItemSingle.js | 5 ++- src/pages/home/report/ReportActionsList.js | 11 +----- src/pages/home/report/ReportFooter.js | 6 --- 20 files changed, 72 insertions(+), 156 deletions(-) diff --git a/src/components/AnonymousReportFooter.js b/src/components/AnonymousReportFooter.js index 43933210dc0b..902a31f12ee3 100644 --- a/src/components/AnonymousReportFooter.js +++ b/src/components/AnonymousReportFooter.js @@ -16,16 +16,12 @@ const propTypes = { isSmallSizeLayout: PropTypes.bool, - /** Personal details of all the users */ - personalDetails: PropTypes.objectOf(participantPropTypes), - ...withLocalizePropTypes, }; const defaultProps = { report: {}, isSmallSizeLayout: false, - personalDetails: {}, }; function AnonymousReportFooter(props) { @@ -34,7 +30,6 @@ function AnonymousReportFooter(props) { diff --git a/src/components/ArchivedReportFooter.js b/src/components/ArchivedReportFooter.js index 71d331b68db0..bca5f0b391c8 100644 --- a/src/components/ArchivedReportFooter.js +++ b/src/components/ArchivedReportFooter.js @@ -34,9 +34,6 @@ const propTypes = { /** The archived report */ report: reportPropTypes.isRequired, - /** Personal details of all users */ - personalDetails: PropTypes.objectOf(personalDetailsPropType), - ...withLocalizePropTypes, }; @@ -46,19 +43,18 @@ const defaultProps = { reason: CONST.REPORT.ARCHIVE_REASON.DEFAULT, }, }, - personalDetails: {}, }; function ArchivedReportFooter(props) { const archiveReason = lodashGet(props.reportClosedAction, 'originalMessage.reason', CONST.REPORT.ARCHIVE_REASON.DEFAULT); - let displayName = PersonalDetailsUtils.getDisplayNameOrDefault(props.personalDetails, [props.report.ownerAccountID, 'displayName']); + let displayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [props.report.ownerAccountID, 'displayName']); let oldDisplayName; if (archiveReason === CONST.REPORT.ARCHIVE_REASON.ACCOUNT_MERGED) { const newAccountID = props.reportClosedAction.originalMessage.newAccountID; const oldAccountID = props.reportClosedAction.originalMessage.oldAccountID; - displayName = PersonalDetailsUtils.getDisplayNameOrDefault(props.personalDetails, [newAccountID, 'displayName']); - oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(props.personalDetails, [oldAccountID, 'displayName']); + displayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [newAccountID, 'displayName']); + oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [oldAccountID, 'displayName']); } const shouldRenderHTML = archiveReason !== CONST.REPORT.ARCHIVE_REASON.DEFAULT; @@ -92,9 +88,6 @@ ArchivedReportFooter.displayName = 'ArchivedReportFooter'; export default compose( withLocalize, withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, reportClosedAction: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, canEvict: false, diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 03ae8f51bfb6..3a4ebee0aa42 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -37,9 +37,6 @@ const propTypes = { /** The size of the avatar */ size: PropTypes.oneOf(_.values(CONST.AVATAR_SIZE)), - /** Personal details of all the users */ - personalDetails: PropTypes.objectOf(participantPropTypes), - /** Whether if it's an unauthenticated user */ isAnonymous: PropTypes.bool, @@ -50,7 +47,6 @@ const propTypes = { }; const defaultProps = { - personalDetails: {}, policy: {}, report: {}, isAnonymous: false, @@ -93,8 +89,8 @@ function AvatarWithDisplayName(props) { const subtitle = ReportUtils.getChatRoomSubtitle(props.report); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(props.report); const isMoneyRequestOrReport = ReportUtils.isMoneyRequestReport(props.report) || ReportUtils.isMoneyRequest(props.report); - const icons = ReportUtils.getIcons(props.report, props.personalDetails, props.policy); - const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs([props.report.ownerAccountID], props.personalDetails); + const icons = ReportUtils.getIcons(props.report, null, props.policy); + const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs([props.report.ownerAccountID]); const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(_.values(ownerPersonalDetails), false); const shouldShowSubscriptAvatar = ReportUtils.shouldReportShowSubscript(props.report); const isExpenseRequest = ReportUtils.isExpenseRequest(props.report); diff --git a/src/components/HeaderWithBackButton/index.js b/src/components/HeaderWithBackButton/index.js index 6a02ce02237d..8e5fed15ff7e 100755 --- a/src/components/HeaderWithBackButton/index.js +++ b/src/components/HeaderWithBackButton/index.js @@ -28,7 +28,6 @@ function HeaderWithBackButton({ onThreeDotsButtonPress = () => {}, report = null, policy = {}, - personalDetails = {}, shouldShowAvatarWithDisplay = false, shouldShowBackButton = true, shouldShowBorderBottom = false, @@ -85,7 +84,6 @@ function HeaderWithBackButton({ ) : ( diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js index e93e3690138e..2ddf8a687cfd 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.js +++ b/src/components/LHNOptionsList/OptionRowLHNData.js @@ -9,11 +9,9 @@ import compose from '../../libs/compose'; import ONYXKEYS from '../../ONYXKEYS'; import OptionRowLHN, {propTypes as basePropTypes, defaultProps as baseDefaultProps} from './OptionRowLHN'; import * as Report from '../../libs/actions/Report'; -import * as UserUtils from '../../libs/UserUtils'; import * as ReportActionsUtils from '../../libs/ReportActionsUtils'; import * as TransactionUtils from '../../libs/TransactionUtils'; -import participantPropTypes from '../participantPropTypes'; import CONST from '../../CONST'; import reportActionPropTypes from '../../pages/home/report/reportActionPropTypes'; @@ -21,9 +19,6 @@ const propTypes = { /** Whether row should be focused */ isFocused: PropTypes.bool, - /** List of users' personal details */ - personalDetails: PropTypes.objectOf(participantPropTypes), - /** The preferred language for the app */ preferredLocale: PropTypes.string, @@ -54,7 +49,6 @@ const propTypes = { const defaultProps = { isFocused: false, - personalDetails: {}, fullReport: {}, policy: {}, parentReportActions: {}, @@ -73,7 +67,6 @@ function OptionRowLHNData({ isFocused, fullReport, reportActions, - personalDetails, preferredLocale, comment, policy, @@ -97,7 +90,7 @@ function OptionRowLHNData({ const optionItem = useMemo(() => { // Note: ideally we'd have this as a dependent selector in onyx! - const item = SidebarUtils.getOptionData(fullReport, reportActions, personalDetails, preferredLocale, policy, parentReportAction); + const item = SidebarUtils.getOptionData(fullReport, reportActions, preferredLocale, policy, parentReportAction); if (deepEqual(item, optionItemRef.current)) { return optionItemRef.current; } @@ -106,7 +99,7 @@ function OptionRowLHNData({ // Listen parentReportAction to update title of thread report when parentReportAction changed // Listen to transaction to update title of transaction report when transaction changed // eslint-disable-next-line react-hooks/exhaustive-deps - }, [fullReport, linkedTransaction, reportActions, personalDetails, preferredLocale, policy, parentReportAction, transaction]); + }, [fullReport, linkedTransaction, reportActions, preferredLocale, policy, parentReportAction, transaction]); useEffect(() => { if (!optionItem || optionItem.hasDraftComment || !comment || comment.length <= 0 || isFocused) { @@ -130,30 +123,6 @@ OptionRowLHNData.propTypes = propTypes; OptionRowLHNData.defaultProps = defaultProps; OptionRowLHNData.displayName = 'OptionRowLHNData'; -/** - * @param {Object} [personalDetails] - * @returns {Object|undefined} - */ -const personalDetailsSelector = (personalDetails) => - _.reduce( - personalDetails, - (finalPersonalDetails, personalData, accountID) => { - // It's OK to do param-reassignment in _.reduce() because we absolutely know the starting state of finalPersonalDetails - // eslint-disable-next-line no-param-reassign - finalPersonalDetails[accountID] = { - accountID: Number(accountID), - login: personalData.login, - displayName: personalData.displayName, - firstName: personalData.firstName, - status: personalData.status, - avatar: UserUtils.getAvatar(personalData.avatar, personalData.accountID), - fallbackIcon: personalData.fallbackIcon, - }; - return finalPersonalDetails; - }, - {}, - ); - /** * This component is rendered in a list. * On scroll we want to avoid that a item re-renders @@ -174,10 +143,6 @@ export default React.memo( key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, canEvict: false, }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - selector: personalDetailsSelector, - }, preferredLocale: { key: ONYXKEYS.NVP_PREFERRED_LOCALE, }, diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index ab0b77c21653..211acaf033ce 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -46,9 +46,6 @@ const propTypes = { /** The next step for the report */ nextStep: nextStepPropTypes, - /** Personal details so we can get the ones for the report participants */ - personalDetails: PropTypes.objectOf(participantPropTypes).isRequired, - /** Session info for the currently logged in user. */ session: PropTypes.shape({ /** Currently logged in user email */ @@ -67,7 +64,7 @@ const defaultProps = { policy: {}, }; -function MoneyReportHeader({session, personalDetails, policy, chatReport, nextStep, report: moneyRequestReport, isSmallScreenWidth}) { +function MoneyReportHeader({session, policy, chatReport, nextStep, report: moneyRequestReport, isSmallScreenWidth}) { const {translate} = useLocalize(); const reimbursableTotal = ReportUtils.getMoneyRequestReimbursableTotal(moneyRequestReport); const isApproved = ReportUtils.isReportApproved(moneyRequestReport); @@ -102,7 +99,6 @@ function MoneyReportHeader({session, personalDetails, policy, chatReport, nextSt shouldShowPinButton={false} report={moneyRequestReport} policy={policy} - personalDetails={personalDetails} shouldShowBackButton={isSmallScreenWidth} onBackButtonPress={() => Navigation.goBack(ROUTES.HOME, false, true)} // Shows border if no buttons or next steps are showing below the header diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index 086e1429baef..d9be47ad312f 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -35,9 +35,6 @@ const propTypes = { name: PropTypes.string, }), - /** Personal details so we can get the ones for the report participants */ - personalDetails: PropTypes.objectOf(participantPropTypes).isRequired, - /* Onyx Props */ /** Session info for the currently logged in user. */ session: PropTypes.shape({ @@ -65,7 +62,7 @@ const defaultProps = { policy: {}, }; -function MoneyRequestHeader({session, parentReport, report, parentReportAction, transaction, policy, personalDetails}) { +function MoneyRequestHeader({session, parentReport, report, parentReportAction, transaction, policy}) { const {translate} = useLocalize(); const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); const moneyRequestReport = parentReport; @@ -125,7 +122,6 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, ownerEmail: lodashGet(parentReport, 'ownerEmail', null), }} policy={policy} - personalDetails={personalDetails} shouldShowBackButton={isSmallScreenWidth} onBackButtonPress={() => Navigation.goBack(ROUTES.HOME, false, true)} /> diff --git a/src/components/withCurrentUserPersonalDetails.tsx b/src/components/withCurrentUserPersonalDetails.tsx index e1472f280f17..fa81c658bc78 100644 --- a/src/components/withCurrentUserPersonalDetails.tsx +++ b/src/components/withCurrentUserPersonalDetails.tsx @@ -4,13 +4,11 @@ import getComponentDisplayName from '../libs/getComponentDisplayName'; import ONYXKEYS from '../ONYXKEYS'; import personalDetailsPropType from '../pages/personalDetailsPropType'; import type {PersonalDetails, Session} from '../types/onyx'; +import { getPersonalDetailsByAccountID } from '../libs/PersonalDetailsUtils'; type CurrentUserPersonalDetails = PersonalDetails | Record; type OnyxProps = { - /** Personal details of all the users, including current user */ - personalDetails: OnyxEntry>; - /** Session of the current user */ session: OnyxEntry; }; @@ -35,7 +33,7 @@ export default function ( ): ComponentType & RefAttributes, keyof OnyxProps>> { function WithCurrentUserPersonalDetails(props: Omit, ref: ForwardedRef) { const accountID = props.session?.accountID ?? 0; - const accountPersonalDetails = props.personalDetails?.[accountID]; + const accountPersonalDetails: PersonalDetails = getPersonalDetailsByAccountID(accountID); const currentUserPersonalDetails: CurrentUserPersonalDetails = useMemo( () => (accountPersonalDetails ? {...accountPersonalDetails, accountID} : {}), [accountPersonalDetails, accountID], @@ -55,9 +53,6 @@ export default function ( const withCurrentUserPersonalDetails = React.forwardRef(WithCurrentUserPersonalDetails); return withOnyx & RefAttributes, OnyxProps>({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, session: { key: ONYXKEYS.SESSION, }, diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 79c480711c4d..4948c4e8ef90 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -152,11 +152,12 @@ function getAvatarsForAccountIDs(accountIDs, personalDetails, defaultValues = {} * Returns the personal details for an array of accountIDs * * @param {Array} accountIDs - * @param {Object} personalDetails + * @param {Object} passedPersonalDetails * @returns {Object} – keys of the object are emails, values are PersonalDetails objects. */ -function getPersonalDetailsForAccountIDs(accountIDs, personalDetails) { +function getPersonalDetailsForAccountIDs(accountIDs, passedPersonalDetails) { const personalDetailsForAccountIDs = {}; + const personalDetails = passedPersonalDetails || allPersonalDetails; if (!personalDetails) { return personalDetailsForAccountIDs; } diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 29c49427bc81..78066c6b1d9d 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -23,8 +23,7 @@ Onyx.connect({ * @returns {String} */ function getDisplayNameOrDefault(passedPersonalDetails, pathToDisplayName, defaultValue) { - const displayName = lodashGet(passedPersonalDetails, pathToDisplayName); - + const displayName = lodashGet(passedPersonalDetails || allPersonalDetails, pathToDisplayName); return displayName || defaultValue || Localize.translateLocal('common.hidden'); } @@ -177,4 +176,26 @@ function getFormattedAddress(privatePersonalDetails) { return formattedAddress.trim().replace(/,$/, ''); } -export {getDisplayNameOrDefault, getPersonalDetailsByIDs, getAccountIDsByLogins, getLoginsByAccountIDs, getNewPersonalDetailsOnyxData, getFormattedAddress}; +function getPersonalDetailsByAccountID(accountID) { + return allPersonalDetails[accountID]; +} + +function getWhisperedToPersonalDetails(whisperedToAccountIDs) { + return _.filter(allPersonalDetails, (details) => _.includes(whisperedToAccountIDs, details.accountID)); +} + +function isPersonalDetailsEmpty() { + return !personalDetails.length; +} + +export { + getDisplayNameOrDefault, + getPersonalDetailsByIDs, + getAccountIDsByLogins, + getLoginsByAccountIDs, + getNewPersonalDetailsOnyxData, + getFormattedAddress, + getPersonalDetailsByAccountID, + getWhisperedToPersonalDetails, + isPersonalDetailsEmpty, +}; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 79c1c500b837..74d744efb837 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -913,15 +913,13 @@ function getReportRecipientAccountIDs(report, currentLoginAccountID) { /** * Whether the time row should be shown for a report. - * @param {Array} personalDetails * @param {Object} report - * @param {Number} accountID * @return {Boolean} */ -function canShowReportRecipientLocalTime(personalDetails, report, accountID) { - const reportRecipientAccountIDs = getReportRecipientAccountIDs(report, accountID); +function canShowReportRecipientLocalTime(report) { + const reportRecipientAccountIDs = getReportRecipientAccountIDs(report, currentUserAccountID); const hasMultipleParticipants = reportRecipientAccountIDs.length > 1; - const reportRecipient = personalDetails[reportRecipientAccountIDs[0]]; + const reportRecipient = allPersonalDetails[reportRecipientAccountIDs[0]]; const reportRecipientTimezone = lodashGet(reportRecipient, 'timezone', CONST.DEFAULT_TIME_ZONE); const isReportParticipantValidated = lodashGet(reportRecipient, 'validated', false); return Boolean( @@ -977,13 +975,13 @@ function getWorkspaceAvatar(report) { * The Avatar sources can be URLs or Icon components according to the chat type. * * @param {Array} participants - * @param {Object} personalDetails + * @param {Object} passedPersonalDetails * @returns {Array<*>} */ -function getIconsForParticipants(participants, personalDetails) { +function getIconsForParticipants(participants, passedPersonalDetails) { const participantDetails = []; const participantsList = participants || []; - + const personalDetails = passedPersonalDetails || allPersonalDetails; for (let i = 0; i < participantsList.length; i++) { const accountID = participantsList[i]; const avatarSource = UserUtils.getAvatar(lodashGet(personalDetails, [accountID, 'avatar'], ''), accountID); @@ -1046,14 +1044,15 @@ function getWorkspaceIcon(report, policy = undefined) { * The Avatar sources can be URLs or Icon components according to the chat type. * * @param {Object} report - * @param {Object} personalDetails + * @param {Object} passedPersonalDetails * @param {*} [defaultIcon] * @param {String} [defaultName] * @param {Number} [defaultAccountID] * @param {Object} [policy] * @returns {Array<*>} */ -function getIcons(report, personalDetails, defaultIcon = null, defaultName = '', defaultAccountID = -1, policy = undefined) { +function getIcons(report, passedPersonalDetails, defaultIcon = null, defaultName = '', defaultAccountID = -1, policy = undefined) { + const personalDetails = passedPersonalDetails || allPersonalDetails; if (_.isEmpty(report)) { const fallbackIcon = { source: defaultIcon || Expensicons.FallbackAvatar, diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.js index 6b9e6d10fd16..5d7dd5bc1ed7 100644 --- a/src/libs/SidebarUtils.js +++ b/src/libs/SidebarUtils.js @@ -55,6 +55,12 @@ Onyx.connect({ }, }); +let allPersonalDetails; +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (val) => (allPersonalDetails = val), +}); + let resolveSidebarIsReadyPromise; let sidebarIsReadyPromise = new Promise((resolve) => { @@ -218,16 +224,16 @@ function getOrderedReportIDs(currentReportId, allReportsDict, betas, policies, p * * @param {Object} report * @param {Object} reportActions - * @param {Object} personalDetails * @param {String} preferredLocale * @param {Object} [policy] * @param {Object} parentReportAction * @returns {Object} */ -function getOptionData(report, reportActions, personalDetails, preferredLocale, policy, parentReportAction) { +function getOptionData(report, reportActions, preferredLocale, policy, parentReportAction) { // When a user signs out, Onyx is cleared. Due to the lazy rendering with a virtual list, it's possible for // this method to be called after the Onyx data has been cleared out. In that case, it's fine to do // a null check here and return early. + const personalDetails = allPersonalDetails; if (!report || !personalDetails) { return; } diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index e88f6cd0b756..d36937b0f420 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -43,9 +43,6 @@ const propTypes = { /** The report currently being looked at */ report: reportPropTypes, - /** Personal details of all the users */ - personalDetails: PropTypes.objectOf(participantPropTypes), - /** Onyx Props */ parentReport: reportPropTypes, @@ -62,7 +59,6 @@ const propTypes = { }; const defaultProps = { - personalDetails: {}, report: null, guideCalendarLink: null, parentReport: {}, @@ -73,7 +69,7 @@ const defaultProps = { function HeaderView(props) { const participants = lodashGet(props.report, 'participantAccountIDs', []); - const participantPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants, props.personalDetails); + const participantPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants); const isMultipleParticipant = participants.length > 1; const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(participantPersonalDetails, isMultipleParticipant); const isChatThread = ReportUtils.isChatThread(props.report); @@ -167,7 +163,7 @@ function HeaderView(props) { const shouldShowSubscript = ReportUtils.shouldReportShowSubscript(props.report); const defaultSubscriptSize = ReportUtils.isExpenseRequest(props.report) ? CONST.AVATAR_SIZE.SMALL_NORMAL : CONST.AVATAR_SIZE.DEFAULT; - const icons = ReportUtils.getIcons(reportHeaderData, props.personalDetails); + const icons = ReportUtils.getIcons(reportHeaderData); const brickRoadIndicator = ReportUtils.hasReportNameError(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; const shouldShowBorderBottom = !isTaskReport || !props.isSmallScreenWidth; const shouldDisableDetailPage = ReportUtils.shouldDisableDetailPage(props.report); diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 64641ab473fb..d6caff573d25 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -40,6 +40,7 @@ import usePrevious from '../../hooks/usePrevious'; import CONST from '../../CONST'; import withCurrentReportID, {withCurrentReportIDPropTypes, withCurrentReportIDDefaultProps} from '../../components/withCurrentReportID'; import reportWithoutHasDraftSelector from '../../libs/OnyxSelectors/reportWithoutHasDraftSelector'; +import { isPersonalDetailsEmpty } from '../../libs/PersonalDetailsUtils'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -86,9 +87,6 @@ const propTypes = { /** The account manager report ID */ accountManagerReportID: PropTypes.string, - /** All of the personal details for everyone */ - personalDetails: PropTypes.objectOf(personalDetailsPropType), - /** Onyx function that marks the component ready for hydration */ markReadyForHydration: PropTypes.func, @@ -115,7 +113,6 @@ const defaultProps = { policies: {}, accountManagerReportID: null, userLeavingStatus: false, - personalDetails: {}, markReadyForHydration: null, ...withCurrentReportIDDefaultProps, }; @@ -140,7 +137,6 @@ function ReportScreen({ reportMetadata, reportActions, accountManagerReportID, - personalDetails, markReadyForHydration, policies, isSidebarLoaded, @@ -173,7 +169,7 @@ function ReportScreen({ const shouldHideReport = !ReportUtils.canAccessReport(report, policies, betas); - const isLoading = !reportID || !isSidebarLoaded || _.isEmpty(personalDetails); + const isLoading = !reportID || !isSidebarLoaded || isPersonalDetailsEmpty(); const parentReportAction = ReportActionsUtils.getParentReportAction(report); const lastReportAction = useMemo( @@ -191,7 +187,6 @@ function ReportScreen({ Navigation.goBack(ROUTES.HOME, false, true)} - personalDetails={personalDetails} report={report} /> ); @@ -201,7 +196,6 @@ function ReportScreen({ @@ -213,7 +207,6 @@ function ReportScreen({ @@ -445,7 +438,6 @@ function ReportScreen({ isComposerFullSize={isComposerFullSize} policies={policies} listHeight={listHeight} - personalDetails={personalDetails} isEmptyChat={isEmptyChat} lastReportAction={lastReportAction} /> @@ -506,9 +498,6 @@ export default compose( key: ONYXKEYS.ACCOUNT_MANAGER_REPORT_ID, initialValue: null, }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, userLeavingStatus: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${getReportID(route)}`, initialValue: false, diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index 2721329919c7..dc515c2495ff 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -16,7 +16,6 @@ import willBlurTextInputOnTapOutsideFunc from '../../../../libs/willBlurTextInpu import canFocusInputOnScreenFocus from '../../../../libs/canFocusInputOnScreenFocus'; import CONST from '../../../../CONST'; import * as ReportUtils from '../../../../libs/ReportUtils'; -import participantPropTypes from '../../../../components/participantPropTypes'; import ParticipantLocalTime from '../ParticipantLocalTime'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../../components/withCurrentUserPersonalDetails'; import {withNetwork} from '../../../../components/OnyxProvider'; @@ -33,6 +32,7 @@ import getModalState from '../../../../libs/getModalState'; import useWindowDimensions from '../../../../hooks/useWindowDimensions'; import * as EmojiPickerActions from '../../../../libs/actions/EmojiPickerAction'; import * as ReportActionsUtils from '../../../../libs/ReportActionsUtils'; +import {getPersonalDetailsByAccountID} from '../../../../libs/PersonalDetailsUtils'; const propTypes = { /** A method to call when the form is submitted */ @@ -41,9 +41,6 @@ const propTypes = { /** The ID of the report actions will be created for */ reportID: PropTypes.string.isRequired, - /** Personal details of all the users */ - personalDetails: PropTypes.objectOf(participantPropTypes), - /** The report currently being looked at */ report: reportPropTypes, @@ -76,7 +73,6 @@ const propTypes = { const defaultProps = { report: {}, blockedFromConcierge: {}, - personalDetails: {}, preferredSkinTone: CONST.EMOJI_DEFAULT_SKIN_TONE, isComposerFullSize: false, pendingAction: null, @@ -100,7 +96,6 @@ function ReportActionCompose({ network, onSubmit, pendingAction, - personalDetails, report, reportID, isEmptyChat, @@ -147,10 +142,7 @@ function ReportActionCompose({ [currentUserPersonalDetails.accountID, report], ); - const shouldShowReportRecipientLocalTime = useMemo( - () => ReportUtils.canShowReportRecipientLocalTime(personalDetails, report, currentUserPersonalDetails.accountID) && !isComposerFullSize, - [personalDetails, report, currentUserPersonalDetails.accountID, isComposerFullSize], - ); + const shouldShowReportRecipientLocalTime = useMemo(() => ReportUtils.canShowReportRecipientLocalTime(report) && !isComposerFullSize, [report, isComposerFullSize]); const includesConcierge = useMemo(() => ReportUtils.chatIncludesConcierge({participantAccountIDs: report.participantAccountIDs}), [report.participantAccountIDs]); const userBlockedFromConcierge = useMemo(() => User.isBlockedFromConcierge(blockedFromConcierge), [blockedFromConcierge]); @@ -307,7 +299,7 @@ function ReportActionCompose({ ); const reportRecipientAcountIDs = ReportUtils.getReportRecipientAccountIDs(report, currentUserPersonalDetails.accountID); - const reportRecipient = personalDetails[reportRecipientAcountIDs[0]]; + const reportRecipient = getPersonalDetailsByAccountID(reportRecipientAcountIDs[0]); const shouldUseFocusedColor = !isBlockedFromConcierge && !disabled && isFocused; const hasReportRecipient = _.isObject(reportRecipient) && !_.isEmpty(reportRecipient); @@ -443,9 +435,6 @@ export default compose( blockedFromConcierge: { key: ONYXKEYS.NVP_BLOCKED_FROM_CONCIERGE, }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, shouldShowComposeInput: { key: ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, }, diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 3afdb437a49a..2697d4b5551d 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -133,7 +133,6 @@ const defaultProps = { }; function ReportActionItem(props) { - const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const [isContextMenuActive, setIsContextMenuActive] = useState(() => ReportActionContextMenu.isActiveReportAction(props.action.reportActionID)); const [isHidden, setIsHidden] = useState(false); const [moderationDecision, setModerationDecision] = useState(CONST.MODERATION.MODERATOR_DECISION_APPROVED); @@ -303,6 +302,7 @@ function ReportActionItem(props) { */ const renderItemContent = (hovered = false, isWhisper = false, hasErrors = false) => { let children; + const _submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [props.report.ownerAccountID, 'displayName'], props.report.ownerEmail); // Show the MoneyRequestPreview for when request was created, bill was split or money was sent if ( @@ -360,7 +360,7 @@ function ReportActionItem(props) { /> ); } else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED) { - const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [props.report.ownerAccountID, 'displayName'], props.report.ownerEmail); + const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [props.report.ownerAccountID, 'displayName'], props.report.ownerEmail); const paymentType = lodashGet(props.action, 'originalMessage.paymentType', ''); const isSubmitterOfUnsettledReport = ReportUtils.isCurrentUserSubmitter(props.report.reportID) && !ReportUtils.isSettled(props.report.reportID); @@ -506,7 +506,7 @@ function ReportActionItem(props) { numberOfReplies={numberOfThreadReplies} mostRecentReply={`${props.action.childLastVisibleActionCreated}`} isHovered={hovered} - icons={ReportUtils.getIconsForParticipants(oldestFourAccountIDs, personalDetails)} + icons={ReportUtils.getIconsForParticipants(oldestFourAccountIDs)} onSecondaryInteraction={showPopover} /> @@ -638,7 +638,7 @@ function ReportActionItem(props) { const isWhisper = whisperedToAccountIDs.length > 0; const isMultipleParticipant = whisperedToAccountIDs.length > 1; const isWhisperOnlyVisibleByUser = isWhisper && ReportUtils.isCurrentUserTheOnlyParticipant(whisperedToAccountIDs); - const whisperedToPersonalDetails = isWhisper ? _.filter(personalDetails, (details) => _.includes(whisperedToAccountIDs, details.accountID)) : []; + const whisperedToPersonalDetails = isWhisper ? PersonalDetailsUtils.getWhisperedToPersonalDetails(whisperedToAccountIDs) : []; const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedToPersonalDetails, isMultipleParticipant) : []; return ( `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, selector: reportWithoutHasDraftSelector, }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, policy: { key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index fc189a3aef36..faa9b4c6fbbc 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -31,6 +31,7 @@ import ONYXKEYS from '../../../ONYXKEYS'; import Text from '../../../components/Text'; import Tooltip from '../../../components/Tooltip'; import DateUtils from '../../../libs/DateUtils'; +import { getPersonalDetailsByAccountID } from '../../../libs/PersonalDetailsUtils'; const propTypes = { /** All the data of the action */ @@ -83,8 +84,8 @@ const showWorkspaceDetails = (reportID) => { }; function ReportActionItemSingle(props) { - const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const actorAccountID = props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport ? props.iouReport.managerID : props.action.actorAccountID; + const personalDetails = getPersonalDetailsByAccountID(actorAccountID); let {displayName} = personalDetails[actorAccountID] || {}; const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID] || {}; let actorHint = (login || displayName || '').replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); @@ -122,7 +123,7 @@ function ReportActionItemSingle(props) { id: secondaryAccountId, }; } else if (!isWorkspaceActor) { - secondaryAvatar = ReportUtils.getIcons(props.report, {})[props.report.isOwnPolicyExpenseChat ? 0 : 1]; + secondaryAvatar = ReportUtils.getIcons(props.report)[props.report.isOwnPolicyExpenseChat ? 0 : 1]; } const icon = {source: avatarSource, type: isWorkspaceActor ? CONST.ICON_TYPE_WORKSPACE : CONST.ICON_TYPE_AVATAR, name: primaryDisplayName, id: isWorkspaceActor ? '' : actorAccountID}; diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 531a11ce7252..6515dc652bdc 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -6,8 +6,6 @@ import {useRoute} from '@react-navigation/native'; import lodashGet from 'lodash/get'; import CONST from '../../../CONST'; import InvertedFlatList from '../../../components/InvertedFlatList'; -import {withPersonalDetails} from '../../../components/OnyxProvider'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '../../../components/withCurrentUserPersonalDetails'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; import useNetwork from '../../../hooks/useNetwork'; import useLocalize from '../../../hooks/useLocalize'; @@ -66,17 +64,14 @@ const propTypes = { }), ...windowDimensionsPropTypes, - ...withCurrentUserPersonalDetailsPropTypes, }; const defaultProps = { - personalDetails: {}, onScroll: () => {}, mostRecentIOUReportActionID: '', isLoadingInitialReportActions: false, isLoadingOlderReportActions: false, isLoadingNewerReportActions: false, - ...withCurrentUserPersonalDetailsDefaultProps, }; const VERTICAL_OFFSET_THRESHOLD = 200; @@ -122,8 +117,6 @@ function ReportActionsList({ onScroll, mostRecentIOUReportActionID, isSmallScreenWidth, - personalDetailsList, - currentUserPersonalDetails, hasOutstandingIOU, loadNewerChats, loadOlderChats, @@ -350,7 +343,7 @@ function ReportActionsList({ // To notify there something changes we can use extraData prop to flatlist const extraData = [isSmallScreenWidth ? currentUnreadMarker : undefined, ReportUtils.isArchivedRoom(report)]; const hideComposer = ReportUtils.shouldDisableWriteActions(report); - const shouldShowReportRecipientLocalTime = ReportUtils.canShowReportRecipientLocalTime(personalDetailsList, report, currentUserPersonalDetails.accountID) && !isComposerFullSize; + const shouldShowReportRecipientLocalTime = ReportUtils.canShowReportRecipientLocalTime(report) && !isComposerFullSize; const contentContainerStyle = useMemo( () => [styles.chatContentScrollView, isLoadingNewerReportActions ? styles.chatContentScrollViewWithHeaderLoader : {}], @@ -435,4 +428,4 @@ ReportActionsList.propTypes = propTypes; ReportActionsList.defaultProps = defaultProps; ReportActionsList.displayName = 'ReportActionsList'; -export default compose(withWindowDimensions, withPersonalDetails(), withCurrentUserPersonalDetails)(ReportActionsList); +export default compose(withWindowDimensions)(ReportActionsList); diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index 5af46b14142d..710eed2b86df 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -33,9 +33,6 @@ const propTypes = { /** The pending action when we are adding a chat */ pendingAction: PropTypes.string, - /** Personal details of all the users */ - personalDetails: PropTypes.objectOf(participantPropTypes), - /** Whether user interactions should be disabled */ shouldDisableCompose: PropTypes.bool, @@ -51,7 +48,6 @@ const propTypes = { const defaultProps = { report: {reportID: '0'}, pendingAction: null, - personalDetails: {}, shouldDisableCompose: false, listHeight: 0, isReportReadyForDisplay: true, @@ -111,7 +107,6 @@ function ReportFooter(props) { )} {isArchivedRoom && } @@ -151,7 +146,6 @@ export default withWindowDimensions( (prevProps, nextProps) => isEqual(prevProps.report, nextProps.report) && isEqual(prevProps.reportActions, nextProps.reportActions) && - isEqual(prevProps.personalDetails, nextProps.personalDetails) && prevProps.pendingAction === nextProps.pendingAction && prevProps.shouldDisableCompose === nextProps.shouldDisableCompose && prevProps.listHeight === nextProps.listHeight && From 47c4717c3ee8553a96b1bb8540df9d5779097f5f Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 26 Oct 2023 16:54:08 +0500 Subject: [PATCH 09/50] fix: linting --- src/components/AnonymousReportFooter.js | 1 - src/components/ArchivedReportFooter.js | 1 - src/components/AvatarWithDisplayName.js | 1 - src/components/MoneyReportHeader.js | 1 - src/components/MoneyRequestHeader.js | 1 - src/pages/home/HeaderView.js | 1 - src/pages/home/ReportScreen.js | 5 ++--- .../ReportActionCompose/ReportActionCompose.js | 4 ++-- .../ReportActionCompose/SuggestionMention.js | 2 +- src/pages/home/report/ReportActionItem.js | 3 +-- src/pages/home/report/ReportActionItemCreated.js | 1 - src/pages/home/report/ReportActionItemSingle.js | 15 +++++++-------- src/pages/home/report/ReportActionsList.js | 4 ++-- src/pages/home/report/ReportFooter.js | 1 - 14 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/components/AnonymousReportFooter.js b/src/components/AnonymousReportFooter.js index 902a31f12ee3..51b9752294e9 100644 --- a/src/components/AnonymousReportFooter.js +++ b/src/components/AnonymousReportFooter.js @@ -8,7 +8,6 @@ import withLocalize, {withLocalizePropTypes} from './withLocalize'; import reportPropTypes from '../pages/reportPropTypes'; import styles from '../styles/styles'; import * as Session from '../libs/actions/Session'; -import participantPropTypes from './participantPropTypes'; const propTypes = { /** The report currently being looked at */ diff --git a/src/components/ArchivedReportFooter.js b/src/components/ArchivedReportFooter.js index bca5f0b391c8..41cf0f65487e 100644 --- a/src/components/ArchivedReportFooter.js +++ b/src/components/ArchivedReportFooter.js @@ -7,7 +7,6 @@ import CONST from '../CONST'; import Banner from './Banner'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import compose from '../libs/compose'; -import personalDetailsPropType from '../pages/personalDetailsPropType'; import ONYXKEYS from '../ONYXKEYS'; import * as ReportUtils from '../libs/ReportUtils'; import reportPropTypes from '../pages/reportPropTypes'; diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 3a4ebee0aa42..8623f6bba4bc 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -5,7 +5,6 @@ import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; import CONST from '../CONST'; import reportPropTypes from '../pages/reportPropTypes'; -import participantPropTypes from './participantPropTypes'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; import styles from '../styles/styles'; diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index 211acaf033ce..55a31c0a9bdc 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -8,7 +8,6 @@ import useLocalize from '../hooks/useLocalize'; import HeaderWithBackButton from './HeaderWithBackButton'; import iouReportPropTypes from '../pages/iouReportPropTypes'; import * as ReportUtils from '../libs/ReportUtils'; -import participantPropTypes from './participantPropTypes'; import styles from '../styles/styles'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; import compose from '../libs/compose'; diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index d9be47ad312f..c106e1aa9bfd 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -8,7 +8,6 @@ import iouReportPropTypes from '../pages/iouReportPropTypes'; import * as ReportUtils from '../libs/ReportUtils'; import compose from '../libs/compose'; import * as Expensicons from './Icon/Expensicons'; -import participantPropTypes from './participantPropTypes'; import styles from '../styles/styles'; import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index d36937b0f420..3f9776ca4946 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -19,7 +19,6 @@ import TaskHeaderActionButton from '../../components/TaskHeaderActionButton'; import Text from '../../components/Text'; import ThreeDotsMenu from '../../components/ThreeDotsMenu'; import Tooltip from '../../components/Tooltip'; -import participantPropTypes from '../../components/participantPropTypes'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '../../components/withWindowDimensions'; import * as OptionsListUtils from '../../libs/OptionsListUtils'; diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index d6caff573d25..3340c4ca5ee3 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -28,7 +28,6 @@ import reportMetadataPropTypes from '../reportMetadataPropTypes'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import withViewportOffsetTop from '../../components/withViewportOffsetTop'; import * as ReportActionsUtils from '../../libs/ReportActionsUtils'; -import personalDetailsPropType from '../personalDetailsPropType'; import getIsReportFullyVisible from '../../libs/getIsReportFullyVisible'; import MoneyRequestHeader from '../../components/MoneyRequestHeader'; import MoneyReportHeader from '../../components/MoneyReportHeader'; @@ -40,7 +39,7 @@ import usePrevious from '../../hooks/usePrevious'; import CONST from '../../CONST'; import withCurrentReportID, {withCurrentReportIDPropTypes, withCurrentReportIDDefaultProps} from '../../components/withCurrentReportID'; import reportWithoutHasDraftSelector from '../../libs/OnyxSelectors/reportWithoutHasDraftSelector'; -import { isPersonalDetailsEmpty } from '../../libs/PersonalDetailsUtils'; +import * as PersonalDetailsUtils from '../../libs/PersonalDetailsUtils'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -169,7 +168,7 @@ function ReportScreen({ const shouldHideReport = !ReportUtils.canAccessReport(report, policies, betas); - const isLoading = !reportID || !isSidebarLoaded || isPersonalDetailsEmpty(); + const isLoading = !reportID || !isSidebarLoaded || PersonalDetailsUtils.isPersonalDetailsEmpty(); const parentReportAction = ReportActionsUtils.getParentReportAction(report); const lastReportAction = useMemo( diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index dc515c2495ff..fa7dfc8418fa 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -32,7 +32,7 @@ import getModalState from '../../../../libs/getModalState'; import useWindowDimensions from '../../../../hooks/useWindowDimensions'; import * as EmojiPickerActions from '../../../../libs/actions/EmojiPickerAction'; import * as ReportActionsUtils from '../../../../libs/ReportActionsUtils'; -import {getPersonalDetailsByAccountID} from '../../../../libs/PersonalDetailsUtils'; +import * as PersonalDetailsUtils from '../../../../libs/PersonalDetailsUtils'; const propTypes = { /** A method to call when the form is submitted */ @@ -299,7 +299,7 @@ function ReportActionCompose({ ); const reportRecipientAcountIDs = ReportUtils.getReportRecipientAccountIDs(report, currentUserPersonalDetails.accountID); - const reportRecipient = getPersonalDetailsByAccountID(reportRecipientAcountIDs[0]); + const reportRecipient = PersonalDetailsUtils.getPersonalDetailsByAccountID(reportRecipientAcountIDs[0]); const shouldUseFocusedColor = !isBlockedFromConcierge && !disabled && isFocused; const hasReportRecipient = _.isObject(reportRecipient) && !_.isEmpty(reportRecipient); diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js index 237b1e956966..0172bfe20038 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js @@ -11,7 +11,6 @@ import * as SuggestionsUtils from '../../../../libs/SuggestionUtils'; import useLocalize from '../../../../hooks/useLocalize'; import usePrevious from '../../../../hooks/usePrevious'; import ONYXKEYS from '../../../../ONYXKEYS'; -import personalDetailsPropType from '../../../personalDetailsPropType'; import * as SuggestionProps from './suggestionProps'; /** @@ -45,6 +44,7 @@ const defaultProps = { * hence we don't have to use it with `withOnyx`. */ let allPersonalDetails = {}; +// eslint-disable-next-line rulesdir/prefer-onyx-connect-in-libs Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => { diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 2697d4b5551d..7c955b0a88e3 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -28,7 +28,7 @@ import MiniReportActionContextMenu from './ContextMenu/MiniReportActionContextMe import * as ReportActionContextMenu from './ContextMenu/ReportActionContextMenu'; import * as ContextMenuActions from './ContextMenu/ContextMenuActions'; import * as EmojiPickerAction from '../../../libs/actions/EmojiPickerAction'; -import {usePersonalDetails, withBlockedFromConcierge, withNetwork, withReportActionsDrafts} from '../../../components/OnyxProvider'; +import {withBlockedFromConcierge, withNetwork, withReportActionsDrafts} from '../../../components/OnyxProvider'; import RenameAction from '../../../components/ReportActionItem/RenameAction'; import InlineSystemMessage from '../../../components/InlineSystemMessage'; import styles from '../../../styles/styles'; @@ -302,7 +302,6 @@ function ReportActionItem(props) { */ const renderItemContent = (hovered = false, isWhisper = false, hasErrors = false) => { let children; - const _submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [props.report.ownerAccountID, 'displayName'], props.report.ownerEmail); // Show the MoneyRequestPreview for when request was created, bill was split or money was sent if ( diff --git a/src/pages/home/report/ReportActionItemCreated.js b/src/pages/home/report/ReportActionItemCreated.js index 9b428bf90ea6..2e7bce92f053 100644 --- a/src/pages/home/report/ReportActionItemCreated.js +++ b/src/pages/home/report/ReportActionItemCreated.js @@ -5,7 +5,6 @@ import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; import ONYXKEYS from '../../../ONYXKEYS'; import ReportWelcomeText from '../../../components/ReportWelcomeText'; -import participantPropTypes from '../../../components/participantPropTypes'; import * as ReportUtils from '../../../libs/ReportUtils'; import styles from '../../../styles/styles'; import OfflineWithFeedback from '../../../components/OfflineWithFeedback'; diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index faa9b4c6fbbc..5450b457e53f 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -13,7 +13,6 @@ import compose from '../../../libs/compose'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import Navigation from '../../../libs/Navigation/Navigation'; import ROUTES from '../../../ROUTES'; -import {usePersonalDetails} from '../../../components/OnyxProvider'; import ControlSelection from '../../../libs/ControlSelection'; import * as ReportUtils from '../../../libs/ReportUtils'; import OfflineWithFeedback from '../../../components/OfflineWithFeedback'; @@ -31,7 +30,7 @@ import ONYXKEYS from '../../../ONYXKEYS'; import Text from '../../../components/Text'; import Tooltip from '../../../components/Tooltip'; import DateUtils from '../../../libs/DateUtils'; -import { getPersonalDetailsByAccountID } from '../../../libs/PersonalDetailsUtils'; +import * as PersonalDetailsUtils from '../../../libs/PersonalDetailsUtils'; const propTypes = { /** All the data of the action */ @@ -85,22 +84,22 @@ const showWorkspaceDetails = (reportID) => { function ReportActionItemSingle(props) { const actorAccountID = props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport ? props.iouReport.managerID : props.action.actorAccountID; - const personalDetails = getPersonalDetailsByAccountID(actorAccountID); - let {displayName} = personalDetails[actorAccountID] || {}; - const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID] || {}; + const personalDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(actorAccountID); + let {displayName} = personalDetails || {}; + const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails || {}; let actorHint = (login || displayName || '').replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); const displayAllActors = useMemo(() => props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport, [props.action.actionName, props.iouReport]); const isWorkspaceActor = ReportUtils.isPolicyExpenseChat(props.report) && (!actorAccountID || displayAllActors); let avatarSource = UserUtils.getAvatar(avatar, actorAccountID); + const delegateDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(props.action.delegateAccountID); if (isWorkspaceActor) { displayName = ReportUtils.getPolicyName(props.report); actorHint = displayName; avatarSource = ReportUtils.getWorkspaceAvatar(props.report); - } else if (props.action.delegateAccountID && personalDetails[props.action.delegateAccountID]) { + } else if (props.action.delegateAccountID && delegateDetails) { // We replace the actor's email, name, and avatar with the Copilot manually for now. And only if we have their // details. This will be improved upon when the Copilot feature is implemented. - const delegateDetails = personalDetails[props.action.delegateAccountID]; const delegateDisplayName = delegateDetails.displayName; actorHint = `${delegateDisplayName} (${props.translate('reportAction.asCopilot')} ${displayName})`; displayName = actorHint; @@ -113,7 +112,7 @@ function ReportActionItemSingle(props) { if (displayAllActors) { // The ownerAccountID and actorAccountID can be the same if the a user requests money back from the IOU's original creator, in that case we need to use managerID to avoid displaying the same user twice const secondaryAccountId = props.iouReport.ownerAccountID === actorAccountID ? props.iouReport.managerID : props.iouReport.ownerAccountID; - const secondaryUserDetails = personalDetails[secondaryAccountId] || {}; + const secondaryUserDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(secondaryAccountId); const secondaryDisplayName = lodashGet(secondaryUserDetails, 'displayName', ''); displayName = `${primaryDisplayName} & ${secondaryDisplayName}`; secondaryAvatar = { diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 6515dc652bdc..8127e40a3aa2 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -13,7 +13,6 @@ import useReportScrollManager from '../../../hooks/useReportScrollManager'; import DateUtils from '../../../libs/DateUtils'; import * as ReportUtils from '../../../libs/ReportUtils'; import * as Report from '../../../libs/actions/Report'; -import compose from '../../../libs/compose'; import styles from '../../../styles/styles'; import variables from '../../../styles/variables'; import reportPropTypes from '../../reportPropTypes'; @@ -72,6 +71,7 @@ const defaultProps = { isLoadingInitialReportActions: false, isLoadingOlderReportActions: false, isLoadingNewerReportActions: false, + policy: {} }; const VERTICAL_OFFSET_THRESHOLD = 200; @@ -428,4 +428,4 @@ ReportActionsList.propTypes = propTypes; ReportActionsList.defaultProps = defaultProps; ReportActionsList.displayName = 'ReportActionsList'; -export default compose(withWindowDimensions)(ReportActionsList); +export default withWindowDimensions(ReportActionsList); diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index 710eed2b86df..760f67dbc310 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -18,7 +18,6 @@ import reportActionPropTypes from './reportActionPropTypes'; import reportPropTypes from '../../reportPropTypes'; import * as ReportUtils from '../../../libs/ReportUtils'; import * as Session from '../../../libs/actions/Session'; -import participantPropTypes from '../../../components/participantPropTypes'; import * as Report from '../../../libs/actions/Report'; import useReportScrollManager from '../../../hooks/useReportScrollManager'; From 9562e8ca37be9b0dd961d9e6f7ea84df753f277a Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 27 Oct 2023 14:44:54 +0500 Subject: [PATCH 10/50] refactor: remove not needed code --- src/libs/PersonalDetailsUtils.js | 16 +++++++++- src/libs/actions/OnyxUpdates.ts | 29 +++++++++---------- src/libs/actions/PersistedRequests.ts | 19 +++++------- src/pages/home/ReportScreen.js | 3 +- .../report/ReportActionsListItemRenderer.js | 4 +++ src/pages/home/report/ReportActionsView.js | 7 ++++- src/pages/home/report/ReportFooter.js | 5 ++-- 7 files changed, 49 insertions(+), 34 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index c2841b139422..acbdeca80a9d 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -176,14 +176,28 @@ function getFormattedAddress(privatePersonalDetails) { return formattedAddress.trim().replace(/,$/, ''); } +/** + * Get personal detail for an accountID + * @param {String} accountID + * @returns {PersonalDetail} personal detail object + */ function getPersonalDetailsByAccountID(accountID) { - return allPersonalDetails[accountID]; + return allPersonalDetails ? allPersonalDetails[accountID] : {}; } +/** + * Get whispered personal details for array of accountIDs + * @param {Array} whisperedToAccountIDs + * @returns {PersonalDetails} personal details + */ function getWhisperedToPersonalDetails(whisperedToAccountIDs) { return _.filter(allPersonalDetails, (details) => _.includes(whisperedToAccountIDs, details.accountID)); } +/** + * Whether personal details is empty + * @returns {Boolean} true if personal details is empty + */ function isPersonalDetailsEmpty() { return !personalDetails.length; } diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index 5f13d8133f16..39a20ae9362a 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -1,6 +1,5 @@ import Onyx, {OnyxEntry} from 'react-native-onyx'; import {Merge} from 'type-fest'; -import {InteractionManager} from 'react-native'; import PusherUtils from '../PusherUtils'; import ONYXKEYS from '../../ONYXKEYS'; import * as QueuedOnyxUpdates from './QueuedOnyxUpdates'; @@ -62,21 +61,19 @@ function apply({lastUpdateID, type, request, response, updates}: Merge | undefined { console.debug(`[OnyxUpdateManager] Applying update type: ${type} with lastUpdateID: ${lastUpdateID}`, {request, response, updates}); - InteractionManager.runAfterInteractions(() => { - if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) { - console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates'); - return Promise.resolve(); - } - if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { - Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID)); - } - if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response) { - return applyHTTPSOnyxUpdates(request, response); - } - if (type === CONST.ONYX_UPDATE_TYPES.PUSHER && updates) { - return applyPusherOnyxUpdates(updates); - } - }); + if (lastUpdateID && lastUpdateIDAppliedToClient && Number(lastUpdateID) < lastUpdateIDAppliedToClient) { + console.debug('[OnyxUpdateManager] Update received was older than current state, returning without applying the updates'); + return Promise.resolve(); + } + if (lastUpdateID && (lastUpdateIDAppliedToClient === null || Number(lastUpdateID) > lastUpdateIDAppliedToClient)) { + Onyx.merge(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, Number(lastUpdateID)); + } + if (type === CONST.ONYX_UPDATE_TYPES.HTTPS && request && response) { + return applyHTTPSOnyxUpdates(request, response); + } + if (type === CONST.ONYX_UPDATE_TYPES.PUSHER && updates) { + return applyPusherOnyxUpdates(updates); + } } /** diff --git a/src/libs/actions/PersistedRequests.ts b/src/libs/actions/PersistedRequests.ts index 1e1a147cecd3..d9f4ed020109 100644 --- a/src/libs/actions/PersistedRequests.ts +++ b/src/libs/actions/PersistedRequests.ts @@ -1,6 +1,5 @@ import Onyx from 'react-native-onyx'; import isEqual from 'lodash/isEqual'; -import {InteractionManager} from 'react-native'; import ONYXKEYS from '../../ONYXKEYS'; import {Request} from '../../types/onyx'; @@ -34,16 +33,14 @@ function remove(requestToRemove: Request) { * We only remove the first matching request because the order of requests matters. * If we were to remove all matching requests, we can end up with a final state that is different than what the user intended. */ - InteractionManager.runAfterInteractions(() => { - const requests = [...persistedRequests]; - const index = requests.findIndex((persistedRequest) => isEqual(persistedRequest, requestToRemove)); - if (index === -1) { - return; - } - requests.splice(index, 1); - persistedRequests = requests; - Onyx.set(ONYXKEYS.PERSISTED_REQUESTS, requests); - }); + const requests = [...persistedRequests]; + const index = requests.findIndex((persistedRequest) => isEqual(persistedRequest, requestToRemove)); + if (index === -1) { + return; + } + requests.splice(index, 1); + persistedRequests = requests; + Onyx.set(ONYXKEYS.PERSISTED_REQUESTS, requests); } function update(oldRequestIndex: number, newRequest: Request) { diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 76be144150c3..ed1fd91b9834 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -172,7 +172,7 @@ function ReportScreen({ const parentReportAction = ReportActionsUtils.getParentReportAction(report); const lastReportAction = useMemo( - () => _.find([...reportActions, parentReportAction], (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action)), + () => reportActions.length ? _.find([...reportActions, parentReportAction], (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action)) : {}, [reportActions, parentReportAction], ); const isSingleTransactionView = ReportUtils.isMoneyRequest(report); @@ -437,7 +437,6 @@ function ReportScreen({ ({ reportActionID: reportAction.reportActionID, diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 415b8b6fbd1b..f1bd30f1a8b9 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -216,12 +216,17 @@ function ReportActionsView(props) { } }; + /** + * Create a lightweight Report so as to keep the re-rendering as light as possible by + * passing in only the required props. + */ const report = useMemo( () => ({ lastReadTime: props.report.lastReadTime, reportID: props.report.reportID, + policyID: props.report.policyID, }), - [props.report.lastReadTime, props.report.reportID], + [props.report.lastReadTime, props.report.reportID, props.report.policyID], ); // Comments have not loaded at all yet do nothing diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index 760f67dbc310..6f061cb3d015 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -85,7 +85,7 @@ function ReportFooter(props) { const onSubmitComment = useCallback( (text) => { - Report.addComment(props.reportID, text); + Report.addComment(props.report.reportID, text); // We need to scroll to the bottom of the list after the comment is added const refID = setTimeout(() => { @@ -119,7 +119,7 @@ function ReportFooter(props) { isEqual(prevProps.report, nextProps.report) && - isEqual(prevProps.reportActions, nextProps.reportActions) && prevProps.pendingAction === nextProps.pendingAction && prevProps.shouldDisableCompose === nextProps.shouldDisableCompose && prevProps.listHeight === nextProps.listHeight && From ad1b337ebc433635371547a8c8c8bbeeff800618 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 27 Oct 2023 16:15:37 +0500 Subject: [PATCH 11/50] fix: linting and test --- src/libs/PersonalDetailsUtils.js | 2 +- .../report/ReportActionCompose/ComposerWithSuggestions.js | 2 ++ tests/unit/ReportActionItemSingleTest.js | 6 ++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index acbdeca80a9d..965f1b927095 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -178,7 +178,7 @@ function getFormattedAddress(privatePersonalDetails) { /** * Get personal detail for an accountID - * @param {String} accountID + * @param {Number} accountID * @returns {PersonalDetail} personal detail object */ function getPersonalDetailsByAccountID(accountID) { diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index 79c53650f27a..115cf404e589 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -283,6 +283,7 @@ function ComposerWithSuggestions({ debouncedBroadcastUserIsTyping(reportID); } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [ preferredSkinTone, reportID, @@ -555,6 +556,7 @@ function ComposerWithSuggestions({ const onChangeText = useCallback((text) => { updateComment(text, true); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( diff --git a/tests/unit/ReportActionItemSingleTest.js b/tests/unit/ReportActionItemSingleTest.js index d6b46eb55414..799f6363d778 100644 --- a/tests/unit/ReportActionItemSingleTest.js +++ b/tests/unit/ReportActionItemSingleTest.js @@ -1,5 +1,5 @@ import Onyx from 'react-native-onyx'; -import {cleanup, screen} from '@testing-library/react-native'; +import {cleanup, screen, waitFor} from '@testing-library/react-native'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; @@ -70,7 +70,9 @@ describe('ReportActionItemSingle', () => { const expectedSecondaryIconTestId = 'SvgDefaultAvatar_w Icon'; return setup().then(() => { - expect(screen.getByTestId(expectedSecondaryIconTestId)).toBeDefined(); + waitFor(() => { + expect(screen.getByTestId(expectedSecondaryIconTestId)).toBeDefined(); + }); }); }); From 9a27bede1cd7a13f7f61bc32afdc23e91113026b Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 27 Oct 2023 17:08:19 +0500 Subject: [PATCH 12/50] fix: failing unread indicator test --- tests/ui/UnreadIndicatorsTest.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index a9ffe258ac7f..34e2c0322cde 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -524,13 +524,12 @@ describe('Unread Indicators', () => { .then(() => { // Simulate the response from the server so that the comment can be deleted in this test lastReportAction = {...CollectionUtils.lastItem(reportActions)}; - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { + return Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { lastMessageText: lastReportAction.message[0].text, lastVisibleActionCreated: DateUtils.getDBTime(lastReportAction.timestamp), lastActorAccountID: lastReportAction.actorAccountID, reportID: REPORT_ID, }); - return waitForBatchedUpdates(); }) .then(() => { // Verify the chat preview text matches the last comment from the current user From 9f294367e0293535250ba3857e9b121dd0a215b4 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 27 Oct 2023 17:15:08 +0500 Subject: [PATCH 13/50] fix: prettier issues --- src/components/LHNOptionsList/OptionRowLHNData.js | 13 +------------ src/components/withCurrentUserPersonalDetails.tsx | 2 +- src/libs/PersonalDetailsUtils.js | 4 ++-- src/libs/SidebarUtils.ts | 2 +- src/pages/home/ReportScreen.js | 5 ++++- .../ReportActionCompose/ComposerWithSuggestions.js | 8 ++------ src/pages/home/report/ReportActionsList.js | 2 +- 7 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js index 2ddf8a687cfd..89142febf679 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.js +++ b/src/components/LHNOptionsList/OptionRowLHNData.js @@ -63,18 +63,7 @@ const defaultProps = { * The OptionRowLHN component is memoized, so it will only * re-render if the data really changed. */ -function OptionRowLHNData({ - isFocused, - fullReport, - reportActions, - preferredLocale, - comment, - policy, - receiptTransactions, - parentReportActions, - transaction, - ...propsToForward -}) { +function OptionRowLHNData({isFocused, fullReport, reportActions, preferredLocale, comment, policy, receiptTransactions, parentReportActions, transaction, ...propsToForward}) { const reportID = propsToForward.reportID; const parentReportAction = parentReportActions[fullReport.parentReportActionID]; diff --git a/src/components/withCurrentUserPersonalDetails.tsx b/src/components/withCurrentUserPersonalDetails.tsx index fa81c658bc78..a54f3f08c8b8 100644 --- a/src/components/withCurrentUserPersonalDetails.tsx +++ b/src/components/withCurrentUserPersonalDetails.tsx @@ -4,7 +4,7 @@ import getComponentDisplayName from '../libs/getComponentDisplayName'; import ONYXKEYS from '../ONYXKEYS'; import personalDetailsPropType from '../pages/personalDetailsPropType'; import type {PersonalDetails, Session} from '../types/onyx'; -import { getPersonalDetailsByAccountID } from '../libs/PersonalDetailsUtils'; +import {getPersonalDetailsByAccountID} from '../libs/PersonalDetailsUtils'; type CurrentUserPersonalDetails = PersonalDetails | Record; diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 965f1b927095..930ba19ad77b 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -178,7 +178,7 @@ function getFormattedAddress(privatePersonalDetails) { /** * Get personal detail for an accountID - * @param {Number} accountID + * @param {Number} accountID * @returns {PersonalDetail} personal detail object */ function getPersonalDetailsByAccountID(accountID) { @@ -187,7 +187,7 @@ function getPersonalDetailsByAccountID(accountID) { /** * Get whispered personal details for array of accountIDs - * @param {Array} whisperedToAccountIDs + * @param {Array} whisperedToAccountIDs * @returns {PersonalDetails} personal details */ function getWhisperedToPersonalDetails(whisperedToAccountIDs) { diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 6db4c5aa8241..c71548a483db 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -1,5 +1,5 @@ /* eslint-disable rulesdir/prefer-underscore-method */ -import Onyx, { OnyxEntry } from 'react-native-onyx'; +import Onyx, {OnyxEntry} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; import {ValueOf} from 'type-fest'; import ONYXKEYS from '../ONYXKEYS'; diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index ed1fd91b9834..31763e064c1d 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -172,7 +172,10 @@ function ReportScreen({ const parentReportAction = ReportActionsUtils.getParentReportAction(report); const lastReportAction = useMemo( - () => reportActions.length ? _.find([...reportActions, parentReportAction], (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action)) : {}, + () => + reportActions.length + ? _.find([...reportActions, parentReportAction], (action) => ReportUtils.canEditReportAction(action) && !ReportActionsUtils.isMoneyRequestAction(action)) + : {}, [reportActions, parentReportAction], ); const isSingleTransactionView = ReportUtils.isMoneyRequest(report); diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index 115cf404e589..26e33a08417e 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -284,11 +284,7 @@ function ComposerWithSuggestions({ } }, // eslint-disable-next-line react-hooks/exhaustive-deps - [ - preferredSkinTone, - reportID, - suggestionsRef, - ], + [preferredSkinTone, reportID, suggestionsRef], ); /** @@ -556,7 +552,7 @@ function ComposerWithSuggestions({ const onChangeText = useCallback((text) => { updateComment(text, true); - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 8127e40a3aa2..6945cf82d158 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -71,7 +71,7 @@ const defaultProps = { isLoadingInitialReportActions: false, isLoadingOlderReportActions: false, isLoadingNewerReportActions: false, - policy: {} + policy: {}, }; const VERTICAL_OFFSET_THRESHOLD = 200; From 352227f178903ec5efe723578c47a12b3ff2ed95 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 27 Oct 2023 19:08:09 +0500 Subject: [PATCH 14/50] fix: failing test --- src/pages/home/report/ReportActionsView.js | 70 ++++------------------ tests/ui/UnreadIndicatorsTest.js | 20 ++++--- 2 files changed, 25 insertions(+), 65 deletions(-) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index f1bd30f1a8b9..266ed6e2e346 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -116,7 +116,7 @@ function ReportActionsView(props) { // update ref with current network state prevNetworkRef.current = props.network; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.network, props.report, isReportFullyVisible]); + }, [props.network, isReportFullyVisible]); useEffect(() => { const prevIsSmallScreenWidth = prevIsSmallScreenWidthRef.current; @@ -130,7 +130,7 @@ function ReportActionsView(props) { // update ref with current state prevIsSmallScreenWidthRef.current = props.isSmallScreenWidth; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.isSmallScreenWidth, props.report, props.reportActions, isReportFullyVisible]); + }, [props.isSmallScreenWidth, props.reportActions, isReportFullyVisible]); useEffect(() => { // Ensures subscription event succeeds when the report/workspace room is created optimistically. @@ -142,7 +142,7 @@ function ReportActionsView(props) { Report.subscribeToReportTypingEvents(reportID); didSubscribeToReportTypingEvents.current = true; } - }, [props.report, didSubscribeToReportTypingEvents, reportID]); + }, [props.report.pendingFields, didSubscribeToReportTypingEvents, reportID]); /** * Retrieves the next set of report actions for the chat once we are nearing the end of what we are currently @@ -225,8 +225,9 @@ function ReportActionsView(props) { lastReadTime: props.report.lastReadTime, reportID: props.report.reportID, policyID: props.report.policyID, + lastVisibleActionCreated: props.report.lastVisibleActionCreated, }), - [props.report.lastReadTime, props.report.reportID, props.report.policyID], + [props.report.lastReadTime, props.report.reportID, props.report.policyID, props.report.lastVisibleActionCreated], ); // Comments have not loaded at all yet do nothing @@ -262,14 +263,6 @@ function arePropsEqual(oldProps, newProps) { return false; } - if (!_.isEqual(oldProps.report.pendingFields, newProps.report.pendingFields)) { - return false; - } - - if (!_.isEqual(oldProps.report.errorFields, newProps.report.errorFields)) { - return false; - } - if (lodashGet(oldProps.network, 'isOffline') !== lodashGet(newProps.network, 'isOffline')) { return false; } @@ -286,26 +279,14 @@ function arePropsEqual(oldProps, newProps) { return false; } - if (oldProps.report.lastReadTime !== newProps.report.lastReadTime) { - return false; - } - if (newProps.isSmallScreenWidth !== oldProps.isSmallScreenWidth) { return false; } - if (lodashGet(newProps.report, 'hasOutstandingIOU') !== lodashGet(oldProps.report, 'hasOutstandingIOU')) { - return false; - } - if (newProps.isComposerFullSize !== oldProps.isComposerFullSize) { return false; } - if (lodashGet(newProps.report, 'statusNum') !== lodashGet(oldProps.report, 'statusNum') || lodashGet(newProps.report, 'stateNum') !== lodashGet(oldProps.report, 'stateNum')) { - return false; - } - if (lodashGet(newProps, 'policy.avatar') !== lodashGet(oldProps, 'policy.avatar')) { return false; } @@ -314,39 +295,14 @@ function arePropsEqual(oldProps, newProps) { return false; } - if (lodashGet(newProps, 'report.reportName') !== lodashGet(oldProps, 'report.reportName')) { - return false; - } - - if (lodashGet(newProps, 'report.description') !== lodashGet(oldProps, 'report.description')) { - return false; - } - - if (lodashGet(newProps, 'report.managerID') !== lodashGet(oldProps, 'report.managerID')) { - return false; - } - - if (lodashGet(newProps, 'report.managerEmail') !== lodashGet(oldProps, 'report.managerEmail')) { - return false; - } - - if (lodashGet(newProps, 'report.total') !== lodashGet(oldProps, 'report.total')) { - return false; - } - - if (lodashGet(newProps, 'report.nonReimbursableTotal') !== lodashGet(oldProps, 'report.nonReimbursableTotal')) { - return false; - } - - if (lodashGet(newProps, 'report.writeCapability') !== lodashGet(oldProps, 'report.writeCapability')) { - return false; - } - - if (lodashGet(newProps, 'report.participantAccountIDs', 0) !== lodashGet(oldProps, 'report.participantAccountIDs', 0)) { - return false; - } - - return _.isEqual(lodashGet(newProps.report, 'icons', []), lodashGet(oldProps.report, 'icons', [])); + return ( + oldProps.report.lastReadTime === newProps.report.lastReadTime && + oldProps.report.reportID === newProps.report.reportID && + oldProps.report.policyID === newProps.report.policyID && + oldProps.report.lastVisibleActionCreated === newProps.report.lastVisibleActionCreated && + oldProps.report.isOptimisticReport === newProps.report.isOptimisticReport && + _.isEqual(oldProps.report.pendingFields, newProps.report.pendingFields) + ); } const MemoizedReportActionsView = React.memo(ReportActionsView, arePropsEqual); diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index 34e2c0322cde..0fdee8aa63f7 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -130,13 +130,15 @@ function signInAndGetAppWithUnreadChat() { // Render the App and sign in as a test user. render(); return waitForBatchedUpdatesWithAct() - .then(() => { - const hintText = Localize.translateLocal('loginForm.loginForm'); - const loginForm = screen.queryAllByLabelText(hintText); - expect(loginForm).toHaveLength(1); - - return TestHelper.signInWithTestUser(USER_A_ACCOUNT_ID, USER_A_EMAIL, undefined, undefined, 'A'); - }) + .then(() => + waitFor(() => { + const hintText = Localize.translateLocal('loginForm.loginForm'); + const loginForm = screen.queryAllByLabelText(hintText); + expect(loginForm).toHaveLength(1); + + return TestHelper.signInWithTestUser(USER_A_ACCOUNT_ID, USER_A_EMAIL, undefined, undefined, 'A'); + }), + ) .then(() => { User.subscribeToUserEvents(); return waitForBatchedUpdates(); @@ -524,12 +526,14 @@ describe('Unread Indicators', () => { .then(() => { // Simulate the response from the server so that the comment can be deleted in this test lastReportAction = {...CollectionUtils.lastItem(reportActions)}; - return Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { lastMessageText: lastReportAction.message[0].text, lastVisibleActionCreated: DateUtils.getDBTime(lastReportAction.timestamp), lastActorAccountID: lastReportAction.actorAccountID, reportID: REPORT_ID, }); + + return waitForBatchedUpdates(); }) .then(() => { // Verify the chat preview text matches the last comment from the current user From 3820a4561ad4464d85adfbbda493ff776c37f01f Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 6 Nov 2023 17:13:07 +0500 Subject: [PATCH 15/50] fix: reassure failures reported --- src/pages/home/ReportScreen.js | 22 ------------------- .../home/report/ReportActionItemSingle.js | 2 +- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index ceeae027f522..a2d76192820c 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -1,4 +1,3 @@ -import {useFocusEffect} from '@react-navigation/core'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; @@ -20,13 +19,11 @@ import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import useWindowDimensions from '@hooks/useWindowDimensions'; import compose from '@libs/compose'; -import getIsReportFullyVisible from '@libs/getIsReportFullyVisible'; import Navigation from '@libs/Navigation/Navigation'; import reportWithoutHasDraftSelector from '@libs/OnyxSelectors/reportWithoutHasDraftSelector'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import Visibility from '@libs/Visibility'; import reportMetadataPropTypes from '@pages/reportMetadataPropTypes'; import reportPropTypes from '@pages/reportPropTypes'; import styles from '@styles/styles'; @@ -259,25 +256,6 @@ function ReportScreen({ Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(accountManagerReportID)); }, [accountManagerReportID]); - useFocusEffect( - useCallback(() => { - const unsubscribeVisibilityListener = Visibility.onVisibilityChange(() => { - const isTopMostReportID = Navigation.getTopmostReportId() === getReportID(route); - // If the report is not fully visible (AKA on small screen devices and LHR is open) or the report is optimistic (AKA not yet created) - // we don't need to call openReport - if (!getIsReportFullyVisible(isTopMostReportID) || report.isOptimisticReport) { - return; - } - - Report.openReport(report.reportID); - }); - - return () => unsubscribeVisibilityListener(); - // The effect should run only on the first focus to attach listener - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []), - ); - useEffect(() => { fetchReportIfNeeded(); ComposerActions.setShouldShowComposeInput(true); diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index 634db2793710..cab7011e88fe 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -112,7 +112,7 @@ function ReportActionItemSingle(props) { if (displayAllActors) { // The ownerAccountID and actorAccountID can be the same if the a user requests money back from the IOU's original creator, in that case we need to use managerID to avoid displaying the same user twice const secondaryAccountId = props.iouReport.ownerAccountID === actorAccountID ? props.iouReport.managerID : props.iouReport.ownerAccountID; - const secondaryUserDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(secondaryAccountId); + const secondaryUserDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(secondaryAccountId) || {}; const secondaryDisplayName = lodashGet(secondaryUserDetails, 'displayName', ''); displayName = `${primaryDisplayName} & ${secondaryDisplayName}`; secondaryAvatar = { From ddf53d3c22e832d8d1ebecff9d08d0626909d067 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 7 Nov 2023 13:49:08 +0500 Subject: [PATCH 16/50] fix: reported reassure issues --- src/components/AttachmentModal.js | 23 ++++++- src/pages/home/ReportScreen.js | 1 - .../ReportActionCompose.js | 62 ++++++++++--------- src/pages/home/report/ReportFooter.js | 50 ++++++--------- 4 files changed, 75 insertions(+), 61 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 7fe342127272..8de07139e022 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -2,7 +2,7 @@ import Str from 'expensify-common/lib/str'; import lodashExtend from 'lodash/extend'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import React, {memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {Animated, Keyboard, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -393,6 +393,14 @@ function AttachmentModal(props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isAttachmentReceipt, props.parentReport, props.parentReportActions, props.policy, props.transaction]); + useImperativeHandle( + props.forwardedRef, + () => ({ + displayFileInModal: validateAndDisplayFileToUpload, + }), + [validateAndDisplayFileToUpload], + ); + return ( <> ( + +)); + +AttachmentModalWithRef.displayName = 'AttachmentModalWithRef'; + export default compose( withWindowDimensions, withLocalize, @@ -539,4 +558,4 @@ export default compose( key: ONYXKEYS.SESSION, }, }), -)(memo(AttachmentModal)); +)(memo(AttachmentModalWithRef)); diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index a2d76192820c..506e635b2336 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -419,7 +419,6 @@ function ReportScreen({ report={report} pendingAction={addWorkspaceRoomOrChatPendingAction} isComposerFullSize={isComposerFullSize} - policies={policies} listHeight={listHeight} isEmptyChat={isEmptyChat} lastReportAction={lastReportAction} diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index 420422534ba7..8b61aa0fa199 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -136,6 +136,7 @@ function ReportActionCompose({ const suggestionsRef = useRef(null); const composerRef = useRef(null); + const attachementModalRef = useRef(null); const reportParticipantIDs = useMemo( () => _.without(lodashGet(report, 'participantAccountIDs', []), currentUserPersonalDetails.accountID), @@ -316,6 +317,10 @@ function ReportActionCompose({ runOnJS(submitForm)(); }, [isSendDisabled, resetFullComposerSize, submitForm, isReportReadyForDisplay]); + const onDisplayFileInModal = () => { + attachementModalRef.current.displayFileInModal(); + }; + return ( @@ -339,6 +344,7 @@ function ReportActionCompose({ ]} > setIsAttachmentPreviewActive(true)} @@ -365,34 +371,6 @@ function ReportActionCompose({ onItemSelected={onItemSelected} actionButtonRef={actionButtonRef} /> - { if (isAttachmentPreviewActive) { @@ -405,6 +383,34 @@ function ReportActionCompose({ )} + { - // eslint-disable-next-line rulesdir/prefer-onyx-connect-in-libs - const connID = Onyx.connect({ - key: ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, - callback: (val) => { - if (val === shouldShowComposeInput) { - return; - } - setShouldShowComposeInput(val); - }, - }); - - return () => { - Onyx.disconnect(connID); - }; - }, [shouldShowComposeInput]); - const onSubmitComment = useCallback( (text) => { Report.addComment(props.report.reportID, text); @@ -114,7 +95,7 @@ function ReportFooter(props) { )} )} - {!hideComposer && (shouldShowComposeInput || !props.isSmallScreenWidth) && ( + {!hideComposer && (props.shouldShowComposeInput || !props.isSmallScreenWidth) && ( @@ -139,14 +119,24 @@ ReportFooter.displayName = 'ReportFooter'; ReportFooter.propTypes = propTypes; ReportFooter.defaultProps = defaultProps; -export default withWindowDimensions( +export default compose( + withWindowDimensions, + withOnyx({ + shouldShowComposeInput: { + key: ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, + }, + }), +)( memo( ReportFooter, (prevProps, nextProps) => isEqual(prevProps.report, nextProps.report) && prevProps.pendingAction === nextProps.pendingAction && - prevProps.shouldDisableCompose === nextProps.shouldDisableCompose && prevProps.listHeight === nextProps.listHeight && + prevProps.isComposerFullSize === nextProps.isComposerFullSize && + prevProps.isEmptyChat === nextProps.isEmptyChat && + prevProps.lastReportAction === nextProps.lastReportAction && + prevProps.shouldShowComposeInput === nextProps.shouldShowComposeInput && prevProps.isReportReadyForDisplay === nextProps.isReportReadyForDisplay, ), ); From 5e2f21f8b62472cb67715f5bcd5f26e9e2b70e0e Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 7 Nov 2023 14:26:07 +0500 Subject: [PATCH 17/50] fix: reported reassure issues --- src/components/LHNOptionsList/LHNOptionsList.js | 14 +------------- .../ComposerWithSuggestions.js | 2 +- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.js b/src/components/LHNOptionsList/LHNOptionsList.js index 3986773aca87..2603c0128af3 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.js +++ b/src/components/LHNOptionsList/LHNOptionsList.js @@ -4,10 +4,8 @@ import React, {useCallback} from 'react'; import {FlatList, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; -import participantPropTypes from '@components/participantPropTypes'; import withCurrentReportID, {withCurrentReportIDDefaultProps, withCurrentReportIDPropTypes} from '@components/withCurrentReportID'; import compose from '@libs/compose'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; import reportActionPropTypes from '@pages/home/report/reportActionPropTypes'; import reportPropTypes from '@pages/reportPropTypes'; import styles from '@styles/styles'; @@ -56,9 +54,6 @@ const propTypes = { /** Indicates which locale the user currently has selected */ preferredLocale: PropTypes.string, - /** List of users' personal details */ - personalDetails: PropTypes.objectOf(participantPropTypes), - /** The transaction from the parent report action */ transactions: PropTypes.objectOf( PropTypes.shape({ @@ -78,7 +73,6 @@ const defaultProps = { reports: {}, policy: {}, preferredLocale: CONST.LOCALES.DEFAULT, - personalDetails: {}, transactions: {}, draftComments: {}, ...withCurrentReportIDDefaultProps, @@ -97,7 +91,6 @@ function LHNOptionsList({ reportActions, policy, preferredLocale, - personalDetails, transactions, draftComments, currentReportID, @@ -144,7 +137,6 @@ function LHNOptionsList({ '', )}`; const itemComment = draftComments[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] || ''; - const participantPersonalDetailList = _.values(OptionsListUtils.getPersonalDetailsForAccountIDs(itemFullReport.participantAccountIDs, personalDetails)); return ( ); }, - [currentReportID, draftComments, onSelectRow, optionMode, personalDetails, policy, preferredLocale, reportActions, reports, shouldDisableFocusOptions, transactions], + [currentReportID, draftComments, onSelectRow, optionMode, policy, preferredLocale, reportActions, reports, shouldDisableFocusOptions, transactions], ); return ( @@ -206,9 +197,6 @@ export default compose( preferredLocale: { key: ONYXKEYS.NVP_PREFERRED_LOCALE, }, - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, transactions: { key: ONYXKEYS.COLLECTION.TRANSACTION, }, diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js index cf772230d2bf..7c5d0cfe2f2a 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js @@ -29,6 +29,7 @@ import * as SuggestionUtils from '@libs/SuggestionUtils'; import updateMultilineInputRange from '@libs/UpdateMultilineInputRange'; import updatePropsPaperWorklet from '@libs/updatePropsPaperWorklet'; import willBlurTextInputOnTapOutsideFunc from '@libs/willBlurTextInputOnTapOutside'; +import SendButton from '@pages/home/report/ReportActionCompose/SendButton'; import SilentCommentUpdater from '@pages/home/report/ReportActionCompose/SilentCommentUpdater'; import Suggestions from '@pages/home/report/ReportActionCompose/Suggestions'; import containerComposeStyles from '@styles/containerComposeStyles'; @@ -40,7 +41,6 @@ import * as Report from '@userActions/Report'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import SendButton from '@pages/home/report/ReportActionCompose/SendButton'; import {defaultProps, propTypes} from './composerWithSuggestionsProps'; const {RNTextInputReset} = NativeModules; From 46c9d60399162fb2f2ff30ac5edd9760d2d0e159 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 13 Nov 2023 13:54:36 +0500 Subject: [PATCH 18/50] fix: pr review --- src/components/ArchivedReportFooter.js | 6 ++--- src/components/AttachmentModal.js | 20 ++++------------ src/components/AvatarWithDisplayName.js | 2 +- src/libs/OptionsListUtils.js | 11 +++++++-- src/libs/PersonalDetailsUtils.js | 15 +++++++++--- src/libs/ReportUtils.js | 6 ++--- src/libs/SidebarUtils.ts | 4 ++-- src/libs/actions/Task.js | 2 +- src/pages/ReportDetailsPage.js | 2 +- .../AttachmentPickerWithMenuItems.js | 3 ++- .../composerWithSuggestionsProps.js | 3 ++- .../ReportActionCompose/SuggestionMention.js | 24 ++++++------------- src/pages/home/report/ReportActionItem.js | 2 +- src/pages/home/report/ReportFooter.js | 2 ++ 14 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/components/ArchivedReportFooter.js b/src/components/ArchivedReportFooter.js index a17cdb92136d..8c790f106ae7 100644 --- a/src/components/ArchivedReportFooter.js +++ b/src/components/ArchivedReportFooter.js @@ -46,14 +46,14 @@ const defaultProps = { function ArchivedReportFooter(props) { const archiveReason = lodashGet(props.reportClosedAction, 'originalMessage.reason', CONST.REPORT.ARCHIVE_REASON.DEFAULT); - let displayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [props.report.ownerAccountID, 'displayName']); + let displayName = PersonalDetailsUtils.getDisplayNameOrDefault([props.report.ownerAccountID, 'displayName']); let oldDisplayName; if (archiveReason === CONST.REPORT.ARCHIVE_REASON.ACCOUNT_MERGED) { const newAccountID = props.reportClosedAction.originalMessage.newAccountID; const oldAccountID = props.reportClosedAction.originalMessage.oldAccountID; - displayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [newAccountID, 'displayName']); - oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [oldAccountID, 'displayName']); + displayName = PersonalDetailsUtils.getDisplayNameOrDefault([newAccountID, 'displayName']); + oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault([oldAccountID, 'displayName']); } const shouldRenderHTML = archiveReason !== CONST.REPORT.ARCHIVE_REASON.DEFAULT; diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 24ac0421c5b2..57ed1ff3e131 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -2,7 +2,7 @@ import Str from 'expensify-common/lib/str'; import lodashExtend from 'lodash/extend'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; +import React, {forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {Animated, Keyboard, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; @@ -110,7 +110,7 @@ const defaultProps = { isWorkspaceAvatar: false, }; -function AttachmentModal(props) { +const AttachmentModal = forwardRef((props, ref) => { const onModalHideCallbackRef = useRef(null); const [isModalOpen, setIsModalOpen] = useState(props.defaultOpen); const [shouldLoadAttachment, setShouldLoadAttachment] = useState(false); @@ -394,7 +394,7 @@ function AttachmentModal(props) { }, [isAttachmentReceipt, props.parentReport, props.parentReportActions, props.policy, props.transaction, file]); useImperativeHandle( - props.forwardedRef, + ref, () => ({ displayFileInModal: validateAndDisplayFileToUpload, }), @@ -514,22 +514,12 @@ function AttachmentModal(props) { })} ); -} +}); AttachmentModal.propTypes = propTypes; AttachmentModal.defaultProps = defaultProps; AttachmentModal.displayName = 'AttachmentModal'; -const AttachmentModalWithRef = React.forwardRef((props, ref) => ( - -)); - -AttachmentModalWithRef.displayName = 'AttachmentModalWithRef'; - export default compose( withWindowDimensions, withLocalize, @@ -558,4 +548,4 @@ export default compose( key: ONYXKEYS.SESSION, }, }), -)(memo(AttachmentModalWithRef)); +)(memo(AttachmentModal)); diff --git a/src/components/AvatarWithDisplayName.js b/src/components/AvatarWithDisplayName.js index 4be31b7d1a45..194a9239fa57 100644 --- a/src/components/AvatarWithDisplayName.js +++ b/src/components/AvatarWithDisplayName.js @@ -88,7 +88,7 @@ function AvatarWithDisplayName(props) { const subtitle = ReportUtils.getChatRoomSubtitle(props.report); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(props.report); const isMoneyRequestOrReport = ReportUtils.isMoneyRequestReport(props.report) || ReportUtils.isMoneyRequest(props.report); - const icons = ReportUtils.getIcons(props.report, null, props.policy); + const icons = ReportUtils.getIcons(props.report, null, '', -1, props.policy); const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs([props.report.ownerAccountID]); const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(_.values(ownerPersonalDetails), false); const shouldShowSubscriptAvatar = ReportUtils.shouldReportShowSubscript(props.report); diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 485bfbbbf08d..6d5588548636 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -529,7 +529,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { (lastReportActions[report.reportID] && lastReportActions[report.reportID].originalMessage && lastReportActions[report.reportID].originalMessage.reason) || CONST.REPORT.ARCHIVE_REASON.DEFAULT; lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { - displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails, 'displayName'), + displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault('displayName', lastActorDetails), policyName: ReportUtils.getPolicyName(report), }); } @@ -561,7 +561,14 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { result.text = reportName; result.searchText = getSearchText(report, reportName, personalDetailList, result.isChatRoom || result.isPolicyExpenseChat, result.isThread); - result.icons = ReportUtils.getIcons(report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), personalDetail.login, personalDetail.accountID); + result.icons = ReportUtils.getIcons( + report, + UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), + personalDetail.login, + personalDetail.accountID, + undefined, + personalDetails, + ); result.subtitle = subtitle; return result; diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 842c06bb3228..bbcfd53454a9 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -17,13 +17,13 @@ Onyx.connect({ }); /** - * @param {Object | Null} passedPersonalDetails * @param {Array | String} pathToDisplayName * @param {String} [defaultValue] optional default display name value + * @param {Object | Null} passedPersonalDetails optional default personal details object * @returns {String} */ -function getDisplayNameOrDefault(passedPersonalDetails, pathToDisplayName, defaultValue = '') { - const displayName = lodashGet(passedPersonalDetails || allPersonalDetails, pathToDisplayName); +function getDisplayNameOrDefault(pathToDisplayName, defaultValue = '', passedPersonalDetails = allPersonalDetails) { + const displayName = lodashGet(passedPersonalDetails, pathToDisplayName); return displayName || defaultValue || Localize.translateLocal('common.hidden'); } @@ -203,6 +203,14 @@ function isPersonalDetailsEmpty() { return !personalDetails.length; } +/** + * Get personal details object + * @returns {PersonalDetail} personal detail object + */ +function getPersonalDetails() { + return allPersonalDetails || {}; +} + export { getDisplayNameOrDefault, getPersonalDetailsByIDs, @@ -213,4 +221,5 @@ export { getPersonalDetailsByAccountID, getWhisperedToPersonalDetails, isPersonalDetailsEmpty, + getPersonalDetails, }; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 83cf62f6da04..38846dce5bc0 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1103,15 +1103,15 @@ function getWorkspaceIcon(report, policy = undefined) { * The Avatar sources can be URLs or Icon components according to the chat type. * * @param {Object} report - * @param {Object} passedPersonalDetails * @param {*} [defaultIcon] * @param {String} [defaultName] * @param {Number} [defaultAccountID] * @param {Object} [policy] + * @param {Object} passedPersonalDetails * @returns {Array<*>} */ -function getIcons(report, passedPersonalDetails, defaultIcon = null, defaultName = '', defaultAccountID = -1, policy = undefined) { - const personalDetails = passedPersonalDetails || allPersonalDetails; +function getIcons(report, defaultIcon = null, defaultName = '', defaultAccountID = -1, policy = undefined, passedPersonalDetails = allPersonalDetails) { + const personalDetails = passedPersonalDetails; if (_.isEmpty(report)) { const fallbackIcon = { source: defaultIcon || Expensicons.FallbackAvatar, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 97ae1d25cdd9..826db3995c43 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -425,7 +425,7 @@ function getOptionData( case CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED: { lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { policyName: ReportUtils.getPolicyName(report, false, policy), - displayName: PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails, 'displayName'), + displayName: PersonalDetailsUtils.getDisplayNameOrDefault('displayName', '', lastActorDetails), }); break; } @@ -515,7 +515,7 @@ function getOptionData( result.subtitle = subtitle; result.participantsList = participantPersonalDetailList; - result.icons = ReportUtils.getIcons(report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), '', -1, policy); + result.icons = ReportUtils.getIcons(report, UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), '', -1, policy, personalDetails); result.searchText = OptionsListUtils.getSearchText(report, reportName, participantPersonalDetailList, result.isChatRoom || result.isPolicyExpenseChat, result.isThread); result.displayNamesWithTooltips = displayNamesWithTooltips; diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index e884a4d7a6b3..6329552d67e2 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -699,7 +699,7 @@ function getShareDestination(reportID, reports, personalDetails) { subtitle = ReportUtils.getChatRoomSubtitle(report); } return { - icons: ReportUtils.getIcons(report, personalDetails, Expensicons.FallbackAvatar), + icons: ReportUtils.getIcons(report, Expensicons.FallbackAvatar, '', -1, undefined, personalDetails), displayName: ReportUtils.getReportName(report), subtitle, displayNamesWithTooltips, diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index de25fdc3a081..935074462966 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -163,7 +163,7 @@ function ReportDetailsPage(props) { return ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForAccountIDs(participants, props.personalDetails), hasMultipleParticipants); }, [participants, props.personalDetails]); - const icons = useMemo(() => ReportUtils.getIcons(props.report, props.personalDetails, props.policies), [props.report, props.personalDetails, props.policies]); + const icons = useMemo(() => ReportUtils.getIcons(props.report, null, '', -1, props.policies, props.personalDetails), [props.report, props.personalDetails, props.policies]); const chatRoomSubtitleText = chatRoomSubtitle ? ( {}, + disabled: false, }; export {propTypes, defaultProps}; diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js index 6d85a0c349de..513e161dda41 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js @@ -1,16 +1,15 @@ import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; -import Onyx from 'react-native-onyx'; import _ from 'underscore'; import * as Expensicons from '@components/Icon/Expensicons'; import MentionSuggestions from '@components/MentionSuggestions'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; +import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; import * as SuggestionProps from './suggestionProps'; /** @@ -38,20 +37,6 @@ const defaultProps = { forwardedRef: null, }; -/** - * We only need the personalDetails once because as long as the - * user is in the ReportScreen, these details won't be changing, - * hence we don't have to use it with `withOnyx`. - */ -let allPersonalDetails = {}; -// eslint-disable-next-line rulesdir/prefer-onyx-connect-in-libs -Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - callback: (val) => { - allPersonalDetails = val; - }, -}); - function SuggestionMention({ value, setValue, @@ -65,7 +50,12 @@ function SuggestionMention({ measureParentContainer, isComposerFocused, }) { - const personalDetails = allPersonalDetails; + /** + * We only need the personalDetails once because as long as the + * user is in the ReportScreen, these details won't be changing, + * hence we don't have to use it with `withOnyx`. + */ + const personalDetails = PersonalDetailsUtils.getPersonalDetails(); const {translate} = useLocalize(); const previousValue = usePrevious(value); const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index fe150be8db90..6c056c1b2527 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -365,7 +365,7 @@ function ReportActionItem(props) { ); } else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED) { - const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(null, [props.report.ownerAccountID, 'displayName']); + const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault([props.report.ownerAccountID, 'displayName']); const paymentType = lodashGet(props.action, 'originalMessage.paymentType', ''); const isSubmitterOfUnsettledReport = ReportUtils.isCurrentUserSubmitter(props.report.reportID) && !ReportUtils.isSettled(props.report.reportID); diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index b407605a1c5d..d9490e17a688 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -137,6 +137,8 @@ export default compose( prevProps.isEmptyChat === nextProps.isEmptyChat && prevProps.lastReportAction === nextProps.lastReportAction && prevProps.shouldShowComposeInput === nextProps.shouldShowComposeInput && + prevProps.windowWidth === nextProps.windowWidth && + prevProps.isSmallScreenWidth === nextProps.isSmallScreenWidth && prevProps.isReportReadyForDisplay === nextProps.isReportReadyForDisplay, ), ); From 83d823ef9a37a5214319e54a41733d61061928ec Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 16 Nov 2023 12:16:32 +0500 Subject: [PATCH 19/50] fix: add default value --- src/libs/OptionsListUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index fc306a1f31bf..37b2e0add4ab 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -514,7 +514,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { (lastReportActions[report.reportID] && lastReportActions[report.reportID].originalMessage && lastReportActions[report.reportID].originalMessage.reason) || CONST.REPORT.ARCHIVE_REASON.DEFAULT; lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { - displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault('displayName', lastActorDetails), + displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault('displayName', '', lastActorDetails), policyName: ReportUtils.getPolicyName(report), }); } From fbf272901234c71597116a1d2a821f29647c2b71 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 30 Nov 2023 11:41:35 +0500 Subject: [PATCH 20/50] fix: appropriate arguments to getDisplayNameOrDefault --- src/pages/ReportParticipantsPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index ceaa53a41a6b..78350fa2a361 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -60,7 +60,7 @@ const getAllParticipants = (report, personalDetails, translate) => .map((accountID, index) => { const userPersonalDetail = lodashGet(personalDetails, accountID, {displayName: personalDetails.displayName || translate('common.hidden'), avatar: ''}); const userLogin = LocalePhoneNumber.formatPhoneNumber(userPersonalDetail.login || '') || translate('common.hidden'); - const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(userPersonalDetail, 'displayName'); + const displayName = PersonalDetailsUtils.getDisplayNameOrDefault('displayName', '', userPersonalDetail); return { alternateText: userLogin, From 1d115c0fad01f0a47c453f602ddd88345b4e8fd6 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 30 Nov 2023 12:29:55 +0500 Subject: [PATCH 21/50] fix: workspace unavailable avatar --- src/pages/ReportDetailsPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 9dcc01258853..c40290f4c3ef 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -254,7 +254,7 @@ export default compose( key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, policies: { - key: ONYXKEYS.COLLECTION.POLICY, + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, }, }), )(ReportDetailsPage); From 3b9817851eb2c8a30fea1a43f1734f6464016f41 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 30 Nov 2023 12:50:39 +0500 Subject: [PATCH 22/50] fix: pass policy instead of policies to getIcons --- src/pages/ReportDetailsPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index c40290f4c3ef..54cee38c31e8 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -150,7 +150,7 @@ function ReportDetailsPage(props) { return ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForAccountIDs(participants, props.personalDetails), hasMultipleParticipants); }, [participants, props.personalDetails]); - const icons = useMemo(() => ReportUtils.getIcons(props.report, null, '', -1, props.policies, props.personalDetails), [props.report, props.personalDetails, props.policies]); + const icons = useMemo(() => ReportUtils.getIcons(props.report, null, '', -1, policy, props.personalDetails), [props.report, props.personalDetails, policy]); const chatRoomSubtitleText = chatRoomSubtitle ? ( `${ONYXKEYS.COLLECTION.POLICY}${report.policyID}`, + key: ONYXKEYS.COLLECTION.POLICY, }, }), )(ReportDetailsPage); From 33703b37cde9ff69205766ed74d69411d1127740 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 1 Dec 2023 13:00:27 +0500 Subject: [PATCH 23/50] test: fix failing caledarpickertest due to an edge case of last month in the calendar --- tests/unit/CalendarPickerTest.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/unit/CalendarPickerTest.js b/tests/unit/CalendarPickerTest.js index 92092acc4cd7..d82efbf65afd 100644 --- a/tests/unit/CalendarPickerTest.js +++ b/tests/unit/CalendarPickerTest.js @@ -64,7 +64,20 @@ describe('CalendarPicker', () => { fireEvent.press(getByTestId('next-month-arrow')); - const nextMonth = new Date().getMonth() + 1; + /** + * Handle edge case for last month "Decemeber" as + * adding a 1 to it will produce an invalid month + * 12 since the last index supported is 11, so we + * set it to 0 to represent January. + */ + let nextMonth = new Date().getMonth(); + + if (nextMonth !== 11) { + nextMonth += 1; + } else { + nextMonth = 0; + } + expect(getByText(monthNames[nextMonth])).toBeTruthy(); }); From ad2549a25ced3e7f228d33519c48ddf1f8579d6f Mon Sep 17 00:00:00 2001 From: hurali97 Date: Mon, 4 Dec 2023 15:52:49 +0500 Subject: [PATCH 24/50] fix: personal details not updating --- src/components/withCurrentUserPersonalDetails.tsx | 6 ++++-- src/pages/home/HeaderView.js | 12 ++++++++++-- src/pages/home/report/ReportActionItemSingle.js | 14 +++++++------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/components/withCurrentUserPersonalDetails.tsx b/src/components/withCurrentUserPersonalDetails.tsx index ede90f6a07c7..a97067c32c72 100644 --- a/src/components/withCurrentUserPersonalDetails.tsx +++ b/src/components/withCurrentUserPersonalDetails.tsx @@ -1,10 +1,11 @@ import React, {ComponentType, ForwardedRef, RefAttributes, useMemo} from 'react'; import {OnyxEntry, withOnyx} from 'react-native-onyx'; import getComponentDisplayName from '@libs/getComponentDisplayName'; -import {getPersonalDetailsByAccountID} from '@libs/PersonalDetailsUtils'; import personalDetailsPropType from '@pages/personalDetailsPropType'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetails, Session} from '@src/types/onyx'; +import {usePersonalDetails} from './OnyxProvider'; type CurrentUserPersonalDetails = PersonalDetails | Record; @@ -32,8 +33,9 @@ export default function >, ): ComponentType & RefAttributes, keyof OnyxProps>> { function WithCurrentUserPersonalDetails(props: Omit, ref: ForwardedRef) { + const personalDetails = usePersonalDetails() ?? CONST.EMPTY_OBJECT; const accountID = props.session?.accountID ?? 0; - const accountPersonalDetails: PersonalDetails = getPersonalDetailsByAccountID(accountID); + const accountPersonalDetails = personalDetails?.[accountID]; const currentUserPersonalDetails: CurrentUserPersonalDetails = useMemo( () => (accountPersonalDetails ? {...accountPersonalDetails, accountID} : {}), [accountPersonalDetails, accountID], diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index bfef2e6b50ca..2b6fa35508db 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -11,6 +11,7 @@ import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import MultipleAvatars from '@components/MultipleAvatars'; import ParentNavigationSubtitle from '@components/ParentNavigationSubtitle'; +import participantPropTypes from '@components/participantPropTypes'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import ReportHeaderSkeletonView from '@components/ReportHeaderSkeletonView'; import SubscriptAvatar from '@components/SubscriptAvatar'; @@ -43,6 +44,9 @@ const propTypes = { /** The report currently being looked at */ report: reportPropTypes, + /** Personal details of all the users */ + personalDetails: PropTypes.objectOf(participantPropTypes), + /** Onyx Props */ parentReport: reportPropTypes, @@ -65,6 +69,7 @@ const propTypes = { }; const defaultProps = { + personalDetails: {}, report: null, guideCalendarLink: null, parentReport: {}, @@ -80,7 +85,7 @@ function HeaderView(props) { const theme = useTheme(); const styles = useThemeStyles(); const participants = lodashGet(props.report, 'participantAccountIDs', []); - const participantPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants); + const participantPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants, props.personalDetails); const isMultipleParticipant = participants.length > 1; const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(participantPersonalDetails, isMultipleParticipant); const isChatThread = ReportUtils.isChatThread(props.report); @@ -178,7 +183,7 @@ function HeaderView(props) { const shouldShowSubscript = ReportUtils.shouldReportShowSubscript(props.report); const defaultSubscriptSize = ReportUtils.isExpenseRequest(props.report) ? CONST.AVATAR_SIZE.SMALL_NORMAL : CONST.AVATAR_SIZE.DEFAULT; - const icons = ReportUtils.getIcons(reportHeaderData); + const icons = ReportUtils.getIcons(reportHeaderData, props.personalDetails); const brickRoadIndicator = ReportUtils.hasReportNameError(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; const shouldShowBorderBottom = !isTaskReport || !isSmallScreenWidth; const shouldDisableDetailPage = ReportUtils.shouldDisableDetailPage(props.report); @@ -306,5 +311,8 @@ export default memo( key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, selector: (policy) => _.pick(policy, ['name', 'avatar', 'pendingAction']), }, + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, })(HeaderView), ); diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index bc9e7eef1b40..1a00d50197d4 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -6,6 +6,7 @@ import _ from 'underscore'; import Avatar from '@components/Avatar'; import MultipleAvatars from '@components/MultipleAvatars'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; +import {usePersonalDetails} from '@components/OnyxProvider'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import SubscriptAvatar from '@components/SubscriptAvatar'; import Text from '@components/Text'; @@ -15,7 +16,6 @@ import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import ControlSelection from '@libs/ControlSelection'; import DateUtils from '@libs/DateUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as UserUtils from '@libs/UserUtils'; import reportPropTypes from '@pages/reportPropTypes'; @@ -81,24 +81,24 @@ const showWorkspaceDetails = (reportID) => { function ReportActionItemSingle(props) { const styles = useThemeStyles(); const theme = useTheme(); + const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const actorAccountID = props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport ? props.iouReport.managerID : props.action.actorAccountID; - const personalDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(actorAccountID); let displayName = ReportUtils.getDisplayNameForParticipant(actorAccountID); - const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails || {}; + const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID] || {}; let actorHint = (login || displayName || '').replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, ''); const displayAllActors = useMemo(() => props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport, [props.action.actionName, props.iouReport]); const isWorkspaceActor = ReportUtils.isPolicyExpenseChat(props.report) && (!actorAccountID || displayAllActors); let avatarSource = UserUtils.getAvatar(avatar, actorAccountID); - const delegateDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(props.action.delegateAccountID); if (isWorkspaceActor) { displayName = ReportUtils.getPolicyName(props.report); actorHint = displayName; avatarSource = ReportUtils.getWorkspaceAvatar(props.report); - } else if (props.action.delegateAccountID && delegateDetails) { + } else if (props.action.delegateAccountID && personalDetails[props.action.delegateAccountID]) { // We replace the actor's email, name, and avatar with the Copilot manually for now. And only if we have their // details. This will be improved upon when the Copilot feature is implemented. + const delegateDetails = personalDetails[props.action.delegateAccountID]; const delegateDisplayName = delegateDetails.displayName; actorHint = `${delegateDisplayName} (${props.translate('reportAction.asCopilot')} ${displayName})`; displayName = actorHint; @@ -111,8 +111,8 @@ function ReportActionItemSingle(props) { if (displayAllActors) { // The ownerAccountID and actorAccountID can be the same if the a user requests money back from the IOU's original creator, in that case we need to use managerID to avoid displaying the same user twice const secondaryAccountId = props.iouReport.ownerAccountID === actorAccountID ? props.iouReport.managerID : props.iouReport.ownerAccountID; - const secondaryUserDetails = PersonalDetailsUtils.getPersonalDetailsByAccountID(secondaryAccountId) || {}; - const secondaryDisplayName = lodashGet(secondaryUserDetails, 'displayName', ''); + const secondaryUserDetails = personalDetails[secondaryAccountId] || {}; + const secondaryDisplayName = ReportUtils.getDisplayNameForParticipant(secondaryAccountId); displayName = `${primaryDisplayName} & ${secondaryDisplayName}`; secondaryAvatar = { source: UserUtils.getAvatar(secondaryUserDetails.avatar, secondaryAccountId), From a99d653bddbc094cac7f03f4eb5dc803898c053b Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 6 Dec 2023 14:46:15 +0500 Subject: [PATCH 25/50] fix: personalDetails not updating --- src/components/LHNOptionsList/OptionRowLHNData.js | 6 ++++-- src/components/MoneyReportHeader.js | 3 +++ src/components/MoneyRequestHeader.js | 3 +++ src/libs/SidebarUtils.ts | 10 ++-------- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js index 77de5b7b87fd..b6c3a6e7ed00 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.js +++ b/src/components/LHNOptionsList/OptionRowLHNData.js @@ -2,6 +2,7 @@ import {deepEqual} from 'fast-equals'; import PropTypes from 'prop-types'; import React, {useEffect, useMemo, useRef} from 'react'; import _ from 'underscore'; +import {usePersonalDetails} from '@components/OnyxProvider'; import transactionPropTypes from '@components/transactionPropTypes'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import SidebarUtils from '@libs/SidebarUtils'; @@ -58,6 +59,7 @@ const defaultProps = { * re-render if the data really changed. */ function OptionRowLHNData({isFocused, fullReport, reportActions, preferredLocale, comment, policy, receiptTransactions, parentReportAction, transaction, ...propsToForward}) { + const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const reportID = propsToForward.reportID; const optionItemRef = useRef(); @@ -70,7 +72,7 @@ function OptionRowLHNData({isFocused, fullReport, reportActions, preferredLocale const optionItem = useMemo(() => { // Note: ideally we'd have this as a dependent selector in onyx! - const item = SidebarUtils.getOptionData(fullReport, reportActions, preferredLocale, policy, parentReportAction); + const item = SidebarUtils.getOptionData(fullReport, reportActions, personalDetails, preferredLocale, policy, parentReportAction); if (deepEqual(item, optionItemRef.current)) { return optionItemRef.current; } @@ -79,7 +81,7 @@ function OptionRowLHNData({isFocused, fullReport, reportActions, preferredLocale // Listen parentReportAction to update title of thread report when parentReportAction changed // Listen to transaction to update title of transaction report when transaction changed // eslint-disable-next-line react-hooks/exhaustive-deps - }, [fullReport, linkedTransaction, reportActions, preferredLocale, policy, parentReportAction, transaction]); + }, [fullReport, linkedTransaction, reportActions, personalDetails, preferredLocale, policy, parentReportAction, transaction]); useEffect(() => { if (!optionItem || optionItem.hasDraftComment || !comment || comment.length <= 0 || isFocused) { diff --git a/src/components/MoneyReportHeader.js b/src/components/MoneyReportHeader.js index 909c52af8912..9cb9d392004d 100644 --- a/src/components/MoneyReportHeader.js +++ b/src/components/MoneyReportHeader.js @@ -20,6 +20,7 @@ import ROUTES from '@src/ROUTES'; import Button from './Button'; import HeaderWithBackButton from './HeaderWithBackButton'; import MoneyReportHeaderStatusBar from './MoneyReportHeaderStatusBar'; +import {usePersonalDetails} from './OnyxProvider'; import SettlementButton from './SettlementButton'; import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions'; @@ -64,6 +65,7 @@ const defaultProps = { }; function MoneyReportHeader({session, policy, chatReport, nextStep, report: moneyRequestReport, isSmallScreenWidth}) { + const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const styles = useThemeStyles(); const {translate} = useLocalize(); const reimbursableTotal = ReportUtils.getMoneyRequestReimbursableTotal(moneyRequestReport); @@ -100,6 +102,7 @@ function MoneyReportHeader({session, policy, chatReport, nextStep, report: money shouldShowPinButton={false} report={moneyRequestReport} policy={policy} + personalDetails={personalDetails} shouldShowBackButton={isSmallScreenWidth} onBackButtonPress={() => Navigation.goBack(ROUTES.HOME, false, true)} // Shows border if no buttons or next steps are showing below the header diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index 3935cf940cfc..db8ab3c3a3eb 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -22,6 +22,7 @@ import ConfirmModal from './ConfirmModal'; import HeaderWithBackButton from './HeaderWithBackButton'; import * as Expensicons from './Icon/Expensicons'; import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar'; +import {usePersonalDetails} from './OnyxProvider'; import transactionPropTypes from './transactionPropTypes'; const propTypes = { @@ -62,6 +63,7 @@ const defaultProps = { }; function MoneyRequestHeader({session, parentReport, report, parentReportAction, transaction, policy}) { + const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const styles = useThemeStyles(); const {translate} = useLocalize(); const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); @@ -121,6 +123,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction, ownerAccountID: lodashGet(parentReport, 'ownerAccountID', null), }} policy={policy} + personalDetails={personalDetails} shouldShowBackButton={isSmallScreenWidth} onBackButtonPress={() => Navigation.goBack(ROUTES.HOME, false, true)} /> diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 50c930d6050e..9872187fa050 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -1,6 +1,6 @@ /* eslint-disable rulesdir/prefer-underscore-method */ import Str from 'expensify-common/lib/str'; -import Onyx, {OnyxCollection, OnyxEntry} from 'react-native-onyx'; +import Onyx, {OnyxCollection} from 'react-native-onyx'; import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -62,12 +62,6 @@ Onyx.connect({ }, }); -let allPersonalDetails: OnyxEntry>; -Onyx.connect({ - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - callback: (val) => (allPersonalDetails = val), -}); - let resolveSidebarIsReadyPromise: (args?: unknown[]) => void; let sidebarIsReadyPromise = new Promise((resolve) => { @@ -239,6 +233,7 @@ type ActorDetails = { function getOptionData( report: Report, reportActions: Record, + personalDetails: Record, preferredLocale: ValueOf, policy: Policy, parentReportAction: ReportAction, @@ -246,7 +241,6 @@ function getOptionData( // When a user signs out, Onyx is cleared. Due to the lazy rendering with a virtual list, it's possible for // this method to be called after the Onyx data has been cleared out. In that case, it's fine to do // a null check here and return early. - const personalDetails = allPersonalDetails; if (!report || !personalDetails) { return; } From b1257f53c4c2cb4df10069874992f3b61333dabc Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 6 Dec 2023 15:08:01 +0500 Subject: [PATCH 26/50] fix: ts issues --- tests/perf-test/SidebarUtils.perf-test.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/perf-test/SidebarUtils.perf-test.ts b/tests/perf-test/SidebarUtils.perf-test.ts index 595f6ad1bbe3..7f9957232cfb 100644 --- a/tests/perf-test/SidebarUtils.perf-test.ts +++ b/tests/perf-test/SidebarUtils.perf-test.ts @@ -3,10 +3,12 @@ import {measureFunction} from 'reassure'; import SidebarUtils from '@libs/SidebarUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import {PersonalDetails} from '@src/types/onyx'; import Policy from '@src/types/onyx/Policy'; import Report from '@src/types/onyx/Report'; import ReportAction, {ReportActions} from '@src/types/onyx/ReportAction'; import createCollection from '../utils/collections/createCollection'; +import createPersonalDetails from '../utils/collections/personalDetails'; import createRandomPolicy from '../utils/collections/policies'; import createRandomReportAction from '../utils/collections/reportActions'; import createRandomReport from '../utils/collections/reports'; @@ -36,6 +38,11 @@ const reportActions = createCollection( (index) => createRandomReportAction(index), ); +const personalDetails = createCollection( + (item) => item.accountID, + (index) => createPersonalDetails(index), +); + const mockedResponseMap = getMockedReports(5000) as Record<`${typeof ONYXKEYS.COLLECTION.REPORT}`, Report>; const runs = CONST.PERFORMANCE_TESTS.RUNS; @@ -50,7 +57,7 @@ test('getOptionData on 5k reports', async () => { }); await waitForBatchedUpdates(); - await measureFunction(() => SidebarUtils.getOptionData(report, reportActions, preferredLocale, policy, parentReportAction), {runs}); + await measureFunction(() => SidebarUtils.getOptionData(report, reportActions, personalDetails, preferredLocale, policy, parentReportAction), {runs}); }); test('getOrderedReportIDs on 5k reports', async () => { From 21749f1dc0b3921b634a9af5f4688308bc16d9c9 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 7 Dec 2023 15:59:59 +0500 Subject: [PATCH 27/50] fix: personaldetails not updating --- src/components/ArchivedReportFooter.tsx | 16 ++++++--- src/components/AvatarWithDisplayName.tsx | 13 ++++++-- src/libs/OptionsListUtils.js | 16 +++------ src/libs/PersonalDetailsUtils.js | 33 ++----------------- src/libs/ReportUtils.ts | 10 +++--- src/libs/SidebarUtils.ts | 4 +-- src/libs/actions/Task.js | 2 +- src/pages/ReportDetailsPage.js | 2 +- src/pages/ReportParticipantsPage.js | 2 +- .../ReportActionCompose.js | 11 ++++--- .../ReportActionCompose/SuggestionMention.js | 9 ++--- src/pages/home/report/ReportActionItem.js | 11 ++++--- src/pages/home/report/ReportActionsList.js | 11 +++++-- 13 files changed, 60 insertions(+), 80 deletions(-) diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx index 4f6de9a1db1b..74901843a05c 100644 --- a/src/components/ArchivedReportFooter.tsx +++ b/src/components/ArchivedReportFooter.tsx @@ -8,12 +8,15 @@ import * as ReportUtils from '@libs/ReportUtils'; import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Report, ReportAction} from '@src/types/onyx'; +import type {PersonalDetails, Report, ReportAction} from '@src/types/onyx'; import Banner from './Banner'; type ArchivedReportFooterOnyxProps = { /** The reason this report was archived */ reportClosedAction: OnyxEntry; + + /** Personal details of all users */ + personalDetails: OnyxEntry>; }; type ArchivedReportFooterProps = ArchivedReportFooterOnyxProps & { @@ -21,20 +24,20 @@ type ArchivedReportFooterProps = ArchivedReportFooterOnyxProps & { report: Report; }; -function ArchivedReportFooter({report, reportClosedAction}: ArchivedReportFooterProps) { +function ArchivedReportFooter({report, reportClosedAction, personalDetails = CONST.EMPTY_OBJECT}: ArchivedReportFooterProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const originalMessage = reportClosedAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED ? reportClosedAction.originalMessage : null; const archiveReason = originalMessage?.reason ?? CONST.REPORT.ARCHIVE_REASON.DEFAULT; - let displayName = PersonalDetailsUtils.getDisplayNameOrDefault([report.ownerAccountID, 'displayName']); + let displayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [report.ownerAccountID, 'displayName']); let oldDisplayName: string | undefined; if (archiveReason === CONST.REPORT.ARCHIVE_REASON.ACCOUNT_MERGED) { const newAccountID = originalMessage?.newAccountID; const oldAccountID = originalMessage?.oldAccountID; - displayName = PersonalDetailsUtils.getDisplayNameOrDefault([newAccountID, 'displayName']); - oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault([oldAccountID, 'displayName']); + displayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [newAccountID, 'displayName']); + oldDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [oldAccountID, 'displayName']); } const shouldRenderHTML = archiveReason !== CONST.REPORT.ARCHIVE_REASON.DEFAULT; @@ -68,6 +71,9 @@ function ArchivedReportFooter({report, reportClosedAction}: ArchivedReportFooter ArchivedReportFooter.displayName = 'ArchivedReportFooter'; export default withOnyx({ + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, reportClosedAction: { key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, canEvict: false, diff --git a/src/components/AvatarWithDisplayName.tsx b/src/components/AvatarWithDisplayName.tsx index 76b878988e08..6ffc7bdadd8b 100644 --- a/src/components/AvatarWithDisplayName.tsx +++ b/src/components/AvatarWithDisplayName.tsx @@ -11,7 +11,7 @@ import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import {Policy, Report, ReportActions} from '@src/types/onyx'; +import {PersonalDetails, Policy, Report, ReportActions} from '@src/types/onyx'; import DisplayNames from './DisplayNames'; import MultipleAvatars from './MultipleAvatars'; import ParentNavigationSubtitle from './ParentNavigationSubtitle'; @@ -22,6 +22,9 @@ import Text from './Text'; type AvatarWithDisplayNamePropsWithOnyx = { /** All of the actions of the report */ parentReportActions: OnyxEntry; + + /** Personal details of all users */ + personalDetails: OnyxEntry>; }; type AvatarWithDisplayNameProps = AvatarWithDisplayNamePropsWithOnyx & { @@ -48,6 +51,7 @@ function AvatarWithDisplayName({ isAnonymous = false, size = CONST.AVATAR_SIZE.DEFAULT, shouldEnableDetailPageNavigation = false, + personalDetails = CONST.EMPTY_OBJECT, }: AvatarWithDisplayNameProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -55,8 +59,8 @@ function AvatarWithDisplayName({ const subtitle = ReportUtils.getChatRoomSubtitle(report); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(report); const isMoneyRequestOrReport = ReportUtils.isMoneyRequestReport(report) || ReportUtils.isMoneyRequest(report); - const icons = ReportUtils.getIcons(report, null, '', -1, policy); - const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(report?.ownerAccountID ? [report.ownerAccountID] : []); + const icons = ReportUtils.getIcons(report, personalDetails, null, '', -1, policy); + const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(report?.ownerAccountID ? [report.ownerAccountID] : [], personalDetails); const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(Object.values(ownerPersonalDetails), false); const shouldShowSubscriptAvatar = ReportUtils.shouldReportShowSubscript(report); const isExpenseRequest = ReportUtils.isExpenseRequest(report); @@ -175,4 +179,7 @@ export default withOnyx `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : '0'}`, canEvict: false, }, + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, })(AvatarWithDisplayName); diff --git a/src/libs/OptionsListUtils.js b/src/libs/OptionsListUtils.js index 05214f2cd1b7..4678ce395f76 100644 --- a/src/libs/OptionsListUtils.js +++ b/src/libs/OptionsListUtils.js @@ -152,12 +152,11 @@ function getAvatarsForAccountIDs(accountIDs, personalDetails, defaultValues = {} * Returns the personal details for an array of accountIDs * * @param {Array} accountIDs - * @param {Object | null} passedPersonalDetails + * @param {Object | null} personalDetails * @returns {Object} – keys of the object are emails, values are PersonalDetails objects. */ -function getPersonalDetailsForAccountIDs(accountIDs, passedPersonalDetails = null) { +function getPersonalDetailsForAccountIDs(accountIDs, personalDetails) { const personalDetailsForAccountIDs = {}; - const personalDetails = passedPersonalDetails || allPersonalDetails; if (!personalDetails) { return personalDetailsForAccountIDs; } @@ -516,7 +515,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { (lastReportActions[report.reportID] && lastReportActions[report.reportID].originalMessage && lastReportActions[report.reportID].originalMessage.reason) || CONST.REPORT.ARCHIVE_REASON.DEFAULT; lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { - displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault('displayName', '', lastActorDetails), + displayName: archiveReason.displayName || PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails, 'displayName'), policyName: ReportUtils.getPolicyName(report), }); } @@ -548,14 +547,7 @@ function createOption(accountIDs, personalDetails, report, reportActions = {}, { result.text = reportName; result.searchText = getSearchText(report, reportName, personalDetailList, result.isChatRoom || result.isPolicyExpenseChat, result.isThread); - result.icons = ReportUtils.getIcons( - report, - UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), - personalDetail.login, - personalDetail.accountID, - undefined, - personalDetails, - ); + result.icons = ReportUtils.getIcons(report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), personalDetail.login, personalDetail.accountID); result.subtitle = subtitle; return result; diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 3205e01c79c2..838e1671c873 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -17,12 +17,12 @@ Onyx.connect({ }); /** + * @param {Object | Null} passedPersonalDetails * @param {Array | String} pathToDisplayName * @param {String} [defaultValue] optional default display name value - * @param {Object | Null} passedPersonalDetails optional default personal details object * @returns {String} */ -function getDisplayNameOrDefault(pathToDisplayName, defaultValue = '', passedPersonalDetails = allPersonalDetails) { +function getDisplayNameOrDefault(passedPersonalDetails, pathToDisplayName, defaultValue = '') { const displayName = lodashGet(passedPersonalDetails, pathToDisplayName); return displayName || defaultValue || Localize.translateLocal('common.hidden'); @@ -197,24 +197,6 @@ function getFormattedAddress(privatePersonalDetails) { return formattedAddress.trim().replace(/,$/, ''); } -/** - * Get personal detail for an accountID - * @param {Number} accountID - * @returns {PersonalDetail} personal detail object - */ -function getPersonalDetailsByAccountID(accountID) { - return allPersonalDetails ? allPersonalDetails[accountID] : {}; -} - -/** - * Get whispered personal details for array of accountIDs - * @param {Array} whisperedToAccountIDs - * @returns {PersonalDetails} personal details - */ -function getWhisperedToPersonalDetails(whisperedToAccountIDs) { - return _.filter(allPersonalDetails, (details) => _.includes(whisperedToAccountIDs, details.accountID)); -} - /** * Whether personal details is empty * @returns {Boolean} true if personal details is empty @@ -223,14 +205,6 @@ function isPersonalDetailsEmpty() { return !personalDetails.length; } -/** - * Get personal details object - * @returns {PersonalDetail} personal detail object - */ -function getPersonalDetails() { - return allPersonalDetails || {}; -} - export { getDisplayNameOrDefault, getPersonalDetailsByIDs, @@ -238,10 +212,7 @@ export { getLoginsByAccountIDs, getNewPersonalDetailsOnyxData, getFormattedAddress, - getPersonalDetailsByAccountID, - getWhisperedToPersonalDetails, isPersonalDetailsEmpty, - getPersonalDetails, getFormattedStreet, getStreetLines, }; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 1f84075c81c8..9eb66f898d9a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1130,10 +1130,10 @@ function getReportRecipientAccountIDs(report: OnyxEntry, currentLoginAcc /** * Whether the time row should be shown for a report. */ -function canShowReportRecipientLocalTime(report: OnyxEntry, accountID: number): boolean { +function canShowReportRecipientLocalTime(personalDetails: OnyxCollection, report: OnyxEntry, accountID: number): boolean { const reportRecipientAccountIDs = getReportRecipientAccountIDs(report, accountID); const hasMultipleParticipants = reportRecipientAccountIDs.length > 1; - const reportRecipient = allPersonalDetails?.[reportRecipientAccountIDs[0]]; + const reportRecipient = personalDetails?.[reportRecipientAccountIDs[0]]; const reportRecipientTimezone = reportRecipient?.timezone ?? CONST.DEFAULT_TIME_ZONE; const isReportParticipantValidated = reportRecipient?.validated ?? false; return Boolean(!hasMultipleParticipants && !isChatRoom(report) && !isPolicyExpenseChat(report) && reportRecipient && reportRecipientTimezone?.selected && isReportParticipantValidated); @@ -1178,10 +1178,9 @@ function getWorkspaceAvatar(report: OnyxEntry): UserUtils.AvatarSource { * Returns the appropriate icons for the given chat report using the stored personalDetails. * The Avatar sources can be URLs or Icon components according to the chat type. */ -function getIconsForParticipants(participants: number[], passedPersonalDetails: OnyxCollection): Icon[] { +function getIconsForParticipants(participants: number[], personalDetails: OnyxCollection): Icon[] { const participantDetails: ParticipantDetails[] = []; const participantsList = participants || []; - const personalDetails = passedPersonalDetails ?? allPersonalDetails; for (const accountID of participantsList) { const avatarSource = UserUtils.getAvatar(personalDetails?.[accountID]?.avatar ?? '', accountID); @@ -1243,11 +1242,11 @@ function getWorkspaceIcon(report: OnyxEntry, policy: OnyxEntry = */ function getIcons( report: OnyxEntry, + personalDetails: OnyxCollection = allPersonalDetails, defaultIcon: UserUtils.AvatarSource | null = null, defaultName = '', defaultAccountID = -1, policy: OnyxEntry = null, - passedPersonalDetails: OnyxCollection = allPersonalDetails, ): Icon[] { if (isEmptyObject(report)) { const fallbackIcon: Icon = { @@ -1258,7 +1257,6 @@ function getIcons( }; return [fallbackIcon]; } - const personalDetails = passedPersonalDetails; if (isExpenseRequest(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); const workspaceIcon = getWorkspaceIcon(report, policy); diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 9872187fa050..bace29e06d28 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -348,7 +348,7 @@ function getOptionData( case CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED: { lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { policyName: ReportUtils.getPolicyName(report, false, policy), - displayName: PersonalDetailsUtils.getDisplayNameOrDefault('displayName', '', lastActorDetails), + displayName: PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails, 'displayName'), }); break; } @@ -436,7 +436,7 @@ function getOptionData( result.subtitle = subtitle; result.participantsList = participantPersonalDetailList; - result.icons = ReportUtils.getIcons(report, UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), '', -1, policy, personalDetails); + result.icons = ReportUtils.getIcons(report, personalDetails, UserUtils.getAvatar(personalDetail.avatar, personalDetail.accountID), '', -1, policy); result.searchText = OptionsListUtils.getSearchText(report, reportName, participantPersonalDetailList, result.isChatRoom || result.isPolicyExpenseChat, result.isThread); result.displayNamesWithTooltips = displayNamesWithTooltips; diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index b12231e26e8f..07a03ed2588a 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -698,7 +698,7 @@ function getShareDestination(reportID, reports, personalDetails) { subtitle = ReportUtils.getChatRoomSubtitle(report); } return { - icons: ReportUtils.getIcons(report, Expensicons.FallbackAvatar, '', -1, undefined, personalDetails), + icons: ReportUtils.getIcons(report, personalDetails, Expensicons.FallbackAvatar, '', -1), displayName: ReportUtils.getReportName(report), subtitle, displayNamesWithTooltips, diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 25dfaddd0e22..563638e7bc78 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -162,7 +162,7 @@ function ReportDetailsPage(props) { return ReportUtils.getDisplayNamesWithTooltips(OptionsListUtils.getPersonalDetailsForAccountIDs(participants, props.personalDetails), hasMultipleParticipants); }, [participants, props.personalDetails]); - const icons = useMemo(() => ReportUtils.getIcons(props.report, null, '', -1, policy, props.personalDetails), [props.report, props.personalDetails, policy]); + const icons = useMemo(() => ReportUtils.getIcons(props.report, props.personalDetails, null, '', -1, policy), [props.report, props.personalDetails, policy]); const chatRoomSubtitleText = chatRoomSubtitle ? ( .map((accountID, index) => { const userPersonalDetail = lodashGet(personalDetails, accountID, {displayName: personalDetails.displayName || translate('common.hidden'), avatar: ''}); const userLogin = LocalePhoneNumber.formatPhoneNumber(userPersonalDetail.login || '') || translate('common.hidden'); - const displayName = PersonalDetailsUtils.getDisplayNameOrDefault('displayName', '', userPersonalDetail); + const displayName = PersonalDetailsUtils.getDisplayNameOrDefault(userPersonalDetail, 'displayName'); return { alternateText: userLogin, diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index 6a53666a8cae..958fc91ae1e7 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -10,14 +10,13 @@ import AttachmentModal from '@components/AttachmentModal'; import ExceededCommentLength from '@components/ExceededCommentLength'; import OfflineIndicator from '@components/OfflineIndicator'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import {withNetwork} from '@components/OnyxProvider'; +import {usePersonalDetails, withNetwork} from '@components/OnyxProvider'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useWindowDimensions from '@hooks/useWindowDimensions'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; import compose from '@libs/compose'; import getModalState from '@libs/getModalState'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import willBlurTextInputOnTapOutsideFunc from '@libs/willBlurTextInputOnTapOutside'; @@ -108,6 +107,7 @@ function ReportActionCompose({ const {translate} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); const actionButtonRef = useRef(null); + const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; /** * Updates the Highlight state of the composer @@ -144,7 +144,10 @@ function ReportActionCompose({ [currentUserPersonalDetails.accountID, report], ); - const shouldShowReportRecipientLocalTime = useMemo(() => ReportUtils.canShowReportRecipientLocalTime(report) && !isComposerFullSize, [report, isComposerFullSize]); + const shouldShowReportRecipientLocalTime = useMemo( + () => ReportUtils.canShowReportRecipientLocalTime(personalDetails, report, currentUserPersonalDetails.accountID) && !isComposerFullSize, + [personalDetails, report, currentUserPersonalDetails.accountID, isComposerFullSize], + ); const includesConcierge = useMemo(() => ReportUtils.chatIncludesConcierge({participantAccountIDs: report.participantAccountIDs}), [report.participantAccountIDs]); const userBlockedFromConcierge = useMemo(() => User.isBlockedFromConcierge(blockedFromConcierge), [blockedFromConcierge]); @@ -296,7 +299,7 @@ function ReportActionCompose({ ); const reportRecipientAcountIDs = ReportUtils.getReportRecipientAccountIDs(report, currentUserPersonalDetails.accountID); - const reportRecipient = PersonalDetailsUtils.getPersonalDetailsByAccountID(reportRecipientAcountIDs[0]); + const reportRecipient = personalDetails[reportRecipientAcountIDs[0]]; const shouldUseFocusedColor = !isBlockedFromConcierge && !disabled && isFocused; const hasReportRecipient = _.isObject(reportRecipient) && !_.isEmpty(reportRecipient); diff --git a/src/pages/home/report/ReportActionCompose/SuggestionMention.js b/src/pages/home/report/ReportActionCompose/SuggestionMention.js index b4694e85d0ca..e55b96ad99f5 100644 --- a/src/pages/home/report/ReportActionCompose/SuggestionMention.js +++ b/src/pages/home/report/ReportActionCompose/SuggestionMention.js @@ -3,10 +3,10 @@ import React, {useCallback, useEffect, useImperativeHandle, useRef, useState} fr import _ from 'underscore'; import * as Expensicons from '@components/Icon/Expensicons'; import MentionSuggestions from '@components/MentionSuggestions'; +import {usePersonalDetails} from '@components/OnyxProvider'; import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; -import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as SuggestionsUtils from '@libs/SuggestionUtils'; import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; @@ -50,12 +50,7 @@ function SuggestionMention({ measureParentContainer, isComposerFocused, }) { - /** - * We only need the personalDetails once because as long as the - * user is in the ReportScreen, these details won't be changing, - * hence we don't have to use it with `withOnyx`. - */ - const personalDetails = PersonalDetailsUtils.getPersonalDetails(); + const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const {translate, formatPhoneNumber} = useLocalize(); const previousValue = usePrevious(value); const [suggestionValues, setSuggestionValues] = useState(defaultSuggestionsValues); diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index fd1c5c508b32..c306d8ab159d 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -12,7 +12,7 @@ import * as Expensicons from '@components/Icon/Expensicons'; import InlineSystemMessage from '@components/InlineSystemMessage'; import KYCWall from '@components/KYCWall'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import {withBlockedFromConcierge, withNetwork, withReportActionsDrafts} from '@components/OnyxProvider'; +import {usePersonalDetails, withBlockedFromConcierge, withNetwork, withReportActionsDrafts} from '@components/OnyxProvider'; import PressableWithSecondaryInteraction from '@components/PressableWithSecondaryInteraction'; import EmojiReactionsPropTypes from '@components/Reactions/EmojiReactionsPropTypes'; import ReportActionItemEmojiReactions from '@components/Reactions/ReportActionItemEmojiReactions'; @@ -137,6 +137,7 @@ const defaultProps = { function ReportActionItem(props) { const theme = useTheme(); const styles = useThemeStyles(); + const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; const [isContextMenuActive, setIsContextMenuActive] = useState(() => ReportActionContextMenu.isActiveReportAction(props.action.reportActionID)); const [isHidden, setIsHidden] = useState(false); @@ -369,7 +370,7 @@ function ReportActionItem(props) { ); } else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTQUEUED) { - const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault([props.report.ownerAccountID, 'displayName']); + const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [props.report.ownerAccountID, 'displayName']); const paymentType = lodashGet(props.action, 'originalMessage.paymentType', ''); const isSubmitterOfUnsettledReport = ReportUtils.isCurrentUserSubmitter(props.report.reportID) && !ReportUtils.isSettled(props.report.reportID); @@ -417,7 +418,7 @@ function ReportActionItem(props) { ); } else if (props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTDEQUEUED) { - const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(PersonalDetailsUtils.getPersonalDetails(), [props.report.ownerAccountID, 'displayName']); + const submitterDisplayName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, [props.report.ownerAccountID, 'displayName']); const amount = CurrencyUtils.convertToDisplayString(props.report.total, props.report.currency); children = ; @@ -524,7 +525,7 @@ function ReportActionItem(props) { numberOfReplies={numberOfThreadReplies} mostRecentReply={`${props.action.childLastVisibleActionCreated}`} isHovered={hovered} - icons={ReportUtils.getIconsForParticipants(oldestFourAccountIDs)} + icons={ReportUtils.getIconsForParticipants(oldestFourAccountIDs, personalDetails)} onSecondaryInteraction={showPopover} /> @@ -663,7 +664,7 @@ function ReportActionItem(props) { const isWhisper = whisperedToAccountIDs.length > 0; const isMultipleParticipant = whisperedToAccountIDs.length > 1; const isWhisperOnlyVisibleByUser = isWhisper && ReportUtils.isCurrentUserTheOnlyParticipant(whisperedToAccountIDs); - const whisperedToPersonalDetails = isWhisper ? PersonalDetailsUtils.getWhisperedToPersonalDetails(whisperedToAccountIDs) : []; + const whisperedToPersonalDetails = isWhisper ? _.filter(personalDetails, (details) => _.includes(whisperedToAccountIDs, details.accountID)) : []; const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedToPersonalDetails, isMultipleParticipant) : []; return ( [styles.chatContentScrollView, isLoadingNewerReportActions ? styles.chatContentScrollViewWithHeaderLoader : {}], @@ -475,4 +482,4 @@ ReportActionsList.propTypes = propTypes; ReportActionsList.defaultProps = defaultProps; ReportActionsList.displayName = 'ReportActionsList'; -export default withWindowDimensions(ReportActionsList); +export default compose(withWindowDimensions, withCurrentUserPersonalDetails)(ReportActionsList); From 411b92ce8118d70a1e2602f500ca1bc8fb41bb69 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 7 Dec 2023 16:41:56 +0500 Subject: [PATCH 28/50] fix: personalDetails not updating in AnonymousReportFooter --- src/components/AnonymousReportFooter.tsx | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/components/AnonymousReportFooter.tsx b/src/components/AnonymousReportFooter.tsx index b706bd9e8747..0414c67a094a 100644 --- a/src/components/AnonymousReportFooter.tsx +++ b/src/components/AnonymousReportFooter.tsx @@ -1,15 +1,22 @@ import React from 'react'; import {Text, View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; import {OnyxEntry} from 'react-native-onyx/lib/types'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@styles/useThemeStyles'; import * as Session from '@userActions/Session'; -import {Report} from '@src/types/onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; +import {Policy, Report} from '@src/types/onyx'; import AvatarWithDisplayName from './AvatarWithDisplayName'; import Button from './Button'; import ExpensifyWordmark from './ExpensifyWordmark'; -type AnonymousReportFooterProps = { +type AnonymousReportFooterPropsWithOnyx = { + /** The policy which the user has access to and which the report is tied to */ + policy: OnyxEntry; +}; + +type AnonymousReportFooterProps = AnonymousReportFooterPropsWithOnyx & { /** The report currently being looked at */ report: OnyxEntry; @@ -17,7 +24,7 @@ type AnonymousReportFooterProps = { isSmallSizeLayout?: boolean; }; -function AnonymousReportFooter({isSmallSizeLayout = false, report}: AnonymousReportFooterProps) { +function AnonymousReportFooter({isSmallSizeLayout = false, report, policy = {}}: AnonymousReportFooterProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -28,6 +35,7 @@ function AnonymousReportFooter({isSmallSizeLayout = false, report}: AnonymousRep report={report} isAnonymous shouldEnableDetailPageNavigation + policy={policy} /> @@ -52,4 +60,8 @@ function AnonymousReportFooter({isSmallSizeLayout = false, report}: AnonymousRep AnonymousReportFooter.displayName = 'AnonymousReportFooter'; -export default AnonymousReportFooter; +export default withOnyx({ + policy: { + key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`, + }, +})(AnonymousReportFooter); From 55c900fd7497e87bc76f1504126b0fd9ce82b2ef Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 7 Dec 2023 16:45:16 +0500 Subject: [PATCH 29/50] refactor: cleanup --- src/pages/home/ReportScreen.js | 1 - tests/ui/UnreadIndicatorsTest.js | 1 - 2 files changed, 2 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 779ea3d22a8f..06532af0e817 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -506,7 +506,6 @@ export default compose( _.isEqual(prevProps.betas, nextProps.betas) && _.isEqual(prevProps.policies, nextProps.policies) && prevProps.accountManagerReportID === nextProps.accountManagerReportID && - _.isEqual(prevProps.personalDetails, nextProps.personalDetails) && prevProps.userLeavingStatus === nextProps.userLeavingStatus && prevProps.report.reportID === nextProps.report.reportID && prevProps.report.policyID === nextProps.report.policyID && diff --git a/tests/ui/UnreadIndicatorsTest.js b/tests/ui/UnreadIndicatorsTest.js index c6ead318a0db..ed44d3088ae0 100644 --- a/tests/ui/UnreadIndicatorsTest.js +++ b/tests/ui/UnreadIndicatorsTest.js @@ -530,7 +530,6 @@ describe('Unread Indicators', () => { lastActorAccountID: lastReportAction.actorAccountID, reportID: REPORT_ID, }); - return waitForBatchedUpdates(); }) .then(() => { From d8c895a98aba59dbc48e833d52c2681164c5216b Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 7 Dec 2023 16:56:44 +0500 Subject: [PATCH 30/50] refactor: cleanup --- src/components/ArchivedReportFooter.tsx | 2 +- src/libs/actions/Task.js | 2 +- src/pages/home/report/ReportActionItem.js | 1 - src/pages/home/report/ReportActionItemSingle.js | 3 +-- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx index 74901843a05c..3187bf3604e8 100644 --- a/src/components/ArchivedReportFooter.tsx +++ b/src/components/ArchivedReportFooter.tsx @@ -24,7 +24,7 @@ type ArchivedReportFooterProps = ArchivedReportFooterOnyxProps & { report: Report; }; -function ArchivedReportFooter({report, reportClosedAction, personalDetails = CONST.EMPTY_OBJECT}: ArchivedReportFooterProps) { +function ArchivedReportFooter({report, reportClosedAction, personalDetails = {}}: ArchivedReportFooterProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 07a03ed2588a..bf816d0a62a7 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -698,7 +698,7 @@ function getShareDestination(reportID, reports, personalDetails) { subtitle = ReportUtils.getChatRoomSubtitle(report); } return { - icons: ReportUtils.getIcons(report, personalDetails, Expensicons.FallbackAvatar, '', -1), + icons: ReportUtils.getIcons(report, personalDetails, Expensicons.FallbackAvatar), displayName: ReportUtils.getReportName(report), subtitle, displayNamesWithTooltips, diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index c306d8ab159d..92bb370155c9 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -138,7 +138,6 @@ function ReportActionItem(props) { const theme = useTheme(); const styles = useThemeStyles(); const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; - const [isContextMenuActive, setIsContextMenuActive] = useState(() => ReportActionContextMenu.isActiveReportAction(props.action.reportActionID)); const [isHidden, setIsHidden] = useState(false); const [moderationDecision, setModerationDecision] = useState(CONST.MODERATION.MODERATOR_DECISION_APPROVED); diff --git a/src/pages/home/report/ReportActionItemSingle.js b/src/pages/home/report/ReportActionItemSingle.js index 11fa212b329a..b13d57ad2976 100644 --- a/src/pages/home/report/ReportActionItemSingle.js +++ b/src/pages/home/report/ReportActionItemSingle.js @@ -82,7 +82,6 @@ function ReportActionItemSingle(props) { const theme = useTheme(); const styles = useThemeStyles(); const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; - const actorAccountID = props.action.actionName === CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && props.iouReport ? props.iouReport.managerID : props.action.actorAccountID; let displayName = ReportUtils.getDisplayNameForParticipant(actorAccountID); const {avatar, login, pendingFields, status, fallbackIcon} = personalDetails[actorAccountID] || {}; @@ -122,7 +121,7 @@ function ReportActionItemSingle(props) { }; } else if (!isWorkspaceActor) { const avatarIconIndex = props.report.isOwnPolicyExpenseChat || ReportUtils.isPolicyExpenseChat(props.report) ? 0 : 1; - const reportIcons = ReportUtils.getIcons(props.report); + const reportIcons = ReportUtils.getIcons(props.report, {}); secondaryAvatar = reportIcons[avatarIconIndex]; } From 3036ef8fc83f2cd6450cfc0964afa27bc1cb9076 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 7 Dec 2023 16:57:26 +0500 Subject: [PATCH 31/50] refactor: move personalDetails to LHNOptionsList --- .../LHNOptionsList/LHNOptionsList.js | 13 ++++++++++-- .../LHNOptionsList/OptionRowLHNData.js | 21 ++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/components/LHNOptionsList/LHNOptionsList.js b/src/components/LHNOptionsList/LHNOptionsList.js index b29db17a91b1..8febd7f247d6 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.js +++ b/src/components/LHNOptionsList/LHNOptionsList.js @@ -5,6 +5,7 @@ import React, {useCallback} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import participantPropTypes from '@components/participantPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import withCurrentReportID, {withCurrentReportIDDefaultProps, withCurrentReportIDPropTypes} from '@components/withCurrentReportID'; import compose from '@libs/compose'; @@ -57,6 +58,9 @@ const propTypes = { /** Indicates which locale the user currently has selected */ preferredLocale: PropTypes.string, + /** List of users' personal details */ + personalDetails: PropTypes.objectOf(participantPropTypes), + /** The transaction from the parent report action */ transactions: PropTypes.objectOf(transactionPropTypes), /** List of draft comments */ @@ -71,6 +75,7 @@ const defaultProps = { reports: {}, policy: {}, preferredLocale: CONST.LOCALES.DEFAULT, + personalDetails: {}, transactions: {}, draftComments: {}, ...withCurrentReportIDDefaultProps, @@ -89,6 +94,7 @@ function LHNOptionsList({ reportActions, policy, preferredLocale, + personalDetails, transactions, draftComments, currentReportID, @@ -114,7 +120,7 @@ function LHNOptionsList({ const itemComment = draftComments[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] || ''; const participants = [...ReportUtils.getParticipantsIDs(itemFullReport), itemFullReport.ownerAccountID]; - const participantsPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants); + const participantsPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(participants, personalDetails); return ( ); }, - [currentReportID, draftComments, onSelectRow, optionMode, policy, preferredLocale, reportActions, reports, shouldDisableFocusOptions, transactions], + [currentReportID, draftComments, onSelectRow, optionMode, personalDetails, policy, preferredLocale, reportActions, reports, shouldDisableFocusOptions, transactions], ); return ( @@ -174,6 +180,9 @@ export default compose( preferredLocale: { key: ONYXKEYS.NVP_PREFERRED_LOCALE, }, + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, transactions: { key: ONYXKEYS.COLLECTION.TRANSACTION, }, diff --git a/src/components/LHNOptionsList/OptionRowLHNData.js b/src/components/LHNOptionsList/OptionRowLHNData.js index b6c3a6e7ed00..e11bfc3cab98 100644 --- a/src/components/LHNOptionsList/OptionRowLHNData.js +++ b/src/components/LHNOptionsList/OptionRowLHNData.js @@ -2,7 +2,7 @@ import {deepEqual} from 'fast-equals'; import PropTypes from 'prop-types'; import React, {useEffect, useMemo, useRef} from 'react'; import _ from 'underscore'; -import {usePersonalDetails} from '@components/OnyxProvider'; +import participantPropTypes from '@components/participantPropTypes'; import transactionPropTypes from '@components/transactionPropTypes'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import SidebarUtils from '@libs/SidebarUtils'; @@ -16,6 +16,9 @@ const propTypes = { /** Whether row should be focused */ isFocused: PropTypes.bool, + /** List of users' personal details */ + personalDetails: PropTypes.objectOf(participantPropTypes), + /** The preferred language for the app */ preferredLocale: PropTypes.string, @@ -44,6 +47,7 @@ const propTypes = { const defaultProps = { isFocused: false, + personalDetails: {}, fullReport: {}, policy: {}, parentReportAction: {}, @@ -58,8 +62,19 @@ const defaultProps = { * The OptionRowLHN component is memoized, so it will only * re-render if the data really changed. */ -function OptionRowLHNData({isFocused, fullReport, reportActions, preferredLocale, comment, policy, receiptTransactions, parentReportAction, transaction, ...propsToForward}) { - const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; +function OptionRowLHNData({ + isFocused, + fullReport, + reportActions, + personalDetails, + preferredLocale, + comment, + policy, + receiptTransactions, + parentReportAction, + transaction, + ...propsToForward +}) { const reportID = propsToForward.reportID; const optionItemRef = useRef(); From 18cd5fe1571019892ca5840d449bba56ad548d6b Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 7 Dec 2023 17:22:50 +0500 Subject: [PATCH 32/50] fix: typescript issues --- src/components/AnonymousReportFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AnonymousReportFooter.tsx b/src/components/AnonymousReportFooter.tsx index 0414c67a094a..9c396db8e517 100644 --- a/src/components/AnonymousReportFooter.tsx +++ b/src/components/AnonymousReportFooter.tsx @@ -24,7 +24,7 @@ type AnonymousReportFooterProps = AnonymousReportFooterPropsWithOnyx & { isSmallSizeLayout?: boolean; }; -function AnonymousReportFooter({isSmallSizeLayout = false, report, policy = {}}: AnonymousReportFooterProps) { +function AnonymousReportFooter({isSmallSizeLayout = false, report, policy}: AnonymousReportFooterProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); From c632b7ce204f9abd1d23d0b66f5b6b37e60dbd94 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Tue, 19 Dec 2023 13:15:52 +0500 Subject: [PATCH 33/50] fix: typecheck for AvatarWithDisplayName --- src/components/AvatarWithDisplayName.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/AvatarWithDisplayName.tsx b/src/components/AvatarWithDisplayName.tsx index a409da9ea068..db6608fb1f32 100644 --- a/src/components/AvatarWithDisplayName.tsx +++ b/src/components/AvatarWithDisplayName.tsx @@ -11,7 +11,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import {PersonalDetails, Policy, Report, ReportActions} from '@src/types/onyx'; +import {PersonalDetailsList, Policy, Report, ReportActions} from '@src/types/onyx'; import DisplayNames from './DisplayNames'; import MultipleAvatars from './MultipleAvatars'; import ParentNavigationSubtitle from './ParentNavigationSubtitle'; @@ -24,7 +24,7 @@ type AvatarWithDisplayNamePropsWithOnyx = { parentReportActions: OnyxEntry; /** Personal details of all users */ - personalDetails: OnyxEntry>; + personalDetails: OnyxEntry; }; type AvatarWithDisplayNameProps = AvatarWithDisplayNamePropsWithOnyx & { @@ -181,6 +181,6 @@ export default withOnyx Date: Tue, 19 Dec 2023 16:00:04 +0500 Subject: [PATCH 34/50] fix: linting --- src/components/AvatarWithDisplayName.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AvatarWithDisplayName.tsx b/src/components/AvatarWithDisplayName.tsx index db6608fb1f32..24beb7f1f16d 100644 --- a/src/components/AvatarWithDisplayName.tsx +++ b/src/components/AvatarWithDisplayName.tsx @@ -181,6 +181,6 @@ export default withOnyx Date: Wed, 10 Jan 2024 16:45:14 +0500 Subject: [PATCH 35/50] fix: paste not working on web --- .../home/report/ReportActionCompose/ReportActionCompose.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index fb9ad6c55649..ab9c989473af 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -343,8 +343,8 @@ function ReportActionCompose({ runOnJS(submitForm)(); }, [isSendDisabled, resetFullComposerSize, submitForm, isReportReadyForDisplay]); - const onDisplayFileInModal = () => { - attachementModalRef.current.displayFileInModal(); + const onDisplayFileInModal = (file) => { + attachementModalRef.current.displayFileInModal(file); }; return ( From 1ab6e1986a4cb14bdeff4e1355876d321b9e6841 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 11 Jan 2024 15:29:36 +0500 Subject: [PATCH 36/50] fix: compose height on mobile --- src/styles/index.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index a6ac2e269eb0..1aff94666fd0 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3,7 +3,7 @@ import type {LineLayerStyleProps} from '@rnmapbox/maps/src/utils/MapboxStyles'; import lodashClamp from 'lodash/clamp'; import type {LineLayer} from 'react-map-gl'; import type {AnimatableNumericValue, Animated, ImageStyle, TextStyle, ViewStyle} from 'react-native'; -import {StyleSheet} from 'react-native'; +import {Platform, StyleSheet} from 'react-native'; import type {CustomAnimation} from 'react-native-animatable'; import type {PickerStyle} from 'react-native-picker-select'; import type {MixedStyleDeclaration, MixedStyleRecord} from 'react-native-render-html'; @@ -1885,6 +1885,14 @@ const styles = (theme: ThemeColors) => height: 40, padding: 10, margin: 3, + ...Platform.select({ + ios: { + marginVertical: 0, + }, + android: { + marginVertical: 0, + }, + }), justifyContent: 'center', }, @@ -1951,9 +1959,17 @@ const styles = (theme: ThemeColors) => alignSelf: 'flex-end', borderRadius: variables.buttonBorderRadius, height: 40, - marginVertical: 3, paddingHorizontal: 10, justifyContent: 'center', + marginVertical: 3, + ...Platform.select({ + ios: { + marginVertical: 0, + }, + android: { + marginVertical: 0, + }, + }), }, editChatItemEmojiWrapper: { From 1139e31d1726e7d89a3f0e39e95714836019e430 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 11 Jan 2024 16:58:54 +0500 Subject: [PATCH 37/50] fix: revert platform specific styles --- src/styles/index.ts | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/styles/index.ts b/src/styles/index.ts index cac903f3c093..359b455fb3c3 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3,7 +3,7 @@ import type {LineLayerStyleProps} from '@rnmapbox/maps/src/utils/MapboxStyles'; import lodashClamp from 'lodash/clamp'; import type {LineLayer} from 'react-map-gl'; import type {AnimatableNumericValue, Animated, ImageStyle, TextStyle, ViewStyle} from 'react-native'; -import {Platform, StyleSheet} from 'react-native'; +import {StyleSheet} from 'react-native'; import type {CustomAnimation} from 'react-native-animatable'; import type {PickerStyle} from 'react-native-picker-select'; import type {MixedStyleDeclaration, MixedStyleRecord} from 'react-native-render-html'; @@ -1885,14 +1885,6 @@ const styles = (theme: ThemeColors) => height: 40, padding: 10, margin: 3, - ...Platform.select({ - ios: { - marginVertical: 0, - }, - android: { - marginVertical: 0, - }, - }), justifyContent: 'center', }, @@ -1962,14 +1954,6 @@ const styles = (theme: ThemeColors) => paddingHorizontal: 10, justifyContent: 'center', marginVertical: 3, - ...Platform.select({ - ios: { - marginVertical: 0, - }, - android: { - marginVertical: 0, - }, - }), }, editChatItemEmojiWrapper: { From 2c6f73ad44e10820cb754879dda6b25333c6ba33 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 11 Jan 2024 16:59:09 +0500 Subject: [PATCH 38/50] fix: extra compose height --- .../ComposerWithSuggestions.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js index ac4163c311e1..978b9bc69c97 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js @@ -597,20 +597,20 @@ function ComposerWithSuggestions({ onScroll={hideSuggestionMenu} shouldContainScroll={Browser.isMobileSafari()} /> + - {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( - replaceSelectionWithText(...args)} - emojiPickerID={reportID} - /> - )} - replaceSelectionWithText(...args)} + emojiPickerID={reportID} /> - + )} + Date: Tue, 16 Jan 2024 12:14:28 +0500 Subject: [PATCH 39/50] fix: reply not shown --- src/pages/home/report/ReportActionsListItemRenderer.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/home/report/ReportActionsListItemRenderer.js b/src/pages/home/report/ReportActionsListItemRenderer.js index 3c58c4e73935..00630a72d156 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.js +++ b/src/pages/home/report/ReportActionsListItemRenderer.js @@ -75,6 +75,8 @@ function ReportActionsListItemRenderer({ error: reportAction.error, created: reportAction.created, actorAccountID: reportAction.actorAccountID, + childVisibleActionCount: reportAction.childVisibleActionCount, + childOldestFourAccountIDs: reportAction.childOldestFourAccountIDs, }), [ reportAction.actionName, @@ -91,6 +93,8 @@ function ReportActionsListItemRenderer({ reportAction.reportActionID, reportAction.whisperedToAccountIDs, reportAction.actorAccountID, + reportAction.childVisibleActionCount, + reportAction.childOldestFourAccountIDs, ], ); From baf9b3e368b4d4e288ccd70d371bd8c0c3d62723 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 17 Jan 2024 12:19:41 +0500 Subject: [PATCH 40/50] fix: add missing reportAction properties --- .../report/ReportActionsListItemRenderer.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/pages/home/report/ReportActionsListItemRenderer.js b/src/pages/home/report/ReportActionsListItemRenderer.js index 00630a72d156..b28a90f496ce 100644 --- a/src/pages/home/report/ReportActionsListItemRenderer.js +++ b/src/pages/home/report/ReportActionsListItemRenderer.js @@ -77,6 +77,17 @@ function ReportActionsListItemRenderer({ actorAccountID: reportAction.actorAccountID, childVisibleActionCount: reportAction.childVisibleActionCount, childOldestFourAccountIDs: reportAction.childOldestFourAccountIDs, + childType: reportAction.childType, + person: reportAction.person, + isOptimisticAction: reportAction.isOptimisticAction, + delegateAccountID: reportAction.delegateAccountID, + previousMessage: reportAction.previousMessage, + attachmentInfo: reportAction.attachmentInfo, + childStateNum: reportAction.childStateNum, + childStatusNum: reportAction.childStatusNum, + childReportName: reportAction.childReportName, + childManagerAccountID: reportAction.childManagerAccountID, + childMoneyRequestCount: reportAction.childMoneyRequestCount, }), [ reportAction.actionName, @@ -95,6 +106,17 @@ function ReportActionsListItemRenderer({ reportAction.actorAccountID, reportAction.childVisibleActionCount, reportAction.childOldestFourAccountIDs, + reportAction.person, + reportAction.isOptimisticAction, + reportAction.childType, + reportAction.delegateAccountID, + reportAction.previousMessage, + reportAction.attachmentInfo, + reportAction.childStateNum, + reportAction.childStatusNum, + reportAction.childReportName, + reportAction.childManagerAccountID, + reportAction.childMoneyRequestCount, ], ); From 470eb7b0956ab8f4bb20b31066b0272f9fcde74f Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 18 Jan 2024 12:34:03 +0500 Subject: [PATCH 41/50] fix: hide reply and task welcome message and add the missing fields --- src/pages/home/report/ReportActionsView.js | 53 +++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 4bed2d7ffb86..990c5e44ff77 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -256,8 +256,59 @@ function ReportActionsView(props) { reportID: props.report.reportID, policyID: props.report.policyID, lastVisibleActionCreated: props.report.lastVisibleActionCreated, + statusNum: props.report.statusNum, + stateNum: props.report.stateNum, + writeCapability: props.report.writeCapability, + type: props.report.type, + errorFields: props.report.errorFields, + isPolicyExpenseChat: props.report.isPolicyExpenseChat, + parentReportID: props.report.parentReportID, + parentReportActionID: props.report.parentReportActionID, + chatType: props.report.chatType, + pendingFields: props.report.pendingFields, + isDeletedParentAction: props.report.isDeletedParentAction, + reportName: props.report.reportName, + description: props.report.description, + managerID: props.report.managerID, + total: props.report.total, + nonReimbursableTotal: props.report.nonReimbursableTotal, + reportFields: props.report.reportFields, + ownerAccountID: props.report.ownerAccountID, + currency: props.report.currency, + participantAccountIDs: props.report.participantAccountIDs, + isWaitingOnBankAccount: props.report.isWaitingOnBankAccount, + iouReportID: props.report.iouReportID, + isOwnPolicyExpenseChat: props.report.isOwnPolicyExpenseChat, }), - [props.report.lastReadTime, props.report.reportID, props.report.policyID, props.report.lastVisibleActionCreated], + [ + props.report.lastReadTime, + props.report.reportID, + props.report.policyID, + props.report.lastVisibleActionCreated, + props.report.statusNum, + props.report.stateNum, + props.report.writeCapability, + props.report.type, + props.report.errorFields, + props.report.isPolicyExpenseChat, + props.report.parentReportID, + props.report.parentReportActionID, + props.report.chatType, + props.report.pendingFields, + props.report.isDeletedParentAction, + props.report.reportName, + props.report.description, + props.report.managerID, + props.report.total, + props.report.nonReimbursableTotal, + props.report.reportFields, + props.report.ownerAccountID, + props.report.currency, + props.report.participantAccountIDs, + props.report.isWaitingOnBankAccount, + props.report.iouReportID, + props.report.isOwnPolicyExpenseChat, + ], ); // Comments have not loaded at all yet do nothing From efd81f5906e2b60d5f60abbed5d2ecdf57860f85 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Thu, 18 Jan 2024 12:34:25 +0500 Subject: [PATCH 42/50] refactor: remove not used prop --- src/pages/home/report/ReportActionItem.js | 1 - src/pages/home/report/ReportActionItemMessageEdit.tsx | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 9573d4a4ff1a..a59b9d268e76 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -502,7 +502,6 @@ function ReportActionItem(props) { reportID={props.report.reportID} index={props.index} ref={textInputRef} - report={props.report} // Avoid defining within component due to an existing Onyx bug preferredSkinTone={props.preferredSkinTone} shouldDisableEmojiPicker={ diff --git a/src/pages/home/report/ReportActionItemMessageEdit.tsx b/src/pages/home/report/ReportActionItemMessageEdit.tsx index 5934c4c333cb..e97aa0338f90 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.tsx +++ b/src/pages/home/report/ReportActionItemMessageEdit.tsx @@ -52,10 +52,6 @@ type ReportActionItemMessageEditProps = { /** Position index of the report action in the overall report FlatList view */ index: number; - /** The report currently being looked at */ - // eslint-disable-next-line react/no-unused-prop-types - report?: OnyxTypes.Report; - /** Whether or not the emoji picker is disabled */ shouldDisableEmojiPicker?: boolean; From 722910dad6685d350bd7ce11d3e641ce0bc0faf1 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Fri, 19 Jan 2024 16:07:06 +0500 Subject: [PATCH 43/50] fix: local time not displayed --- src/pages/home/report/ReportFooter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index 8f4906a32aa9..6e6a849658ae 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -134,6 +134,7 @@ function ReportFooter(props) { Date: Fri, 19 Jan 2024 16:10:09 +0500 Subject: [PATCH 44/50] refactor: move memoized report object to report screen --- src/pages/home/ReportScreen.js | 44 +++++++++++++- src/pages/home/report/ReportActionsView.js | 67 +--------------------- 2 files changed, 44 insertions(+), 67 deletions(-) diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index b238bcf74d8a..00136cea43b1 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -140,7 +140,7 @@ function getReportID(route) { function ReportScreen({ betas, route, - report, + report: reportProp, reportMetadata, reportActions, parentReportAction, @@ -162,6 +162,48 @@ function ReportScreen({ const firstRenderRef = useRef(true); const flatListRef = useRef(); const reactionListRef = useRef(); + /** + * Create a lightweight Report so as to keep the re-rendering as light as possible by + * passing in only the required props. + * + * Also, this plays nicely in contrast with Onyx, + * which creates a new object every time collection changes. Because of this we can't + * put this into onyx selector as it will be the same. + */ + const report = useMemo( + () => ({ + lastReadTime: reportProp.lastReadTime, + reportID: reportProp.reportID, + policyID: reportProp.policyID, + lastVisibleActionCreated: reportProp.lastVisibleActionCreated, + statusNum: reportProp.statusNum, + stateNum: reportProp.stateNum, + writeCapability: reportProp.writeCapability, + type: reportProp.type, + errorFields: reportProp.errorFields, + isPolicyExpenseChat: reportProp.isPolicyExpenseChat, + parentReportID: reportProp.parentReportID, + parentReportActionID: reportProp.parentReportActionID, + chatType: reportProp.chatType, + pendingFields: reportProp.pendingFields, + isDeletedParentAction: reportProp.isDeletedParentAction, + reportName: reportProp.reportName, + description: reportProp.description, + managerID: reportProp.managerID, + total: reportProp.total, + nonReimbursableTotal: reportProp.nonReimbursableTotal, + reportFields: reportProp.reportFields, + ownerAccountID: reportProp.ownerAccountID, + currency: reportProp.currency, + participantAccountIDs: reportProp.participantAccountIDs, + isWaitingOnBankAccount: reportProp.isWaitingOnBankAccount, + iouReportID: reportProp.iouReportID, + isOwnPolicyExpenseChat: reportProp.isOwnPolicyExpenseChat, + notificationPreference: reportProp.notificationPreference, + }), + [reportProp.lastReadTime, reportProp.reportID, reportProp.policyID, reportProp.lastVisibleActionCreated, reportProp.statusNum, reportProp.stateNum, reportProp.writeCapability, reportProp.type, reportProp.errorFields, reportProp.isPolicyExpenseChat, reportProp.parentReportID, reportProp.parentReportActionID, reportProp.chatType, reportProp.pendingFields, reportProp.isDeletedParentAction, reportProp.reportName, reportProp.description, reportProp.managerID, reportProp.total, reportProp.nonReimbursableTotal, reportProp.reportFields, reportProp.ownerAccountID, reportProp.currency, reportProp.participantAccountIDs, reportProp.isWaitingOnBankAccount, reportProp.iouReportID, reportProp.isOwnPolicyExpenseChat, reportProp.notificationPreference], + ); + const prevReport = usePrevious(report); const prevUserLeavingStatus = usePrevious(userLeavingStatus); const [isBannerVisible, setIsBannerVisible] = useState(true); diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 990c5e44ff77..9bd8551ba7b3 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -246,71 +246,6 @@ function ReportActionsView(props) { } }; - /** - * Create a lightweight Report so as to keep the re-rendering as light as possible by - * passing in only the required props. - */ - const report = useMemo( - () => ({ - lastReadTime: props.report.lastReadTime, - reportID: props.report.reportID, - policyID: props.report.policyID, - lastVisibleActionCreated: props.report.lastVisibleActionCreated, - statusNum: props.report.statusNum, - stateNum: props.report.stateNum, - writeCapability: props.report.writeCapability, - type: props.report.type, - errorFields: props.report.errorFields, - isPolicyExpenseChat: props.report.isPolicyExpenseChat, - parentReportID: props.report.parentReportID, - parentReportActionID: props.report.parentReportActionID, - chatType: props.report.chatType, - pendingFields: props.report.pendingFields, - isDeletedParentAction: props.report.isDeletedParentAction, - reportName: props.report.reportName, - description: props.report.description, - managerID: props.report.managerID, - total: props.report.total, - nonReimbursableTotal: props.report.nonReimbursableTotal, - reportFields: props.report.reportFields, - ownerAccountID: props.report.ownerAccountID, - currency: props.report.currency, - participantAccountIDs: props.report.participantAccountIDs, - isWaitingOnBankAccount: props.report.isWaitingOnBankAccount, - iouReportID: props.report.iouReportID, - isOwnPolicyExpenseChat: props.report.isOwnPolicyExpenseChat, - }), - [ - props.report.lastReadTime, - props.report.reportID, - props.report.policyID, - props.report.lastVisibleActionCreated, - props.report.statusNum, - props.report.stateNum, - props.report.writeCapability, - props.report.type, - props.report.errorFields, - props.report.isPolicyExpenseChat, - props.report.parentReportID, - props.report.parentReportActionID, - props.report.chatType, - props.report.pendingFields, - props.report.isDeletedParentAction, - props.report.reportName, - props.report.description, - props.report.managerID, - props.report.total, - props.report.nonReimbursableTotal, - props.report.reportFields, - props.report.ownerAccountID, - props.report.currency, - props.report.participantAccountIDs, - props.report.isWaitingOnBankAccount, - props.report.iouReportID, - props.report.isOwnPolicyExpenseChat, - ], - ); - // Comments have not loaded at all yet do nothing if (!_.size(props.reportActions)) { return null; @@ -319,7 +254,7 @@ function ReportActionsView(props) { return ( <> Date: Wed, 24 Jan 2024 12:52:48 +0500 Subject: [PATCH 45/50] fix: composer not being auto focused --- src/components/AttachmentModal.js | 15 +--- .../ReportActionCompose.js | 68 +++++++++---------- 2 files changed, 34 insertions(+), 49 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index fe0793bd7c5e..6e2e13a46dfa 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -2,7 +2,7 @@ import Str from 'expensify-common/lib/str'; import lodashExtend from 'lodash/extend'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; -import React, {forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useState} from 'react'; +import React, {memo, useCallback, useEffect, useMemo, useState} from 'react'; import {Animated, Keyboard, View} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; import {withOnyx} from 'react-native-onyx'; @@ -118,7 +118,7 @@ const defaultProps = { canEditReceipt: false, }; -const AttachmentModal = forwardRef((props, ref) => { +function AttachmentModal(props) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -415,14 +415,6 @@ const AttachmentModal = forwardRef((props, ref) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.isReceiptAttachment, props.parentReport, props.parentReportActions, props.policy, props.transaction, file, source]); - useImperativeHandle( - ref, - () => ({ - displayFileInModal: validateAndDisplayFileToUpload, - }), - [validateAndDisplayFileToUpload], - ); - // There are a few things that shouldn't be set until we absolutely know if the file is a receipt or an attachment. // props.isReceiptAttachment will be null until its certain what the file is, in which case it will then be true|false. let headerTitle = props.headerTitle; @@ -546,12 +538,11 @@ const AttachmentModal = forwardRef((props, ref) => { })} ); -}); +}; AttachmentModal.propTypes = propTypes; AttachmentModal.defaultProps = defaultProps; AttachmentModal.displayName = 'AttachmentModal'; - export default compose( withWindowDimensions, withLocalize, diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index 341cabe28cb9..2aa41e4f9d3d 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -158,7 +158,6 @@ function ReportActionCompose({ const suggestionsRef = useRef(null); const composerRef = useRef(null); - const attachementModalRef = useRef(null); const reportParticipantIDs = useMemo( () => _.without(lodashGet(report, 'participantAccountIDs', []), currentUserPersonalDetails.accountID), @@ -341,10 +340,6 @@ function ReportActionCompose({ runOnJS(submitForm)(); }, [isSendDisabled, resetFullComposerSize, submitForm, isReportReadyForDisplay]); - const onDisplayFileInModal = (file) => { - attachementModalRef.current.displayFileInModal(file); - }; - return ( @@ -368,7 +363,6 @@ function ReportActionCompose({ ]} > setIsAttachmentPreviewActive(true)} @@ -398,6 +392,37 @@ function ReportActionCompose({ onItemSelected={onItemSelected} actionButtonRef={actionButtonRef} /> + { if (isAttachmentPreviewActive) { @@ -410,37 +435,6 @@ function ReportActionCompose({ )} - Date: Wed, 24 Jan 2024 12:53:17 +0500 Subject: [PATCH 46/50] refactor: remove not needed code change --- src/pages/home/report/ReportFooter.js | 1 - src/styles/index.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportFooter.js b/src/pages/home/report/ReportFooter.js index 6e6a849658ae..1761e135481a 100644 --- a/src/pages/home/report/ReportFooter.js +++ b/src/pages/home/report/ReportFooter.js @@ -152,7 +152,6 @@ function ReportFooter(props) { ReportFooter.displayName = 'ReportFooter'; ReportFooter.propTypes = propTypes; ReportFooter.defaultProps = defaultProps; - export default compose( withWindowDimensions, withOnyx({ diff --git a/src/styles/index.ts b/src/styles/index.ts index 01d8b87edd15..aace13c34594 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1958,9 +1958,9 @@ const styles = (theme: ThemeColors) => alignSelf: 'flex-end', borderRadius: variables.buttonBorderRadius, height: 40, + marginVertical: 3, paddingHorizontal: 10, justifyContent: 'center', - marginVertical: 3, }, editChatItemEmojiWrapper: { From ddb5ef6961ae905785f1a1a98b789dacabd31524 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 24 Jan 2024 13:29:19 +0500 Subject: [PATCH 47/50] fix: linting --- src/components/AttachmentModal.js | 2 +- src/pages/home/ReportScreen.js | 33 +++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/components/AttachmentModal.js b/src/components/AttachmentModal.js index 6e2e13a46dfa..66b95e33eb82 100755 --- a/src/components/AttachmentModal.js +++ b/src/components/AttachmentModal.js @@ -538,7 +538,7 @@ function AttachmentModal(props) { })} ); -}; +} AttachmentModal.propTypes = propTypes; AttachmentModal.defaultProps = defaultProps; diff --git a/src/pages/home/ReportScreen.js b/src/pages/home/ReportScreen.js index 00136cea43b1..5c333278de78 100644 --- a/src/pages/home/ReportScreen.js +++ b/src/pages/home/ReportScreen.js @@ -165,7 +165,7 @@ function ReportScreen({ /** * Create a lightweight Report so as to keep the re-rendering as light as possible by * passing in only the required props. - * + * * Also, this plays nicely in contrast with Onyx, * which creates a new object every time collection changes. Because of this we can't * put this into onyx selector as it will be the same. @@ -201,7 +201,36 @@ function ReportScreen({ isOwnPolicyExpenseChat: reportProp.isOwnPolicyExpenseChat, notificationPreference: reportProp.notificationPreference, }), - [reportProp.lastReadTime, reportProp.reportID, reportProp.policyID, reportProp.lastVisibleActionCreated, reportProp.statusNum, reportProp.stateNum, reportProp.writeCapability, reportProp.type, reportProp.errorFields, reportProp.isPolicyExpenseChat, reportProp.parentReportID, reportProp.parentReportActionID, reportProp.chatType, reportProp.pendingFields, reportProp.isDeletedParentAction, reportProp.reportName, reportProp.description, reportProp.managerID, reportProp.total, reportProp.nonReimbursableTotal, reportProp.reportFields, reportProp.ownerAccountID, reportProp.currency, reportProp.participantAccountIDs, reportProp.isWaitingOnBankAccount, reportProp.iouReportID, reportProp.isOwnPolicyExpenseChat, reportProp.notificationPreference], + [ + reportProp.lastReadTime, + reportProp.reportID, + reportProp.policyID, + reportProp.lastVisibleActionCreated, + reportProp.statusNum, + reportProp.stateNum, + reportProp.writeCapability, + reportProp.type, + reportProp.errorFields, + reportProp.isPolicyExpenseChat, + reportProp.parentReportID, + reportProp.parentReportActionID, + reportProp.chatType, + reportProp.pendingFields, + reportProp.isDeletedParentAction, + reportProp.reportName, + reportProp.description, + reportProp.managerID, + reportProp.total, + reportProp.nonReimbursableTotal, + reportProp.reportFields, + reportProp.ownerAccountID, + reportProp.currency, + reportProp.participantAccountIDs, + reportProp.isWaitingOnBankAccount, + reportProp.iouReportID, + reportProp.isOwnPolicyExpenseChat, + reportProp.notificationPreference, + ], ); const prevReport = usePrevious(report); From 17a5cdab574002d9613756c8fe4d4b3ad596da5c Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 24 Jan 2024 17:53:06 +0500 Subject: [PATCH 48/50] fix: reassure tests --- .../ComposerWithSuggestions.js | 41 +++---------------- .../ReportActionCompose.js | 35 +++++++++++++--- 2 files changed, 35 insertions(+), 41 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js index f6a97316479f..883255c273f9 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.js @@ -3,10 +3,8 @@ import lodashGet from 'lodash/get'; import React, {memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import {findNodeHandle, InteractionManager, NativeModules, View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import {runOnJS, setNativeProps, useAnimatedRef} from 'react-native-reanimated'; import _ from 'underscore'; import Composer from '@components/Composer'; -import EmojiPickerButton from '@components/EmojiPicker/EmojiPickerButton'; import withKeyboardState from '@components/withKeyboardState'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; @@ -20,7 +18,6 @@ import compose from '@libs/compose'; import * as ComposerUtils from '@libs/ComposerUtils'; import getDraftComment from '@libs/ComposerUtils/getDraftComment'; import convertToLTRForComposer from '@libs/convertToLTRForComposer'; -import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as EmojiUtils from '@libs/EmojiUtils'; import focusComposerWithDelay from '@libs/focusComposerWithDelay'; import getPlatform from '@libs/getPlatform'; @@ -31,7 +28,6 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as SuggestionUtils from '@libs/SuggestionUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import willBlurTextInputOnTapOutsideFunc from '@libs/willBlurTextInputOnTapOutside'; -import SendButton from '@pages/home/report/ReportActionCompose/SendButton'; import SilentCommentUpdater from '@pages/home/report/ReportActionCompose/SilentCommentUpdater'; import Suggestions from '@pages/home/report/ReportActionCompose/Suggestions'; import * as EmojiPickerActions from '@userActions/EmojiPickerAction'; @@ -97,7 +93,7 @@ function ComposerWithSuggestions({ disabled, isFullComposerAvailable, setIsFullComposerAvailable, - isSendDisabled, + setIsCommentEmpty, handleSendMessage, shouldShowComposeInput, measureParentContainer, @@ -106,6 +102,7 @@ function ComposerWithSuggestions({ raiseIsScrollLikelyLayoutTriggered, // Refs suggestionsRef, + animatedRef, forwardedRef, isNextModalWillOpenRef, editFocused, @@ -121,8 +118,6 @@ function ComposerWithSuggestions({ const emojisPresentBefore = useRef([]); const draftComment = getDraftComment(reportID) || ''; - const [isCommentEmpty, setIsCommentEmpty] = useState(() => !draftComment || !!draftComment.match(/^(\s)*$/)); - const animatedRef = useAnimatedRef(); const [value, setValue] = useState(() => { if (draftComment) { emojisPresentBefore.current = EmojiUtils.extractEmojis(draftComment); @@ -131,7 +126,7 @@ function ComposerWithSuggestions({ }); const commentRef = useRef(value); - const {isSmallScreenWidth, isMediumScreenWidth} = useWindowDimensions(); + const {isSmallScreenWidth} = useWindowDimensions(); const maxComposerLines = isSmallScreenWidth ? CONST.COMPOSER.MAX_LINES_SMALL_SCREEN : CONST.COMPOSER.MAX_LINES; const parentReportAction = lodashGet(parentReportActions, [parentReportActionID]); @@ -196,19 +191,6 @@ function ComposerWithSuggestions({ [], ); - const sendMessage = useCallback(() => { - 'worklet'; - - if (isCommentEmpty) { - return; - } - - runOnJS(handleSendMessage)(); - // We are setting the isCommentEmpty flag to true so the status of it will be in sync of the native text input state - runOnJS(setIsCommentEmpty)(true); - setNativeProps(animatedRef, {text: ''}); // clears native text input on the UI thread - }, [animatedRef, handleSendMessage, isCommentEmpty]); - /** * Update the value of the comment in Onyx * @@ -357,7 +339,7 @@ function ComposerWithSuggestions({ // Submit the form when Enter is pressed if (e.key === CONST.KEYBOARD_SHORTCUTS.ENTER.shortcutKey && !e.shiftKey) { e.preventDefault(); - sendMessage(); + handleSendMessage(); } // Trigger the edit box for last sent message if ArrowUp is pressed and the comment is empty and Chronos is not in the participants @@ -369,7 +351,7 @@ function ComposerWithSuggestions({ } } }, - [isSmallScreenWidth, isKeyboardShown, suggestionsRef, includeChronos, sendMessage, lastReportAction, reportID], + [isSmallScreenWidth, isKeyboardShown, suggestionsRef, includeChronos, handleSendMessage, lastReportAction, reportID], ); const onChangeText = useCallback( @@ -600,19 +582,6 @@ function ComposerWithSuggestions({ /> - {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( - replaceSelectionWithText(...args)} - emojiPickerID={reportID} - /> - )} - - { + const draftComment = getDraftComment(reportID); + return !draftComment || !!draftComment.match(/^(\s)*$/); + }); /** * Updates the visibility state of the menu @@ -329,16 +338,19 @@ function ReportActionCompose({ const hasReportRecipient = _.isObject(reportRecipient) && !_.isEmpty(reportRecipient); - const isSendDisabled = isBlockedFromConcierge || disabled || hasExceededMaxCommentLength; + const isSendDisabled = isCommentEmpty || isBlockedFromConcierge || disabled || hasExceededMaxCommentLength; const handleSendMessage = useCallback(() => { if (isSendDisabled || !isReportReadyForDisplay) { return; } + // We are setting the isCommentEmpty flag to true so the status of it will be in sync of the native text input state + runOnJS(setIsCommentEmpty)(true); runOnJS(resetFullComposerSize)(); + setNativeProps(animatedRef, {text: ''}); // clears native text input on the UI thread runOnJS(submitForm)(); - }, [isSendDisabled, resetFullComposerSize, submitForm, isReportReadyForDisplay]); + }, [isSendDisabled, isReportReadyForDisplay, resetFullComposerSize, animatedRef, submitForm]); return ( @@ -394,6 +406,7 @@ function ReportActionCompose({ /> + {DeviceCapabilities.canUseTouchScreen() && isMediumScreenWidth ? null : ( + composerRef.current.replaceSelectionWithText(...args)} + emojiPickerID={report.reportID} + /> + )} + Date: Wed, 24 Jan 2024 18:24:11 +0500 Subject: [PATCH 49/50] refactor: add prop types --- .../ComposerWithSuggestions/composerWithSuggestionsProps.js | 6 ++++++ .../home/report/ReportActionCompose/ReportActionCompose.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/composerWithSuggestionsProps.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/composerWithSuggestionsProps.js index 2543149abd50..9d05db572949 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/composerWithSuggestionsProps.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions/composerWithSuggestionsProps.js @@ -56,6 +56,9 @@ const propTypes = { /** Function to set whether the full composer is available or not */ setIsFullComposerAvailable: PropTypes.func.isRequired, + /** Function to set whether the comment is empty or not */ + setIsCommentEmpty: PropTypes.func.isRequired, + /** A method to call when the form is submitted */ handleSendMessage: PropTypes.func.isRequired, @@ -82,6 +85,9 @@ const propTypes = { }), }).isRequired, + /** Ref for the animated view (text input) */ + animatedRef: PropTypes.func.isRequired, + /** Ref for the composer */ forwardedRef: PropTypes.shape({current: PropTypes.shape({})}), diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index 072b41b484c0..712bdf1aa0d3 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -350,7 +350,7 @@ function ReportActionCompose({ runOnJS(resetFullComposerSize)(); setNativeProps(animatedRef, {text: ''}); // clears native text input on the UI thread runOnJS(submitForm)(); - }, [isSendDisabled, isReportReadyForDisplay, resetFullComposerSize, animatedRef, submitForm]); + }, [isSendDisabled, resetFullComposerSize, submitForm, animatedRef, isReportReadyForDisplay]); return ( From da78bf0f5dc6d4df15833b65e7044a64914c6058 Mon Sep 17 00:00:00 2001 From: hurali97 Date: Wed, 31 Jan 2024 12:40:28 +0500 Subject: [PATCH 50/50] fix: crash and profile not updating --- src/libs/ReportUtils.ts | 2 +- .../ReportActionCompose/ReportActionCompose.js | 2 ++ src/pages/home/report/ReportActionItemCreated.tsx | 14 +++++++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8f0e6b7eee57..228db29aea6c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1439,7 +1439,7 @@ function getWorkspaceIcon(report: OnyxEntry, policy: OnyxEntry = */ function getIcons( report: OnyxEntry, - personalDetails: OnyxCollection = allPersonalDetails, + personalDetails: OnyxCollection, defaultIcon: UserUtils.AvatarSource | null = null, defaultName = '', defaultAccountID = -1, diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js index af871c42a439..cc07716209a2 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.js +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.js @@ -341,6 +341,8 @@ function ReportActionCompose({ const isSendDisabled = isCommentEmpty || isBlockedFromConcierge || disabled || hasExceededMaxCommentLength; const handleSendMessage = useCallback(() => { + 'worklet'; + if (isSendDisabled || !isReportReadyForDisplay) { return; } diff --git a/src/pages/home/report/ReportActionItemCreated.tsx b/src/pages/home/report/ReportActionItemCreated.tsx index a7877914c7f4..f8345ca7d2d0 100644 --- a/src/pages/home/report/ReportActionItemCreated.tsx +++ b/src/pages/home/report/ReportActionItemCreated.tsx @@ -15,7 +15,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import {navigateToConciergeChatAndDeleteReport} from '@userActions/Report'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Policy, Report} from '@src/types/onyx'; +import type {PersonalDetailsList, Policy, Report} from '@src/types/onyx'; import AnimatedEmptyStateBackground from './AnimatedEmptyStateBackground'; type ReportActionItemCreatedOnyxProps = { @@ -24,6 +24,9 @@ type ReportActionItemCreatedOnyxProps = { /** The policy object for the current route */ policy: OnyxEntry; + + /** Personal details of all the users */ + personalDetails: OnyxEntry; }; type ReportActionItemCreatedProps = ReportActionItemCreatedOnyxProps & { @@ -45,7 +48,7 @@ function ReportActionItemCreated(props: ReportActionItemCreatedProps) { return null; } - const icons = ReportUtils.getIcons(props.report); + const icons = ReportUtils.getIcons(props.report, props.personalDetails); const shouldDisableDetailPage = ReportUtils.shouldDisableDetailPage(props.report); return ( @@ -100,6 +103,10 @@ export default withOnyx `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, }, + + personalDetails: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, })( memo( ReportActionItemCreated, @@ -108,6 +115,7 @@ export default withOnyx