Closed techievivek closed 9 months ago
Job added to Upwork: https://www.upwork.com/jobs/~01c9dae839c28297cd
Triggered auto assignment to Contributor-plus team member for initial proposal review - @getusha (External
)
This improvement was discussed here: https://expensify.slack.com/archives/C049HHMV9SM/p1697506231709359
I would like to work on it. According to the comment on the issue we need to change this lines of code to navigate the user to the last report using ReportUtils.findLastAccessedReport
https://github.com/Expensify/App/blob/3db5ff4a1924156c748291d25eb9d774d82f879e/src/libs/actions/Report.js#L2074-L2078
Drop user back to the last chat they were on when they leave a room
Actually when we leave room, we navigate to parent report if it's a thread, otherwise we will navigate to concierge chat
We can store a value in Onyx like LAST_VIEWED_REPORT_ID
. Whenever reportID
from route
is changed we will update this key if this reportID is valid.
And then when we leave the room we will goBack to the last report that is stored from this key. If the room is the first room we viewed we will calculate the last access reportID base on findLastAccessedReport
with excluding this room.
https://github.com/Expensify/App/blob/8ad83451972102ea0c638498424219b5614305c7/src/pages/home/ReportScreen.js#L313-L322
I think we should also remove this logic here because we already have navigate logic when leaving room in ReportScreen
https://github.com/Expensify/App/blob/3db5ff4a1924156c748291d25eb9d774d82f879e/src/libs/actions/Report.js#L2074-L2077
We want to drop the user back to the last chat they were on when they leave a room, instead of droping them to Concierge chat.
We are navigating to concierge chat after leaving the room here: https://github.com/Expensify/App/blob/c79529a66a05c6a924e24955229c6949ba198623/src/libs/actions/Report.js#L2074-L2078
we should implement a new function in ReportUtils called findPreviousAccessedReport. This function will determine the previous chat that the user was on
function findPreviousAccessedReport(reports) {
let sortedReports = sortReportsByLastRead(reports);
// Get the last report in the array, which is the current report.
const currentReport = sortedReports[sortedReports.length - 1];
// Find the index of the current report in the array.
const currentIndex = sortedReports.indexOf(currentReport);
if (currentIndex > 0) {
// If the current report is not the first report in the array,
// return the report before it as the previously accessed report.
return sortedReports[currentIndex - 1];
} else {
// If the current report is the first report in the array,
// there is no previously accessed report.
return null;
}
}
and the navigation logic should be updated as follows:
if (isWorkspaceMemberLeavingWorkspaceRoom) {
const previousReport = ReportUtils.findPreviousAccessedReport(allReports);
if(previousReport){
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(previousReport.reportID));
} else {
const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.CONCIERGE]);
const chat = ReportUtils.getChatByParticipants(participantAccountIDs);
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chat.reportID));
}
}
We can go back twice to get into the previous chat.
if (isWorkspaceMemberLeavingWorkspaceRoom) {
Navigation.goBack();
Navigation.goBack();
}
Contributor details Your Expensify account email: orshikh.dev@gmail.com Upwork Profile Link: https://www.upwork.com/freelancers/~01477bce71bc1bc694
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
@getusha Can you please review the above proposal, this is part of wave8 milestone and we want to get this shipped ASAP, thanks.
We can store a value in Onyx like LAST_VIEWED_REPORT_ID. Whenever reportID from route is changed we will update this key if this reportID is valid.
@dukenv0307 is there any reason, to store the value in Onyx? can't we do it without introducing a new key?
@getushaI think we can create a ref like prevReportID in ReportScreen to store the previous reportID. That will accept this will be reset when we refresh the page.
If it's not the expected we can use a new Onyx key.
@dukenv0307 could you point me where we will update the value of the ref from? i feel like there is a better way to get the navigation history and use it for this case instead of storing a new value.
We will use usePrevious hook to store the previous reportID and then whenever the previous and the current reporrID is different we will update the ref to previous value.
@dukenv0307 If the previous report is another room and when we leave that room again where will the navigation land? in this case we will have the previous room path saved and it'll navigate to a room that doesn't exist, that's why i am not confidence in using a ref or any sort of state to store the path.
@techievivek Should we also address the scenario of a reload? In my opinion, I believe we should simply navigate to the Concierge in such cases.
@getusha In this case I think we should go to the last access to prevent this case or do nothing or when we leave room, we can reuse getTopmostReportId with excluding the room that we are leaving to get the last reportID.
cc @techievivek can you please confirm the expected again.
@sourcecodedeveloper i was testing your proposal is there any reason you removed it for?
We can store a value in Onyx like LAST_VIEWED_REPORT_ID. Whenever reportID from route is changed we will update this key if this reportID is valid.
I don't think storing a single reportID will work, if we need to make it work we will need to store a stack of reportIDs in the order they were accessed. But that info is already available to us through navigation stack so maybe we can just reuse that?
@getusha
Should we also address the scenario of a reload? In my opinion, I believe we should simply navigate to the Concierge in such cases.
Do we reset the navigation stack when we reload the page, or does it persist? I would guess it persists so can't we figure out the last report they were on from it?
Can we not use findLastAccessedReport
or getTopmostReportId
to find the last report the user was on? If, in any case, we are unable to determine, then we can drop them to concierge chat.
But that info is already available to us through navigation stack so maybe we can just reuse that?
Yes i also that's something we should reuse. @dukenv0307 @rayane-djouah could you take a look at this approach and update your proposals if possible?
Do we reset the navigation stack when we reload the page, or does it persist? I would guess it persists so can't we figure out the last report they were on from it?
It think the state will reset incase of a reload, can confirm that logging navigationRef.getState()
.
@getusha I'm pretty sure the the stack is reset after reloading.
We have a problem that is if we visit a report many time. Navigation stack will have many screen of this report. And then when we leave this room, in navigation stack still have this screen.
Upwork job price has been updated to $1000
Doubled the pricing since this will require a little work debugging the navigation stack.
@techievivek Can you check my comment here https://github.com/Expensify/App/issues/30778#issuecomment-1792340348?
We have a problem that is if we visit a report many time. Navigation stack will have many screen of this report. And then when we leave this room, in navigation stack still have this screen.
In this case we can filter out the screen, i don't think it differs from filtering from stored paths, if take that approach.
I'm pretty sure the the stack is reset after reloading.
I see, but when I am testing this locally it correctly takes me back to the chats I accessed even after I have refreshed the page, how does that work? Is it because of findLastAccessedReport
?
We have a problem that is if we visit a report many time. Navigation stack will have many screen of this report. And then when we leave this room, in navigation stack still have this screen.
I am not sure how this is a problem. Can you explain this with an example of how you think this could be a problem? For now, I think we know which chat the user is leaving so we can look at the content of the navigation stack from the top and find the first report which doesn't match the given reportID and that will be our destination, no? The important thing that we will need to consider here is that we will need to pop all the entries until the destination reportID.
I think we should handle the following cases.
Case 1
#room1
#room1
Case 2
#room1
#room2
#room2
#room1
#room1
room1
if there is any, otherwise navigate to ConciergeCase 3
#room1
#room1
Advertised on slack https://expensify.slack.com/archives/C01GTK53T8Q/p1699258134099569
I am not sure how this is a problem. Can you explain this with an example of how you think this could be a problem?
Here is an example.
You can see navigation state in the video which we have two routes of report A. So it's hard to use navigation stack to detect the last viewed report.
https://github.com/Expensify/App/assets/129500732/9e6547bd-aae4-4137-8823-ff873212bf09
We want to drop the user back to the last chat they were on when they leave a room, instead of droping them to Concierge chat.
We are navigating to concierge chat after leaving the room here: https://github.com/Expensify/App/blob/c79529a66a05c6a924e24955229c6949ba198623/src/libs/actions/Report.js#L2074-L2078
we should implement a new function in Navigation.js called navigateToPreviousReport. This function will determine the previous chat that the user was on
function navigateToPreviousReport() {
const navigationState = navigationRef.current.getState();
console.log(navigationState);
let currentReportID = null;
let lastCentralPaneNavigator = null;
for (let i = navigationState.routes.length - 1; i >= 0; i--) {
const route = navigationState.routes[i];
if (route.name === "CentralPaneNavigator") {
lastCentralPaneNavigator = route;
currentReportID = route.params.params.reportID;
console.log("Found CentralPaneNavigator with Report ID:", currentReportID);
break;
}
}
console.log("Current Report ID:", currentReportID);
console.log("Last Central Pane Navigator:", lastCentralPaneNavigator);
if (lastCentralPaneNavigator && currentReportID) {
let previousReportID = null;
outerLoop: for (let i = navigationState.routes.length - 1; i >= 0; i--) {
const route = navigationState.routes[i];
if (route === lastCentralPaneNavigator) {
console.log("Skipping lastCentralPaneNavigator route");
continue;
}
if (route.name === "CentralPaneNavigator") {
console.log("route.params.params.reportID:");
console.log(route.params.params.reportID);
if (route.params.params.reportID !== currentReportID) {
previousReportID = route.params.params.reportID;
console.log("Found previous CentralPaneNavigator with Report ID:", previousReportID);
break outerLoop;
}
}
}
console.log("Previous Report ID:", previousReportID);
if (previousReportID) {
navigate(ROUTES.REPORT_WITH_ID.getRoute(previousReportID));
console.log("Navigating to Previous Report ID:", previousReportID);
return true;
} else {
console.log("No previous Report ID found. Navigation not performed.");
return false;
}
} else {
console.log("CentralPaneNavigator or currentReportID not found. Navigation not performed.");
return false;
}
}
and the navigation logic should be updated as follows:
if (isWorkspaceMemberLeavingWorkspaceRoom) {
Navigation.dismissModal();
const didNavigate = Navigation.navigateToPreviousReport();
if(!didNavigate){
//if there is no previous report found
//On mobile we always get here because the navigation state is reset
const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.CONCIERGE]);
const chat = ReportUtils.getChatByParticipants(participantAccountIDs);
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chat.reportID));
//On mobile this go to the chats screen
Navigation.navigate(ROUTES.HOME);
}
}
Result:
https://github.com/Expensify/App/assets/77965000/41193003-ce0d-4f17-9b0b-7b73091bee04
Late to the party but interesting conversation here. Going through the previous comments to see if I can put in my 2 cents here.
@getusha Can you please prioritize reviewing the above proposals? 🙏
@sourcecodedeveloper could you please share your branch so i can test it?
@techievivek based on the complexity of getting the last accessed report from the navigation state, it's must to store a stack of report ids to solve this issue.
lastReadTime
only updates when user reads some message by marking it as unread, else it won't update.findLastAccessibleReportID
function that will return the last accessible report ID.
sortReportsByLastRead
function.shouldReportBeInOptionList
function, as we want user to navigate to an accessible report.findLastAccessibleReportID
function that which will be like filterReportIDs
, to filter the current room from the list.report.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN
this condition for notification preference as when user leaves room we set notificationPreference
to hidden
to mark it as user left room.last
report ID from the list.leaveRoom
function
https://github.com/Expensify/App/blob/d5725c52bead4baa02ecec38915fc7c8c4316a4a/src/libs/actions/Report.js#L2024and we will add below code in isWorkspaceMemberLeavingWorkspaceRoom
condition scope.
const lastAccessibleReportID = ReportUtils.findLastAccessibleReportID( [reportID] );
if (lastAccessibleReportID) {
Navigation.dismissModal(lastAccessibleReportID);
return;
}
ReportScreen.js
.
https://github.com/Expensify/App/blob/d5725c52bead4baa02ecec38915fc7c8c4316a4a/src/pages/home/ReportScreen.js#L303-L312PR To Test: https://github.com/Expensify/App/pull/31003
@getusha
based on the complexity of getting the last accessed report from the navigation state, it's must to store a stack of report ids to solve this issue.
I don't find much benefit in keeping a collection of reportIDs apart from just helping with this particular use case, and it's possible that handling the associated logic could be complicated as well. I would still stick with creating a helper method to retrieve the reportID from the navigation state, even if that's a bit of work.
@rayane-djouah your proposal is not working.
https://github.com/Expensify/App/assets/75031127/62987c2b-84c9-47eb-ba8f-fa619b90c0cc
@getusha I tested it before and it was working. Could you please check if there is any console error?
@rayane-djouah there are no console errors
I will check it out during the day
Drop user back to the last chat they were on when they leave a room.
The root cause of this issue if that we redirect page to concierge after leave a room, see
To drop user to the most recently access chat, we need to trace the access records of opened reportIDs/chats and navigate to the most recent reportID after leaving a room.
To achieve it,
state
event listener of the Report
screen by changing this lineto
<Stack.Navigator
screenListeners={{
state: (e) => {
const reportID = lodashGet(e, 'data.state.routes[0].params.reportID', '');
if (reportID) {
console.log('state changed, reportID = ', reportID);
Report.updateRecentlyAccessedReportID(reportID);
}
},
}}
>
See https://reactnavigation.org/docs/navigation-events#state
updateRecentlyAccessedReportID
in Report.js
to trace the more recently accessed reportIDs, likelet recentlyAccessedReportIDs = [];
Onyx.connect({
key: ONYXKEYS.RECENTLY_ACCESSED_REPORT_ID_LIST,
waitForCollectionCallback: true,
callback: (val) => {
recentlyAccessedReportIDs = val;
},
});
/**
* Update recently accessed reportID
*
* @param {String} currentReportID
* @param {Boolean} shouldRemoveCurrentReportID
* @returns {String} Returns the most recent accessed reportID
*/
function updateRecentlyAccessedReportID(currentReportID, shouldRemoveCurrentReportID = false) {
const reportIDs = _.filter(recentlyAccessedReportIDs, (reportID) => reportID !== currentReportID);
if (!shouldRemoveCurrentReportID) {
reportIDs.push(currentReportID);
}
// We can discuss the number of reportIDs need to be saved
if (reportIDs.length > 10) {
reportIDs.shift();
}
console.log('current recentlyAccessedReportIDs = ', reportIDs);
// We want to save recently accessed reportIDs to Onyx to be used after refreshing the page
Onyx.set(ONYXKEYS.RECENTLY_ACCESSED_REPORT_ID_LIST, reportIDs);
return reportIDs.length > 0 ? reportIDs[reportIDs.length - 1] : '';
}
to
const lastAccessedReportID = updateRecentlyAccessedReportID(reportID, true);
if (lastAccessedReportID) {
Navigation.dismissModal(lastAccessedReportID);
} else {
const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.CONCIERGE]);
const chat = ReportUtils.getChatByParticipants(participantAccountIDs);
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chat.reportID));
}
N/A
@techievivek @getusha I also proposed a solution which uses state
event listener to trace the opened reportID instead of inspecting the state stack by brute force method. The code will be clean and maintainable. For example, in method updateRecentlyAccessedReportID we can get the report by reportID and apply necessary checks of report for future functions.
You can checkout this branch https://github.com/Expensify/App/compare/main...eh2077:App:30778-drop-user-to-most-recently-accessed-report-after-leaving-a-room, if you want to test it.
@eh2077 Please check this comment, we're not looking to store report IDs in Onyx.
@getusha any comments on my proposal https://github.com/Expensify/App/issues/30778#issuecomment-1797115161
@jeet-dhandha I will test your proposal, will it handle all of these cases?
@sourcecodedeveloper please check this comment https://github.com/Expensify/App/issues/30778#issuecomment-1798298915
@getusha
based on the complexity of getting the last accessed report from the navigation state, it's must to store a stack of report ids to solve this issue.
I don't find much benefit in keeping a collection of reportIDs apart from just helping with this particular use case, and it's possible that handling the associated logic could be complicated as well. I would still stick with creating a helper method to retrieve the reportID from the navigation state, even if that's a bit of work.
@techievivek I understand your concern that the solution will be hard to maintain and be complicated extend to new features if we find the reportID by inspecting the navigation state using brute force method.
So, I went through doc of react-native-navigation
and find that it provides event listener feature to help us trace the changes of navigator, see https://reactnavigation.org/docs/navigation-events/#state
By using the event listener, we can have an elegant way to trace accessed reportIDs and the stored reportIDs can be easily extended to new future features. See the updateRecentlyAccessedReportID
method in my proposal and branch. Given the solution is clean and maintainable, I think we can consider storing reportIDs.
Looking forward to your feedback. Appreciated!
I have added videos for two scenarios, i will add for remaining scenarios in few hours. Only one scenario is covered in the videos, rest 2 i will add.
Problem:
Currently, if you leave a Room or Group you are dropped into your Concierge DM. The best reasoning for this is that you might have a support question or request. This is an assumption that, when weighed up against other options, feels less intuitive or useful.
Solution:
Drop users into whatever chat they were previously viewing before the viewed the room or group they left. Dropping a user into a chat they were just viewing is more likely to be useful than dropping a user into a Concierge DM.
E.g.:
On mobile, you should be dropped into the "chats screen". Let me know if you have any questions or need clarification with anything, thanks.
This was reported here: https://expensify.slack.com/archives/C049HHMV9SM/p1697506231709359
Upwork Automation - Do Not Edit