Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update the behavior of the yellow thread highlight after exiting a thread #51533

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
dd5edc8
v1 POC
ishpaul777 Oct 4, 2024
ab619d4
formating
ishpaul777 Oct 4, 2024
449e647
Merge branch 'main' into fix/42165
ishpaul777 Oct 12, 2024
a0cb3af
fixes some bugs
ishpaul777 Oct 12, 2024
617e166
Merge branch 'Expensify:main' into fix/42165
ishpaul777 Oct 26, 2024
4c10da0
fix reply in thread case
ishpaul777 Oct 26, 2024
dbe29ef
fix typing
ishpaul777 Oct 26, 2024
a5f1ba7
disable nullish prefer-nullish-coalescing
ishpaul777 Oct 26, 2024
d37fc01
fixes delay in message highlight
ishpaul777 Oct 26, 2024
6c2803f
minor fixes
ishpaul777 Oct 27, 2024
a56034c
pretteir diffs
ishpaul777 Oct 27, 2024
076b609
Merge branch 'main' into fix/42165
ishpaul777 Oct 27, 2024
67f3398
fix merge conflict
ishpaul777 Oct 27, 2024
08e5e6a
Merge branch 'main' into fix/42165
ishpaul777 Nov 3, 2024
2d6aff5
remove unnecessary comments
ishpaul777 Nov 3, 2024
f5759bb
Merge branch 'main' into fix/42165
ishpaul777 Nov 6, 2024
184beec
prettier diffs
ishpaul777 Nov 6, 2024
f9cf514
fix as per review
ishpaul777 Nov 6, 2024
80ec6a3
Merge branch 'main' into fix/42165
ishpaul777 Nov 6, 2024
f1da203
Merge branch 'Expensify:main' into fix/42165
ishpaul777 Nov 11, 2024
6b5f9d8
Merge branch 'Expensify:main' into fix/42165
ishpaul777 Nov 17, 2024
df01205
Merge branch 'Expensify:main' into fix/42165
ishpaul777 Nov 21, 2024
91fb565
Merge branch 'Expensify:main' into fix/42165
ishpaul777 Nov 26, 2024
47bf7ce
fix as per review
ishpaul777 Nov 26, 2024
c98dfae
Merge branch 'main' into fix/42165
ishpaul777 Dec 4, 2024
2b9aeff
fix perf tests
ishpaul777 Dec 4, 2024
c49f364
Merge branch 'Expensify:main' into fix/42165
ishpaul777 Dec 10, 2024
cc2377a
Add conditional navigation parameter update for narrow layout in Repo…
ishpaul777 Dec 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/components/ReportActionHighlightProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type {ReactNode} from 'react';
import React, {createContext, useCallback, useMemo, useState} from 'react';

type ReportActionHighlightContextType = {
linkedReportActionID: string | null;
setHighlight: (id: string) => void;
removeHighlight: () => void;
};

const ReportActionHighlightContext = createContext<ReportActionHighlightContextType>({
linkedReportActionID: null,
setHighlight: () => {},
removeHighlight: () => {},
});

type ReportActionHighlightProviderProps = {
children: ReactNode;
};

function ReportActionHighlightProvider({children}: ReportActionHighlightProviderProps) {
const [linkedReportActionID, setLinkedReportActionID] = useState<string | null>(null);

const setHighlight = useCallback((id: string) => {
setLinkedReportActionID(id);
}, []);

const removeHighlight = useCallback(() => {
setLinkedReportActionID(null);
}, []);

const value = useMemo(
() => ({
linkedReportActionID,
setHighlight,
removeHighlight,
}),
[linkedReportActionID, removeHighlight, setHighlight],
);

return <ReportActionHighlightContext.Provider value={value}>{children}</ReportActionHighlightContext.Provider>;
}

