diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx index 361cedd56354..19204958dd5f 100644 --- a/src/pages/home/report/ReportActionsList.tsx +++ b/src/pages/home/report/ReportActionsList.tsx @@ -348,12 +348,10 @@ function ReportActionsList({ const unsubscribe = Report.subscribeToNewActionEvent(report.reportID, scrollToBottomForCurrentUserAction); const cleanup = () => { - if (unsubscribe) { - unsubscribe(); + if (!unsubscribe) { + return; } - InteractionManager.runAfterInteractions(() => { - Report.unsubscribeFromReportChannel(report.reportID); - }); + unsubscribe(); }; newActionUnsubscribeMap[report.reportID] = cleanup; diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx index 65d880879d61..392b1debc960 100755 --- a/src/pages/home/report/ReportActionsView.tsx +++ b/src/pages/home/report/ReportActionsView.tsx @@ -3,8 +3,8 @@ import {useIsFocused, useRoute} from '@react-navigation/native'; import lodashIsEqual from 'lodash/isEqual'; import React, {useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react'; import {InteractionManager} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import useCopySelectionHelper from '@hooks/useCopySelectionHelper'; import useInitialValue from '@hooks/useInitialValue'; import useNetwork from '@hooks/useNetwork'; @@ -17,8 +17,8 @@ import * as NumberUtils from '@libs/NumberUtils'; import {generateNewRandomInt} from '@libs/NumberUtils'; import Performance from '@libs/Performance'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import {isUserCreatedPolicyRoom} from '@libs/ReportUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import {isUserCreatedPolicyRoom} from '@libs/ReportUtils'; import {didUserLogInDuringSession} from '@libs/SessionUtils'; import shouldFetchReport from '@libs/shouldFetchReport'; import {ReactionListContext} from '@pages/home/ReportScreenContext'; @@ -32,6 +32,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import getInitialPaginationSize from './getInitialPaginationSize'; import PopoverReactionList from './ReactionList/PopoverReactionList'; import ReportActionsList from './ReportActionsList'; +import UserTypingEventListener from './UserTypingEventListener'; type ReportActionsViewOnyxProps = { /** Session info for the currently logged in user. */ @@ -93,7 +94,6 @@ function ReportActionsView({ const route = useRoute>(); const reportActionID = route?.params?.reportActionID; const didLayout = useRef(false); - const didSubscribeToReportTypingEvents = useRef(false); // triggerListID is used when navigating to a chat with messages loaded from LHN. Typically, these include thread actions, task actions, etc. Since these messages aren't the latest,we don't maintain their position and instead trigger a recalculation of their positioning in the list. // we don't set currentReportActionID on initial render as linkedID as it should trigger visibleReportActions after linked message was positioned @@ -287,27 +287,6 @@ function ReportActionsView({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [isSmallScreenWidth, reportActions, isReportFullyVisible]); - useEffect(() => { - // Ensures the optimistic report is created successfully - if (route?.params?.reportID !== reportID) { - return; - } - // Ensures subscription event succeeds when the report/workspace room is created optimistically. - // Check if the optimistic `OpenReport` or `AddWorkspaceRoom` has succeeded by confirming - // any `pendingFields.createChat` or `pendingFields.addWorkspaceRoom` fields are set to null. - // Existing reports created will have empty fields for `pendingFields`. - const didCreateReportSuccessfully = !report.pendingFields || (!report.pendingFields.addWorkspaceRoom && !report.pendingFields.createChat); - if (!didSubscribeToReportTypingEvents.current && didCreateReportSuccessfully) { - const interactionTask = InteractionManager.runAfterInteractions(() => { - Report.subscribeToReportTypingEvents(reportID); - didSubscribeToReportTypingEvents.current = true; - }); - return () => { - interactionTask.cancel(); - }; - } - }, [report.pendingFields, didSubscribeToReportTypingEvents, route, reportID]); - const onContentSizeChange = useCallback((w: number, h: number) => { contentListHeight.current = h; }, []); @@ -525,6 +504,7 @@ function ReportActionsView({ onContentSizeChange={onContentSizeChange} shouldEnableAutoScrollToTopThreshold={shouldEnableAutoScroll} /> + ); diff --git a/src/pages/home/report/UserTypingEventListener.tsx b/src/pages/home/report/UserTypingEventListener.tsx new file mode 100644 index 000000000000..45e56cfe7ff1 --- /dev/null +++ b/src/pages/home/report/UserTypingEventListener.tsx @@ -0,0 +1,74 @@ +import type {RouteProp} from '@react-navigation/native'; +import {useIsFocused, useRoute} from '@react-navigation/native'; +import {useEffect, useRef} from 'react'; +import {InteractionManager} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import Navigation from '@libs/Navigation/Navigation'; +import type {CentralPaneNavigatorParamList} from '@libs/Navigation/types'; +import * as Report from '@userActions/Report'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; +import type * as OnyxTypes from '@src/types/onyx'; + +type UserTypingEventListenerOnyxProps = { + /** Stores last visited path */ + lastVisitedPath?: string; +}; + +type UserTypingEventListenerProps = UserTypingEventListenerOnyxProps & { + /** The report currently being looked at */ + report: OnyxTypes.Report; +}; +function UserTypingEventListener({report, lastVisitedPath}: UserTypingEventListenerProps) { + const didSubscribeToReportTypingEvents = useRef(false); + const reportID = report.reportID; + const isFocused = useIsFocused(); + const route = useRoute>(); + useEffect(() => { + // Ensures any optimistic report that is being created (ex: a thread report) gets created and initialized successfully before subscribing + if (route?.params?.reportID !== reportID) { + return; + } + let interactionTask: ReturnType | null = null; + if (isFocused) { + // Ensures subscription event succeeds when the report/workspace room is created optimistically. + // Check if the optimistic `OpenReport` or `AddWorkspaceRoom` has succeeded by confirming + // any `pendingFields.createChat` or `pendingFields.addWorkspaceRoom` fields are set to null. + // Existing reports created will have empty fields for `pendingFields`. + const didCreateReportSuccessfully = !report.pendingFields || (!report.pendingFields.addWorkspaceRoom && !report.pendingFields.createChat); + + if (!didSubscribeToReportTypingEvents.current && didCreateReportSuccessfully) { + interactionTask = InteractionManager.runAfterInteractions(() => { + Report.subscribeToReportTypingEvents(reportID); + didSubscribeToReportTypingEvents.current = true; + }); + } + } else { + const topmostReportId = Navigation.getTopmostReportId(); + + if (topmostReportId !== reportID && didSubscribeToReportTypingEvents.current) { + didSubscribeToReportTypingEvents.current = false; + InteractionManager.runAfterInteractions(() => { + Report.unsubscribeFromReportChannel(reportID); + }); + } + } + return () => { + if (!interactionTask) { + return; + } + interactionTask.cancel(); + }; + }, [isFocused, report.pendingFields, didSubscribeToReportTypingEvents, lastVisitedPath, reportID, route]); + + return null; +} + +UserTypingEventListener.displayName = 'UserTypingEventListener'; + +export default withOnyx({ + lastVisitedPath: { + key: ONYXKEYS.LAST_VISITED_PATH, + selector: (path) => path ?? '', + }, +})(UserTypingEventListener);