diff --git a/src/lib/actions/Report.js b/src/lib/actions/Report.js index 1fdbef6965ba..7c7e98e3a549 100644 --- a/src/lib/actions/Report.js +++ b/src/lib/actions/Report.js @@ -11,74 +11,6 @@ import ExpensiMark from '../ExpensiMark'; import Notification from '../Notification'; import * as PersonalDetails from './PersonalDetails'; -/** - * Updates a report in the store with a new report action - * - * @param {string} reportID - * @param {object} reportAction - */ -function updateReportWithNewAction(reportID, reportAction) { - let currentUserEmail; - Ion.get(`${IONKEYS.REPORT}_${reportID}`, 'reportID') - .then((ionReportID) => { - // This is necessary for local development because there will be pusher events from other engineers with - // different reportIDs - if (!CONFIG.IS_IN_PRODUCTION && !ionReportID) { - throw new Error('report does not exist in the store, so ignoring new comments'); - } - - // Get the report history and return that to the next chain - return Ion.get(`${IONKEYS.REPORT_HISTORY}_${reportID}`); - }) - - // Look to see if the report action from pusher already exists or not (it would exist if it's a comment just - // written by the user). If the action doesn't exist, then update the unread flag on the report so the user - // knows there is a new comment - .then((reportHistory) => { - if (reportHistory && !reportHistory[reportAction.sequenceNumber]) { - Ion.merge(`${IONKEYS.REPORT}_${reportID}`, {hasUnread: true}); - } - return reportHistory || {}; - }) - - // Put the report action from pusher into the history, it's OK to overwrite it if it already exists - .then(reportHistory => ({ - ...reportHistory, - [reportAction.sequenceNumber]: reportAction, - })) - - // Put the report history back into Ion - .then(reportHistory => Ion.set(`${IONKEYS.REPORT_HISTORY}_${reportID}`, reportHistory)) - - // Check to see if we need to show a notification for this report - .then(() => Ion.get(IONKEYS.SESSION, 'email')) - .then((email) => { - currentUserEmail = email; - return Ion.get(IONKEYS.CURRENT_URL); - }) - .then((currentUrl) => { - // If this comment is from the current user we don't want to parrot whatever they wrote back to them. - if (reportAction.actorEmail === currentUserEmail) { - return; - } - - const currentReportID = Number(lodashGet(currentUrl.split('/'), [1], 0)); - - // If we are currently viewing this report do not show a notification. - if (reportID === currentReportID) { - return; - } - - Notification.showCommentNotification({ - reportAction, - onClick: () => { - // Navigate to this report onClick - Ion.set(IONKEYS.APP_REDIRECT_TO, `/${reportID}`); - } - }); - }); -} - /** * Checks the report to see if there are any unread history items * @@ -126,21 +58,6 @@ function getSimplifiedReportObject(report, accountID) { }; } -/** - * Initialize our pusher subscriptions to listen for new report comments - * - * @returns {Promise} - */ -function subscribeToReportCommentEvents() { - return Ion.get(IONKEYS.SESSION, 'accountID') - .then((accountID) => { - const pusherChannelName = `private-user-accountID-${accountID}`; - Pusher.subscribe(pusherChannelName, 'reportComment', (pushJSON) => { - updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction); - }); - }); -} - /** * Returns a generated report title based on the participants * @@ -159,27 +76,26 @@ function getChatReportName(sharedReportList, personalDetails, currentUserEmail) } /** - * Get all chat reports and provide the proper report name - * by fetching sharedReportList and personalDetails + * Fetches chat reports when provided a list of + * chat report IDs * - * @returns {Promise} + * @param {Array} chatList + * @return {Promise} */ -function fetchChatReports() { +function fetchChatReportsByIDs(chatList) { let currentUserEmail; let currentUserAccountID; let fetchedReports; - return Ion.get(IONKEYS.SESSION) .then((session) => { currentUserEmail = session.email; currentUserAccountID = session.accountID; - return queueRequest('Get', {returnValueList: 'chatList'}); + return queueRequest('Get', { + returnValueList: 'reportStuff', + reportIDList: chatList.join(','), + shouldLoadOptionalKeys: true, + }); }) - .then(({chatList}) => queueRequest('Get', { - returnValueList: 'reportStuff', - reportIDList: chatList, - shouldLoadOptionalKeys: true, - })) .then(({reports}) => { fetchedReports = reports; @@ -220,6 +136,111 @@ function fetchChatReports() { }); } +/** + * Updates a report in the store with a new report action + * + * @param {string} reportID + * @param {object} reportAction + */ +function updateReportWithNewAction(reportID, reportAction) { + let currentUserEmail; + Ion.get(`${IONKEYS.REPORT}_${reportID}`, 'reportID') + .then((ionReportID) => { + // This is necessary for local development because there will be pusher events from other engineers with + // different reportIDs. This means that while in development it's not possible to make new chats appear + // by creating chats then leaving comments in other windows. + if (!CONFIG.IS_IN_PRODUCTION && !ionReportID) { + throw new Error('report does not exist in the store, so ignoring new comments'); + } + + // When handling a realtime update for a chat that does not yet exist in our store we + // need to fetch it so that we can properly navigate to it. This enables us populate + // newly created chats in the LHN without requiring a full refresh of the app. + if (!ionReportID) { + return fetchChatReportsByIDs([reportID]) + .then(() => Ion.get(`${IONKEYS.REPORT_HISTORY}_${reportID}`)); + } + + // Get the report history and return that to the next chain + return Ion.get(`${IONKEYS.REPORT_HISTORY}_${reportID}`); + }) + + // Look to see if the report action from pusher already exists or not (it would exist if it's a comment just + // written by the user). If the action doesn't exist, then update the unread flag on the report so the user + // knows there is a new comment + .then((reportHistory) => { + if (reportHistory && !reportHistory[reportAction.sequenceNumber]) { + Ion.merge(`${IONKEYS.REPORT}_${reportID}`, {hasUnread: true}); + } + return reportHistory || {}; + }) + + // Put the report action from pusher into the history, it's OK to overwrite it if it already exists + .then(reportHistory => ({ + ...reportHistory, + [reportAction.sequenceNumber]: reportAction, + })) + + // Put the report history back into Ion + .then(reportHistory => Ion.set(`${IONKEYS.REPORT_HISTORY}_${reportID}`, reportHistory)) + + // Check to see if we need to show a notification for this report + .then(() => Ion.get(IONKEYS.SESSION, 'email')) + .then((email) => { + currentUserEmail = email; + return Ion.get(IONKEYS.CURRENT_URL); + }) + .then((currentUrl) => { + // If this comment is from the current user we don't want to parrot whatever they wrote back to them. + if (reportAction.actorEmail === currentUserEmail) { + return; + } + + const currentReportID = Number(lodashGet(currentUrl.split('/'), [1], 0)); + + // If we are currently viewing this report do not show a notification. + if (reportID === currentReportID) { + return; + } + + Notification.showCommentNotification({ + reportAction, + onClick: () => { + // Navigate to this report onClick + Ion.set(IONKEYS.APP_REDIRECT_TO, `/${reportID}`); + } + }); + }); +} + +/** + * Initialize our pusher subscriptions to listen for new report comments + * + * @returns {Promise} + */ +function subscribeToReportCommentEvents() { + return Ion.get(IONKEYS.SESSION, 'accountID') + .then((accountID) => { + const pusherChannelName = `private-user-accountID-${accountID}`; + Pusher.subscribe(pusherChannelName, 'reportComment', (pushJSON) => { + updateReportWithNewAction(pushJSON.reportID, pushJSON.reportAction); + }); + }); +} + +/** + * Get all chat reports and provide the proper report name + * by fetching sharedReportList and personalDetails + * + * @returns {Promise} + */ +function fetchChatReports() { + return queueRequest('Get', {returnValueList: 'chatList'}) + + // The string cast below is necessary as Get rvl='chatList' may return an int + .then(({chatList}) => fetchChatReportsByIDs(String(chatList).split(','))); +} + /** * Get all of our reports * diff --git a/src/page/home/sidebar/SidebarBottom.js b/src/page/home/sidebar/SidebarBottom.js index 65b166116e28..4bb589f5aa87 100644 --- a/src/page/home/sidebar/SidebarBottom.js +++ b/src/page/home/sidebar/SidebarBottom.js @@ -39,6 +39,14 @@ const SidebarBottom = ({myPersonalDetails, isOffline, insets}) => { styles.statusIndicator, isOffline ? styles.statusIndicatorOffline : styles.statusIndicatorOnline ]; + + // On the very first sign in or after clearing storage these + // details will not be present on the first render so we'll just + // return nothing for now. + if (!myPersonalDetails) { + return null; + } + return (