export default ReportActionHighlightProvider;
export {ReportActionHighlightContext};
3 changes: 2 additions & 1 deletion src/libs/Navigation/AppNavigator/AuthScreens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {ValueOf} from 'type-fest';
import ActiveGuidesEventListener from '@components/ActiveGuidesEventListener';
import ComposeProviders from '@components/ComposeProviders';
import OptionsListContextProvider from '@components/OptionListContextProvider';
import ReportActionHighlightProvider from '@components/ReportActionHighlightProvider';
import {SearchContextProvider} from '@components/Search/SearchContext';
import {useSearchRouterContext} from '@components/Search/SearchRouter/SearchRouterContext';
import SearchRouterModal from '@components/Search/SearchRouter/SearchRouterModal';
Expand Down Expand Up @@ -414,7 +415,7 @@ function AuthScreens({session, lastOpenedPublicRoomID, initialLastUpdateIDApplie
};

return (
<ComposeProviders components={[OptionsListContextProvider, SearchContextProvider]}>
<ComposeProviders components={[OptionsListContextProvider, SearchContextProvider, ReportActionHighlightProvider]}>
<View style={styles.rootNavigatorContainerStyles(shouldUseNarrowLayout)}>
<RootStack.Navigator screenOptions={rootNavigatorOptions.centralPaneNavigator}>
<RootStack.Screen
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import lodashIsEqual from 'lodash/isEqual';
import type {MutableRefObject, RefObject} from 'react';
import React, {memo, useMemo, useRef, useState} from 'react';
import React, {memo, useContext, useMemo, useRef, useState} from 'react';
import {InteractionManager, View} from 'react-native';
// eslint-disable-next-line no-restricted-imports
import type {GestureResponderEvent, Text as RNText, View as ViewType} from 'react-native';
Expand All @@ -9,6 +9,7 @@ import {useOnyx} from 'react-native-onyx';
import type {ContextMenuItemHandle} from '@components/ContextMenuItem';
import ContextMenuItem from '@components/ContextMenuItem';
import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal';
import {ReportActionHighlightContext} from '@components/ReportActionHighlightProvider';
import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager';
import useEnvironment from '@hooks/useEnvironment';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
Expand Down Expand Up @@ -187,6 +188,8 @@ function BaseReportActionContextMenu({
const areHoldRequirementsMet =
!isInvoiceReport && isMoneyRequestOrReport && !ReportUtils.isArchivedRoom(transactionThreadReportID ? childReport : parentReport, parentReportNameValuePairs);

const {removeHighlight} = useContext(ReportActionHighlightContext);

const shouldEnableArrowNavigation = !isMini && (isVisible || shouldKeepOpen);
let filteredContextMenuActions = ContextMenuActions.filter(
(contextAction) =>
Expand Down Expand Up @@ -330,6 +333,7 @@ function BaseReportActionContextMenu({
setIsEmojiPickerActive,
moneyRequestAction,
hasCard: !!card,
removeHighlight: isMini ? removeHighlight : () => {},
};

if ('renderContent' in contextAction) {
Expand Down
4 changes: 3 additions & 1 deletion src/pages/home/report/ContextMenu/ContextMenuActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type ContextMenuActionPayload = {
anchorRef?: MutableRefObject<View | null>;
moneyRequestAction: ReportAction | undefined;
hasCard?: boolean;
removeHighlight?: () => void;
};

type OnPress = (closePopover: boolean, payload: ContextMenuActionPayload, selection?: string, reportID?: string, draftMessage?: string) => void;
Expand Down Expand Up @@ -185,7 +186,7 @@ const ContextMenuActions: ContextMenuAction[] = [
}
return !ReportUtils.shouldDisableThread(reportAction, reportID, isThreadReportParentAction);
},
onPress: (closePopover, {reportAction, reportID}) => {
onPress: (closePopover, {reportAction, reportID, removeHighlight}) => {
const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction);
if (closePopover) {
hideContextMenu(false, () => {
Expand All @@ -198,6 +199,7 @@ const ContextMenuActions: ContextMenuAction[] = [
});
return;
}
removeHighlight?.();
Report.navigateToAndOpenChildReport(reportAction?.childReportID ?? '-1', reportAction, originalReportID);
},
getDescription: () => {},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {useNavigation} from '@react-navigation/native';
import lodashDebounce from 'lodash/debounce';
import noop from 'lodash/noop';
import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react';
import React, {memo, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
import type {MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInputFocusEventData, TextInputSelectionChangeEventData} from 'react-native';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
Expand All @@ -19,6 +19,7 @@ import type {Mention} from '@components/MentionSuggestions';
import OfflineIndicator from '@components/OfflineIndicator';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import {usePersonalDetails} from '@components/OnyxProvider';
import {ReportActionHighlightContext} from '@components/ReportActionHighlightProvider';
import Text from '@components/Text';
import EducationalTooltip from '@components/Tooltip/EducationalTooltip';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
Expand Down Expand Up @@ -264,6 +265,8 @@ function ReportActionCompose({
setIsAttachmentPreviewActive(false);
}, [updateShouldShowSuggestionMenuToFalse]);

const {removeHighlight} = useContext(ReportActionHighlightContext);

/**
* Add a new comment to this chat
*/
Expand All @@ -280,15 +283,17 @@ function ReportActionCompose({
Performance.markStart(CONST.TIMING.SEND_MESSAGE, {message: newCommentTrimmed});
Timing.start(CONST.TIMING.SEND_MESSAGE);
onSubmit(newCommentTrimmed);
removeHighlight();
}
},
[onSubmit, reportID],
[onSubmit, reportID, removeHighlight],
);

const onTriggerAttachmentPicker = useCallback(() => {
removeHighlight();
isNextModalWillOpenRef.current = true;
isKeyboardVisibleWhenShowingModalRef.current = true;
}, []);
}, [removeHighlight]);

const onBlur = useCallback(
(event: NativeSyntheticEvent<TextInputFocusEventData>) => {
Expand Down Expand Up @@ -354,6 +359,7 @@ function ReportActionCompose({
const composerRefShared = useSharedValue<{
clear: (() => void) | undefined;
}>({clear: undefined});

const handleSendMessage = useCallback(() => {
'worklet';

Expand Down
20 changes: 12 additions & 8 deletions src/pages/home/report/ReportActionItem.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import lodashIsEqual from 'lodash/isEqual';
import React, {memo, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
import React, {memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react';
import type {GestureResponderEvent, TextInput} from 'react-native';
import {InteractionManager, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
Expand All @@ -19,6 +19,7 @@ import {useBlockedFromConcierge, usePersonalDetails} from '@components/OnyxProvi
import PressableWithSecondaryInteraction from '@components/PressableWithSecondaryInteraction';
import ReportActionItemEmojiReactions from '@components/Reactions/ReportActionItemEmojiReactions';
import RenderHTML from '@components/RenderHTML';
import {ReportActionHighlightContext} from '@components/ReportActionHighlightProvider';
import type {ActionableItem} from '@components/ReportActionItem/ActionableItemButtons';
import ActionableItemButtons from '@components/ReportActionItem/ActionableItemButtons';
import ChronosOOOListActions from '@components/ReportActionItem/ChronosOOOListActions';
Expand Down Expand Up @@ -209,12 +210,13 @@ function ReportActionItem({
const isActionableWhisper =
ReportActionsUtils.isActionableMentionWhisper(action) || ReportActionsUtils.isActionableTrackExpense(action) || ReportActionsUtils.isActionableReportMentionWhisper(action);
const originalMessage = ReportActionsUtils.getOriginalMessage(action);

const highlightedBackgroundColorIfNeeded = useMemo(
() => (isReportActionLinked ? StyleUtils.getBackgroundColorStyle(theme.messageHighlightBG) : {}),
[StyleUtils, isReportActionLinked, theme.messageHighlightBG],
);

const {linkedReportActionID: contextlinkedReportActionID, setHighlight} = useContext(ReportActionHighlightContext);
useLayoutEffect(() => {
if (!isReportActionLinked) {
return;
}
setHighlight(linkedReportActionID);
}, [isReportActionLinked, linkedReportActionID, setHighlight]);
const isDeletedParentAction = ReportActionsUtils.isDeletedParentAction(action);
const isOriginalMessageAnObject = originalMessage && typeof originalMessage === 'object';
const hasResolutionInOriginalMessage = isOriginalMessageAnObject && 'resolution' in originalMessage;
Expand Down Expand Up @@ -956,6 +958,8 @@ function ReportActionItem({
const isWhisperOnlyVisibleByUser = isWhisper && ReportUtils.isCurrentUserTheOnlyParticipant(whisperedTo);
const displayNamesWithTooltips = isWhisper ? ReportUtils.getDisplayNamesWithTooltips(whisperedToPersonalDetails, isMultipleParticipant) : [];

const highlightedBackgroundColorStyles =
isReportActionLinked && contextlinkedReportActionID === action.reportActionID ? StyleUtils.getBackgroundColorStyle(theme.messageHighlightBG) : {};
return (
<PressableWithSecondaryInteraction
ref={popoverAnchorRef}
Expand All @@ -975,7 +979,7 @@ function ReportActionItem({
shouldFreezeCapture={isPaymentMethodPopoverActive}
>
{(hovered) => (
<View style={highlightedBackgroundColorIfNeeded}>
<View style={highlightedBackgroundColorStyles}>
{shouldDisplayNewMarker && (!shouldUseThreadDividerLine || !isFirstVisibleReportAction) && <UnreadActionIndicator reportActionID={action.reportActionID} />}
{shouldDisplayContextMenu && (
<MiniReportActionContextMenu
Expand Down
22 changes: 20 additions & 2 deletions src/pages/home/report/ReportActionsList.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import type {ListRenderItemInfo} from '@react-native/virtualized-lists/Lists/VirtualizedList';
import {useIsFocused, useRoute} from '@react-navigation/native';
import {useIsFocused, useNavigation, useRoute} from '@react-navigation/native';
// eslint-disable-next-line lodash/import-scope
import type {DebouncedFunc} from 'lodash';
import React, {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react';
import React, {memo, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
import {DeviceEventEmitter, InteractionManager, View} from 'react-native';
import type {LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import InvertedFlatList from '@components/InvertedFlatList';
import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@components/InvertedFlatList/BaseInvertedFlatList';
import {usePersonalDetails} from '@components/OnyxProvider';
import {ReportActionHighlightContext} from '@components/ReportActionHighlightProvider';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useLocalize from '@hooks/useLocalize';
import useNetworkWithOfflineStatus from '@hooks/useNetworkWithOfflineStatus';
Expand Down Expand Up @@ -155,6 +156,7 @@ function ReportActionsList({
const {translate} = useLocalize();
const {windowHeight} = useWindowDimensions();
const {isInNarrowPaneModal, shouldUseNarrowLayout} = useResponsiveLayout();
const navigation = useNavigation();

const {preferredLocale} = useLocalize();
const {isOffline, lastOfflineAt, lastOnlineAt} = useNetworkWithOfflineStatus();
Expand Down Expand Up @@ -360,6 +362,22 @@ function ReportActionsList({
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, [lastAction?.created]);

const {removeHighlight, linkedReportActionID: ctxlinkedReportActionID} = useContext(ReportActionHighlightContext);

useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
if (linkedReportActionID !== ctxlinkedReportActionID) {
return;
}
if (shouldUseNarrowLayout) {
navigation.setParams({reportActionID: ''});
}
removeHighlight();
});

return unsubscribe;
}, [linkedReportActionID, removeHighlight, report.reportID, navigation, ctxlinkedReportActionID, shouldUseNarrowLayout]);

const lastActionIndex = lastAction?.reportActionID;
const reportActionSize = useRef(sortedVisibleReportActions.length);
const lastVisibleActionCreated =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {useIsFocused as useIsFocusedOriginal, useNavigationState} from '@react-navigation/native';
import type {ImageContentFit} from 'expo-image';
import type {ForwardedRef} from 'react';
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
import React, {forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
import {View} from 'react-native';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
Expand All @@ -11,6 +11,7 @@ import FloatingActionButton from '@components/FloatingActionButton';
import * as Expensicons from '@components/Icon/Expensicons';
import type {PopoverMenuItem} from '@components/PopoverMenu';
import PopoverMenu from '@components/PopoverMenu';
import {ReportActionHighlightContext} from '@components/ReportActionHighlightProvider';
import Text from '@components/Text';
import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
Expand Down Expand Up @@ -182,6 +183,8 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu}: Fl
const isValidReport = !(isEmptyObject(quickActionReport) || ReportUtils.isArchivedRoom(quickActionReport, reportNameValuePairs));
const {environment} = useEnvironment();
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);

const {removeHighlight} = useContext(ReportActionHighlightContext);
const navatticURL = getNavatticURL(environment, introSelected?.choice);
const [hasSeenTour = false] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
selector: hasSeenTourSelector,
Expand Down Expand Up @@ -428,7 +431,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu}: Fl
}),
},
];
}, [canUseCombinedTrackSubmit, translate, selfDMReportID, hasSeenTrackTraining, isOffline, shouldRedirectToExpensifyClassic]);
}, [canUseCombinedTrackSubmit, selfDMReportID, translate, shouldRedirectToExpensifyClassic, hasSeenTrackTraining, isOffline]);

const quickActionMenuItems = useMemo(() => {
// Define common properties in baseQuickAction
Expand Down Expand Up @@ -559,6 +562,7 @@ function FloatingActionButtonAndPopover({onHideCreateMenu, onShowCreateMenu}: Fl
text: translate('tour.takeATwoMinuteTour'),
description: translate('tour.exploreExpensify'),
onSelected: () => {
removeHighlight();
Link.openExternalLink(navatticURL);
Welcome.setSelfTourViewed(Session.isAnonymousUser());
if (viewTourTaskReport) {
Expand Down
4 changes: 4 additions & 0 deletions tests/perf-test/ReportActionsList.perf-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ jest.mock('@react-navigation/native', () => {
...actualNav,
useRoute: () => mockedNavigate,
useIsFocused: () => true,
useNavigation: () => ({
navigate: jest.fn(),
addListener: () => jest.fn(),
}),
};
});

Expand Down
Loading