Skip to content

Commit 366e024

Browse files
authored
Merge pull request Expensify#65489 from thelullabyy/fix/65110-lack-of-visual-feedback-expense
Lack of Visual Feedback When Expense is Auto-Merged into Report
2 parents 18bd290 + 7aa74aa commit 366e024

8 files changed

Lines changed: 69 additions & 16 deletions

File tree

src/CONST/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1508,6 +1508,7 @@ const CONST = {
15081508
PUSHER_PING_PONG: 'pusher_ping_pong',
15091509
LOCATION_UPDATE_INTERVAL: 5000,
15101510
PLAY_SOUND_MESSAGE_DEBOUNCE_TIME: 500,
1511+
NOTIFY_NEW_ACTION_DELAY: 700,
15111512
SKELETON_ANIMATION_SPEED: 3,
15121513
SEARCH_OPTIONS_COMPARISON: 'search_options_comparison',
15131514
SEARCH_MOST_RECENT_OPTIONS: 'search_most_recent_options',

src/components/Search/SearchList.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
2929
import {turnOnMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
3030
import {isMobileChrome} from '@libs/Browser';
3131
import {addKeyDownPressListener, removeKeyDownPressListener} from '@libs/KeyboardShortcut/KeyDownPressListener';
32+
import durationHighlightItem from '@libs/Navigation/helpers/getDurationHighlightItem';
3233
import variables from '@styles/variables';
3334
import CONST from '@src/CONST';
3435
import ONYXKEYS from '@src/ONYXKEYS';
@@ -287,16 +288,9 @@ function SearchList(
287288
clearTimeout(itemFocusTimeoutRef.current);
288289
}
289290

290-
const duration =
291-
CONST.ANIMATED_HIGHLIGHT_ENTRY_DELAY +
292-
CONST.ANIMATED_HIGHLIGHT_ENTRY_DURATION +
293-
CONST.ANIMATED_HIGHLIGHT_START_DELAY +
294-
CONST.ANIMATED_HIGHLIGHT_START_DURATION +
295-
CONST.ANIMATED_HIGHLIGHT_END_DELAY +
296-
CONST.ANIMATED_HIGHLIGHT_END_DURATION;
297291
itemFocusTimeoutRef.current = setTimeout(() => {
298292
setItemsToHighlight(null);
299-
}, duration);
293+
}, durationHighlightItem);
300294
},
301295
[data, scrollToIndex],
302296
);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import CONST from '@src/CONST';
2+
3+
const durationHighlightItem =
4+
CONST.ANIMATED_HIGHLIGHT_ENTRY_DELAY +
5+
CONST.ANIMATED_HIGHLIGHT_ENTRY_DURATION +
6+
CONST.ANIMATED_HIGHLIGHT_START_DELAY +
7+
CONST.ANIMATED_HIGHLIGHT_START_DURATION +
8+
CONST.ANIMATED_HIGHLIGHT_END_DELAY +
9+
CONST.ANIMATED_HIGHLIGHT_END_DURATION;
10+
export default durationHighlightItem;

src/libs/actions/IOU.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5609,7 +5609,11 @@ function requestMoney(requestMoneyInformation: RequestMoneyInformation) {
56095609
}
56105610

56115611
if (activeReportID && !isMoneyRequestReport) {
5612-
notifyNewAction(activeReportID, payeeAccountID);
5612+
Navigation.setNavigationActionToMicrotaskQueue(() =>
5613+
setTimeout(() => {
5614+
notifyNewAction(activeReportID, payeeAccountID, reportPreviewAction);
5615+
}, CONST.TIMING.NOTIFY_NEW_ACTION_DELAY),
5616+
);
56135617
}
56145618
}
56155619

