Skip to content

Commit f17a6ec

Browse files
authored
Merge pull request #27227 from margelo/feat/##23230-highlight-linked-comment
Highlight linked comment
2 parents 9c15943 + 534ffba commit f17a6ec

6 files changed

+75
-7
lines changed

src/libs/Navigation/Navigation.js

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import linkingConfig from './linkingConfig';
1010
import navigationRef from './navigationRef';
1111
import NAVIGATORS from '../../NAVIGATORS';
1212
import originalGetTopmostReportId from './getTopmostReportId';
13+
import originalGetTopmostReportActionId from './getTopmostReportActionID';
1314
import getStateFromPath from './getStateFromPath';
1415
import SCREENS from '../../SCREENS';
1516
import CONST from '../../CONST';
@@ -46,6 +47,9 @@ function canNavigate(methodName, params = {}) {
4647
// Re-exporting the getTopmostReportId here to fill in default value for state. The getTopmostReportId isn't defined in this file to avoid cyclic dependencies.
4748
const getTopmostReportId = (state = navigationRef.getState()) => originalGetTopmostReportId(state);
4849

50+
// Re-exporting the getTopmostReportActionID here to fill in default value for state. The getTopmostReportActionID isn't defined in this file to avoid cyclic dependencies.
51+
const getTopmostReportActionId = (state = navigationRef.getState()) => originalGetTopmostReportActionId(state);
52+
4953
/**
5054
* Method for finding on which index in stack we are.
5155
* @param {Object} route
@@ -268,6 +272,7 @@ export default {
268272
setIsNavigationReady,
269273
getTopmostReportId,
270274
getRouteNameFromStateEvent,
275+
getTopmostReportActionId,
271276
};
272277

273278
export {navigationRef};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import lodashFindLast from 'lodash/findLast';
2+
import lodashGet from 'lodash/get';
3+
4+
// This function is in a separate file than Navigation.js to avoid cyclic dependency.
5+
6+
/**
7+
* Find the last visited report screen in the navigation state and get the linked reportActionID of it.
8+
*
9+
* @param {Object} state - The react-navigation state
10+
* @returns {String | undefined} - It's possible that there is no report screen
11+
*/
12+
function getTopmostReportActionID(state) {
13+
if (!state) {
14+
return;
15+
}
16+
const topmostCentralPane = lodashFindLast(state.routes, (route) => route.name === 'CentralPaneNavigator');
17+
18+
if (!topmostCentralPane) {
19+
return;
20+
}
21+
22+
const directReportActionIDParam = lodashGet(topmostCentralPane, 'params.params.reportActionID');
23+
24+
if (!topmostCentralPane.state && !directReportActionIDParam) {
25+
return;
26+
}
27+
28+
if (directReportActionIDParam) {
29+
return directReportActionIDParam;
30+
}
31+
32+
const topmostReport = lodashFindLast(topmostCentralPane.state.routes, (route) => route.name === 'Report');
33+
if (!topmostReport) {
34+
return;
35+
}
36+
37+
const topmostReportActionID = lodashGet(topmostReport, 'params.reportActionID');
38+
39+
return topmostReportActionID;
40+
}
41+
42+
export default getTopmostReportActionID;

src/pages/home/report/ReportActionItem.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import _ from 'underscore';
22
import lodashGet from 'lodash/get';
3-
import React, {useState, useRef, useEffect, memo, useCallback, useContext} from 'react';
3+
import React, {useState, useRef, useEffect, memo, useCallback, useContext, useMemo} from 'react';
44
import {InteractionManager, View} from 'react-native';
55
import PropTypes from 'prop-types';
66
import {withOnyx} from 'react-native-onyx';
@@ -61,12 +61,13 @@ import * as Session from '../../../libs/actions/Session';
6161
import MoneyRequestView from '../../../components/ReportActionItem/MoneyRequestView';
6262
import {hideContextMenu} from './ContextMenu/ReportActionContextMenu';
6363
import * as PersonalDetailsUtils from '../../../libs/PersonalDetailsUtils';
64-
import ReportActionItemBasicMessage from './ReportActionItemBasicMessage';
6564
import * as store from '../../../libs/actions/ReimbursementAccount/store';
6665
import * as BankAccounts from '../../../libs/actions/BankAccounts';
6766
import {ReactionListContext} from '../ReportScreenContext';
6867
import usePrevious from '../../../hooks/usePrevious';
6968
import Permissions from '../../../libs/Permissions';
69+
import themeColors from '../../../styles/themes/default';
70+
import ReportActionItemBasicMessage from './ReportActionItemBasicMessage';
7071
import RenderHTML from '../../../components/RenderHTML';
7172
import ReportAttachmentsContext from './ReportAttachmentsContext';
7273

@@ -138,6 +139,9 @@ function ReportActionItem(props) {
138139
const prevDraftMessage = usePrevious(props.draftMessage);
139140
const originalReportID = ReportUtils.getOriginalReportID(props.report.reportID, props.action);
140141
const originalReport = props.report.reportID === originalReportID ? props.report : ReportUtils.getReport(originalReportID);
142+
const isReportActionLinked = props.linkedReportActionID === props.action.reportActionID;
143+
144+
const highlightedBackgroundColorIfNeeded = useMemo(() => (isReportActionLinked ? StyleUtils.getBackgroundColorStyle(themeColors.highlightBG) : {}), [isReportActionLinked]);
141145

142146
// When active action changes, we need to update the `isContextMenuActive` state
143147
const isActiveReportActionForMenu = ReportActionContextMenu.isActiveReportAction(props.action.reportActionID);
@@ -594,7 +598,7 @@ function ReportActionItem(props) {
594598
disabled={Boolean(props.draftMessage)}
595599
>
596600
{(hovered) => (
597-
<View>
601+
<View style={highlightedBackgroundColorIfNeeded}>
598602
{props.shouldDisplayNewMarker && <UnreadActionIndicator reportActionID={props.action.reportActionID} />}
599603
<MiniReportActionContextMenu
600604
reportID={props.report.reportID}
@@ -638,7 +642,7 @@ function ReportActionItem(props) {
638642
/>
639643
</View>
640644
)}
641-
{renderReportActionItem(hovered, isWhisper, hasErrors)}
645+
{renderReportActionItem(hovered || isReportActionLinked, isWhisper, hasErrors)}
642646
</OfflineWithFeedback>
643647
</View>
644648
</View>
@@ -711,6 +715,7 @@ export default compose(
711715
prevProps.report.managerID === nextProps.report.managerID &&
712716
prevProps.report.managerEmail === nextProps.report.managerEmail &&
713717
prevProps.shouldHideThreadDividerLine === nextProps.shouldHideThreadDividerLine &&
714-
lodashGet(prevProps.report, 'total', 0) === lodashGet(nextProps.report, 'total', 0),
718+
lodashGet(prevProps.report, 'total', 0) === lodashGet(nextProps.report, 'total', 0) &&
719+
prevProps.linkedReportActionID === nextProps.linkedReportActionID,
715720
),
716721
);

src/pages/home/report/ReportActionsList.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import PropTypes from 'prop-types';
22
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
33
import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
44
import _ from 'underscore';
5+
import {useRoute} from '@react-navigation/native';
6+
import lodashGet from 'lodash/get';
57
import CONST from '../../../CONST';
68
import InvertedFlatList from '../../../components/InvertedFlatList';
79
import {withPersonalDetails} from '../../../components/OnyxProvider';
@@ -117,13 +119,15 @@ function ReportActionsList({
117119
const reportScrollManager = useReportScrollManager();
118120
const {translate} = useLocalize();
119121
const {isOffline} = useNetwork();
122+
const route = useRoute();
120123
const opacity = useSharedValue(0);
121124
const userActiveSince = useRef(null);
122125
const [currentUnreadMarker, setCurrentUnreadMarker] = useState(null);
123126
const scrollingVerticalOffset = useRef(0);
124127
const readActionSkipped = useRef(false);
125128
const reportActionSize = useRef(sortedReportActions.length);
126129
const firstRenderRef = useRef(true);
130+
const linkedReportActionID = lodashGet(route, 'params.reportActionID', '');
127131

128132
// This state is used to force a re-render when the user manually marks a message as unread
129133
// by using a timestamp you can force re-renders without having to worry about if another message was marked as unread before
@@ -303,6 +307,7 @@ function ReportActionsList({
303307
reportAction={reportAction}
304308
index={index}
305309
report={report}
310+
linkedReportActionID={linkedReportActionID}
306311
hasOutstandingIOU={hasOutstandingIOU}
307312
sortedReportActions={sortedReportActions}
308313
mostRecentIOUReportActionID={mostRecentIOUReportActionID}
@@ -311,7 +316,7 @@ function ReportActionsList({
311316
/>
312317
);
313318
},
314-
[report, hasOutstandingIOU, sortedReportActions, mostRecentIOUReportActionID, messageManuallyMarkedUnread, shouldHideThreadDividerLine, currentUnreadMarker],
319+
[report, linkedReportActionID, hasOutstandingIOU, sortedReportActions, mostRecentIOUReportActionID, messageManuallyMarkedUnread, shouldHideThreadDividerLine, currentUnreadMarker],
315320
);
316321

317322
// Native mobile does not render updates flatlist the changes even though component did update called.

src/pages/home/report/ReportActionsListItemRenderer.js

+6
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,15 @@ const propTypes = {
3333

3434
/** Should we display the new marker on top of the comment? */
3535
shouldDisplayNewMarker: PropTypes.bool.isRequired,
36+
37+
/** Linked report action ID */
38+
linkedReportActionID: PropTypes.string,
3639
};
3740

3841
const defaultProps = {
3942
mostRecentIOUReportActionID: '',
4043
hasOutstandingIOU: false,
44+
linkedReportActionID: '',
4145
};
4246

4347
function ReportActionsListItemRenderer({
@@ -49,6 +53,7 @@ function ReportActionsListItemRenderer({
4953
mostRecentIOUReportActionID,
5054
shouldHideThreadDividerLine,
5155
shouldDisplayNewMarker,
56+
linkedReportActionID,
5257
}) {
5358
const shouldDisplayParentAction =
5459
reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED &&
@@ -67,6 +72,7 @@ function ReportActionsListItemRenderer({
6772
shouldHideThreadDividerLine={shouldHideThreadDividerLine}
6873
report={report}
6974
action={reportAction}
75+
linkedReportActionID={linkedReportActionID}
7076
displayAsGroup={ReportActionsUtils.isConsecutiveActionMadeByPreviousActor(sortedReportActions, index)}
7177
shouldDisplayNewMarker={shouldDisplayNewMarker}
7278
shouldShowSubscriptAvatar={

src/pages/home/sidebar/SidebarLinks.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,12 @@ class SidebarLinks extends React.PureComponent {
141141
// or when clicking the active LHN row on large screens
142142
// or when continuously clicking different LHNs, only apply to small screen
143143
// since getTopmostReportId always returns on other devices
144-
if (this.props.isCreateMenuOpen || option.reportID === Navigation.getTopmostReportId() || (this.props.isSmallScreenWidth && this.props.isActiveReport(option.reportID))) {
144+
const reportActionID = Navigation.getTopmostReportActionId();
145+
if (
146+
this.props.isCreateMenuOpen ||
147+
(option.reportID === Navigation.getTopmostReportId() && !reportActionID) ||
148+
(this.props.isSmallScreenWidth && this.props.isActiveReport(option.reportID) && !reportActionID)
149+
) {
145150
return;
146151
}
147152
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(option.reportID));

0 commit comments

Comments
 (0)