src/pages/home/report/PureReportActionItem.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,9 @@ type PureReportActionItemProps = {
362362
/** Whether to show border for MoneyRequestReportPreviewContent */
363363
shouldShowBorder?: boolean;
364364

365+
/** Whether to highlight the action for a few seconds */
366+
shouldHighlight?: boolean;
367+
365368
/** Current user's account id */
366369
currentUserAccountID?: number;
367370
};
@@ -428,6 +431,7 @@ function PureReportActionItem({
428431
dismissTrackExpenseActionableWhisper = () => {},
429432
userBillingFundID,
430433
shouldShowBorder,
434+
shouldHighlight = false,
431435
currentUserAccountID,
432436
}: PureReportActionItemProps) {
433437
const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext);
@@ -457,8 +461,9 @@ function PureReportActionItem({
457461
const isReportArchived = useReportIsArchived(report?.reportID);
458462

459463
const highlightedBackgroundColorIfNeeded = useMemo(
460-
() => (isReportActionLinked ? StyleUtils.getBackgroundColorStyle(theme.messageHighlightBG) : {}),
461-
[StyleUtils, isReportActionLinked, theme.messageHighlightBG],
464+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
465+
() => (isReportActionLinked || shouldHighlight ? StyleUtils.getBackgroundColorStyle(theme.messageHighlightBG) : {}),
466+
[StyleUtils, isReportActionLinked, theme.messageHighlightBG, shouldHighlight],
462467
);
463468

464469
const reportPreviewStyles = StyleUtils.getMoneyRequestReportPreviewStyle(shouldUseNarrowLayout, 1, undefined, undefined);
@@ -1646,6 +1651,7 @@ export default memo(PureReportActionItem, (prevProps, nextProps) => {
16461651
deepEqual(prevProps.missingPaymentMethod, nextProps.missingPaymentMethod) &&
16471652
prevProps.reimbursementDeQueuedOrCanceledActionMessage === nextProps.reimbursementDeQueuedOrCanceledActionMessage &&
16481653
prevProps.modifiedExpenseMessage === nextProps.modifiedExpenseMessage &&
1654+
prevProps.shouldHighlight === nextProps.shouldHighlight &&
16491655
prevProps.userBillingFundID === nextProps.userBillingFundID
16501656
);
16511657
});

src/pages/home/report/ReportActionsList.tsx

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions';
2222
import {isSafari} from '@libs/Browser';
2323
import DateUtils from '@libs/DateUtils';
2424
import {getChatFSAttributes, parseFSAttributes} from '@libs/Fullstory';
25+
import durationHighlightItem from '@libs/Navigation/helpers/getDurationHighlightItem';
2526
import isReportTopmostSplitNavigator from '@libs/Navigation/helpers/isReportTopmostSplitNavigator';
2627
import isSearchTopmostFullScreenRoute from '@libs/Navigation/helpers/isSearchTopmostFullScreenRoute';
2728
import Navigation from '@libs/Navigation/Navigation';
@@ -173,6 +174,7 @@ function ReportActionsList({
173174
const isReportArchived = useReportIsArchived(report?.reportID);
174175

175176
const [isScrollToBottomEnabled, setIsScrollToBottomEnabled] = useState(false);
177+
const [actionIdToHighlight, setActionIdToHighlight] = useState('');
176178

177179
useEffect(() => {
178180
const unsubscribe = Visibility.onVisibilityChange(() => {
@@ -409,7 +411,7 @@ function ReportActionsList({
409411
}, [lastAction, prevSortedVisibleReportActionsObjects, reportScrollManager]);
410412

411413
const scrollToBottomForCurrentUserAction = useCallback(
412-
(isFromCurrentUser: boolean) => {
414+
(isFromCurrentUser: boolean, action?: OnyxTypes.ReportAction) => {
413415
InteractionManager.runAfterInteractions(() => {
414416
// If a new comment is added and it's from the current user scroll to the bottom otherwise leave the user positioned where
415417
// they are now in the list.
@@ -425,14 +427,42 @@ function ReportActionsList({
425427
});
426428
return;
427429
}
430+
const index = sortedVisibleReportActions.findIndex((item) => keyExtractor(item) === action?.reportActionID);
431+
if (action?.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW) {
432+
if (index > 0) {
433+
setTimeout(() => {
434+
reportScrollManager.scrollToIndex(index);
435+
}, 100);
436+
} else {
437+
setIsFloatingMessageCounterVisible(false);
438+
reportScrollManager.scrollToBottom();
439+
}
440+
if (action?.reportActionID) {
441+
setActionIdToHighlight(action.reportActionID);
442+
}
443+
} else {
444+
setIsFloatingMessageCounterVisible(false);
445+
reportScrollManager.scrollToBottom();
446+
}
428447

429-
setIsFloatingMessageCounterVisible(false);
430-
reportScrollManager.scrollToBottom();
431448
setIsScrollToBottomEnabled(true);
432449
});
433450
},
434-
[report.reportID, reportScrollManager, setIsFloatingMessageCounterVisible],
451+
[report.reportID, reportScrollManager, setIsFloatingMessageCounterVisible, sortedVisibleReportActions, reportScrollManager],
435452
);
453+
454+
// Clear the highlighted report action after scrolling and highlighting
455+
useEffect(() => {
456+
if (actionIdToHighlight === '') {
457+
return;
458+
}
459+
// Time highlight is the same as SearchPage
460+
const timer = setTimeout(() => {
461+
setActionIdToHighlight('');
462+
}, durationHighlightItem);
463+
return () => clearTimeout(timer);
464+
}, [actionIdToHighlight]);
465+
436466
useEffect(() => {
437467
// Why are we doing this, when in the cleanup of the useEffect we are already calling the unsubscribe function?
438468
// Answer: On web, when navigating to another report screen, the previous report screen doesn't get unmounted,
@@ -608,6 +638,7 @@ function ReportActionsList({
608638
isFirstVisibleReportAction={firstVisibleReportActionID === reportAction.reportActionID}
609639
shouldUseThreadDividerLine={shouldUseThreadDividerLine}
610640
transactions={Object.values(transactions ?? {})}
641+
shouldHighlight={actionIdToHighlight === reportAction.reportActionID}
611642
/>
612643
);
613644
},
@@ -627,6 +658,7 @@ function ReportActionsList({
627658
shouldUseThreadDividerLine,
628659
firstVisibleReportActionID,
629660
unreadMarkerReportActionID,
661+
actionIdToHighlight,
630662
],
631663
);
632664

src/pages/home/report/ReportActionsListItemRenderer.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ type ReportActionsListItemRendererProps = {
6161

6262
/** If the thread divider line will be used */
6363
shouldUseThreadDividerLine?: boolean;
64+
65+
/** Animate highlight action in few seconds */
66+
shouldHighlight?: boolean;
6467
};
6568

6669
function ReportActionsListItemRenderer({
@@ -81,6 +84,7 @@ function ReportActionsListItemRenderer({
8184
shouldDisplayReplyDivider,
8285
isFirstVisibleReportAction = false,
8386
shouldUseThreadDividerLine = false,
87+
shouldHighlight = false,
8488
parentReportActionForTransactionThread,
8589
}: ReportActionsListItemRendererProps) {
8690
const originalMessage = useMemo(() => getOriginalMessage(reportAction), [reportAction]);
@@ -204,6 +208,7 @@ function ReportActionsListItemRenderer({
204208
index={index}
205209
isFirstVisibleReportAction={isFirstVisibleReportAction}
206210
shouldUseThreadDividerLine={shouldUseThreadDividerLine}
211+
shouldHighlight={shouldHighlight}
207212
/>
208213
);
209214
}

tests/actions/IOUTest.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import {WRITE_COMMANDS} from '@libs/API/types';
4444
import type {ApiCommand} from '@libs/API/types';
4545
import {getMicroSecondOnyxErrorWithTranslationKey} from '@libs/ErrorUtils';
4646
import {translateLocal} from '@libs/Localize';
47+
import Navigation from '@libs/Navigation/Navigation';
4748
import {rand64} from '@libs/NumberUtils';
4849
import {getLoginsByAccountIDs} from '@libs/PersonalDetailsUtils';
4950
import {
@@ -1613,7 +1614,7 @@ describe('actions/IOU', () => {
16131614
comment: '',
16141615
},
16151616
});
1616-
expect(notifyNewAction).toHaveBeenCalledTimes(1);
1617+
expect(Navigation.setNavigationActionToMicrotaskQueue).toHaveBeenCalledTimes(1);
16171618
});
16181619
});
16191620

0 commit comments

Comments
 (0)