From 56de552c499bea8b795e6fea2cf94f32e58df8fb Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 3 Oct 2023 17:52:32 +0200 Subject: [PATCH 01/28] [TS migration] Migrate 'Report.js' lib to TypeScript --- src/ONYXKEYS.ts | 2 +- src/libs/EmojiUtils.js | 2 +- src/libs/actions/{Report.js => Report.ts} | 357 +++++++++++----------- 3 files changed, 179 insertions(+), 182 deletions(-) rename src/libs/actions/{Report.js => Report.ts} (91%) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 0a17d3a1d2f7..97508a154647 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -386,7 +386,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT]: string; [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES]: number; [ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE]: boolean; - [ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING]: boolean; + [ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING]: Record; [ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM]: boolean; [ONYXKEYS.COLLECTION.SECURITY_GROUP]: OnyxTypes.SecurityGroup; [ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction; diff --git a/src/libs/EmojiUtils.js b/src/libs/EmojiUtils.js index 80665541e24b..58ca274b3d5f 100644 --- a/src/libs/EmojiUtils.js +++ b/src/libs/EmojiUtils.js @@ -411,7 +411,7 @@ function suggestEmojis(text, lang, limit = CONST.AUTO_COMPLETE_SUGGESTER.MAX_AMO /** * Retrieve preferredSkinTone as Number to prevent legacy 'default' String value * - * @param {Number | String} val + * @param {Number | String | null} val * @returns {Number} */ const getPreferredSkinToneIndex = (val) => { diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.ts similarity index 91% rename from src/libs/actions/Report.js rename to src/libs/actions/Report.ts index e21d9fdd75c6..4be7822633a1 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.ts @@ -27,8 +27,11 @@ import * as Welcome from './Welcome'; import * as PersonalDetailsUtils from '../PersonalDetailsUtils'; import * as Environment from '../Environment/Environment'; import * as Session from './Session'; +import ReportAction from '../../types/onyx/ReportAction'; +import Report from '../../types/onyx/Report'; +import PersonalDetails from '../../types/onyx/PersonalDetails'; -let currentUserAccountID; +let currentUserAccountID: number | undefined; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { @@ -41,7 +44,7 @@ Onyx.connect({ }, }); -let preferredSkinTone; +let preferredSkinTone: number; Onyx.connect({ key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, callback: (val) => { @@ -49,58 +52,61 @@ Onyx.connect({ }, }); -const allReportActions = {}; +const allReportActions: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, - callback: (actions, key) => { - if (!key || !actions) { + callback: (action, key) => { + if (!key || !action) { return; } const reportID = CollectionUtils.extractCollectionItemID(key); - allReportActions[reportID] = actions; + allReportActions[reportID] = action; }, }); -const currentReportData = {}; +const currentReportData: Record = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, - callback: (data, key) => { - if (!key || !data) { + callback: (report, key) => { + if (!key || !report) { return; } const reportID = CollectionUtils.extractCollectionItemID(key); - currentReportData[reportID] = data; + currentReportData[reportID] = report; }, }); let isNetworkOffline = false; Onyx.connect({ key: ONYXKEYS.NETWORK, - callback: (val) => (isNetworkOffline = lodashGet(val, 'isOffline', false)), + callback: (val) => { + isNetworkOffline = val?.isOffline ?? false; + }, }); -let allPersonalDetails; +let allPersonalDetails: Record = {}; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => { - allPersonalDetails = val || {}; + allPersonalDetails = val ?? {}; }, }); -const allReports = {}; -let conciergeChatReportID; -const typingWatchTimers = {}; +const allReports: Record = {}; +let conciergeChatReportID: string | undefined; +const typingWatchTimers: Record = {}; /** * Get the private pusher channel name for a Report. - * - * @param {String} reportID - * @returns {String} */ -function getReportChannelName(reportID) { +function getReportChannelName(reportID: string): string { return `${CONST.PUSHER.PRIVATE_REPORT_CHANNEL_PREFIX}${reportID}${CONFIG.PUSHER.SUFFIX}`; } +type NonNormalizedStatus = Record & { + userLogin?: string; +}; + /** * There are 2 possibilities that we can receive via pusher for a user's typing/leaving status: * 1. The "new" way from New Expensify is passed as {[login]: Boolean} (e.g. {yuwen@expensify.com: true}), where the value @@ -108,15 +114,14 @@ function getReportChannelName(reportID) { * 2. The "old" way from e.com which is passed as {userLogin: login} (e.g. {userLogin: bstites@expensify.com}) * * This method makes sure that no matter which we get, we return the "new" format - * - * @param {Object} status - * @returns {Object} */ -function getNormalizedStatus(status) { - let normalizedStatus = status; +function getNormalizedStatus(status: NonNormalizedStatus): Record { + let normalizedStatus: Record; - if (_.first(_.keys(status)) === 'userLogin') { + if (status.userLogin) { normalizedStatus = {[status.userLogin]: true}; + } else { + normalizedStatus = status; } return normalizedStatus; @@ -124,10 +129,8 @@ function getNormalizedStatus(status) { /** * Initialize our pusher subscriptions to listen for someone typing in a report. - * - * @param {String} reportID */ -function subscribeToReportTypingEvents(reportID) { +function subscribeToReportTypingEvents(reportID: string) { if (!reportID) { return; } @@ -136,12 +139,12 @@ function subscribeToReportTypingEvents(reportID) { Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, {}); const pusherChannelName = getReportChannelName(reportID); - Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_TYPING, (typingStatus) => { + Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_TYPING, (typingStatus: NonNormalizedStatus) => { // If the pusher message comes from OldDot, we expect the typing status to be keyed by user // login OR by 'Concierge'. If the pusher message comes from NewDot, it is keyed by accountID // since personal details are keyed by accountID. const normalizedTypingStatus = getNormalizedStatus(typingStatus); - const accountIDOrLogin = _.first(_.keys(normalizedTypingStatus)); + const accountIDOrLogin = Object.keys(normalizedTypingStatus)[0]; if (!accountIDOrLogin) { return; @@ -159,22 +162,20 @@ function subscribeToReportTypingEvents(reportID) { // Wait for 1.5s of no additional typing events before setting the status back to false. typingWatchTimers[reportUserIdentifier] = setTimeout(() => { - const typingStoppedStatus = {}; + const typingStoppedStatus: Record = {}; typingStoppedStatus[accountIDOrLogin] = false; Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, typingStoppedStatus); delete typingWatchTimers[reportUserIdentifier]; }, 1500); }).catch((error) => { - Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', false, {errorType: error.type, pusherChannelName}); + Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', {errorType: error.type, pusherChannelName}); }); } /** * Initialize our pusher subscriptions to listen for someone leaving a room. - * - * @param {String} reportID */ -function subscribeToReportLeavingEvents(reportID) { +function subscribeToReportLeavingEvents(reportID: string) { if (!reportID) { return; } @@ -183,12 +184,12 @@ function subscribeToReportLeavingEvents(reportID) { Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, false); const pusherChannelName = getReportChannelName(reportID); - Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, (leavingStatus) => { + Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, (leavingStatus: NonNormalizedStatus) => { // If the pusher message comes from OldDot, we expect the leaving status to be keyed by user // login OR by 'Concierge'. If the pusher message comes from NewDot, it is keyed by accountID // since personal details are keyed by accountID. const normalizedLeavingStatus = getNormalizedStatus(leavingStatus); - const accountIDOrLogin = _.first(_.keys(normalizedLeavingStatus)); + const accountIDOrLogin = Object.keys(normalizedLeavingStatus)[0]; if (!accountIDOrLogin) { return; @@ -200,16 +201,14 @@ function subscribeToReportLeavingEvents(reportID) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, true); }).catch((error) => { - Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', false, {errorType: error.type, pusherChannelName}); + Log.hmmm('[Report] Failed to initially subscribe to Pusher channel', {errorType: error.type, pusherChannelName}); }); } /** * Remove our pusher subscriptions to listen for someone typing in a report. - * - * @param {String} reportID */ -function unsubscribeFromReportChannel(reportID) { +function unsubscribeFromReportChannel(reportID: string) { if (!reportID) { return; } @@ -221,10 +220,8 @@ function unsubscribeFromReportChannel(reportID) { /** * Remove our pusher subscriptions to listen for someone leaving a report. - * - * @param {String} reportID */ -function unsubscribeFromLeavingRoomReportChannel(reportID) { +function unsubscribeFromLeavingRoomReportChannel(reportID: string) { if (!reportID) { return; } @@ -240,9 +237,9 @@ let newActionSubscribers = []; /** * Enables the Report actions file to let the ReportActionsView know that a new comment has arrived in realtime for the current report * Add subscriber for report id - * @param {String} reportID - * @param {Function} callback - * @returns {Function} Remove subscriber for report id + * @param reportID + * @param callback + * @returns Remove subscriber for report id */ function subscribeToNewActionEvent(reportID, callback) { newActionSubscribers.push({callback, reportID}); @@ -254,9 +251,9 @@ function subscribeToNewActionEvent(reportID, callback) { /** * Notify the ReportActionsView that a new comment has arrived * - * @param {String} reportID - * @param {Number} accountID - * @param {String} reportActionID + * @param reportID + * @param accountID + * @param reportActionID */ function notifyNewAction(reportID, accountID, reportActionID) { const actionSubscriber = _.find(newActionSubscribers, (subscriber) => subscriber.reportID === reportID); @@ -274,9 +271,9 @@ function notifyNewAction(reportID, accountID, reportActionID) { * - Adding one attachment * - Add both a comment and attachment simultaneously * - * @param {String} reportID - * @param {String} [text] - * @param {Object} [file] + * @param reportID + * @param [text] + * @param [file] */ function addActions(reportID, text = '', file) { let reportCommentText = ''; @@ -416,9 +413,9 @@ function addActions(reportID, text = '', file) { * * Add an attachment and optional comment. * - * @param {String} reportID - * @param {File} file - * @param {String} [text] + * @param reportID + * @param file + * @param [text] */ function addAttachment(reportID, file, text = '') { addActions(reportID, text, file); @@ -427,8 +424,8 @@ function addAttachment(reportID, file, text = '') { /** * Add a single comment to a report * - * @param {String} reportID - * @param {String} text + * @param reportID + * @param text */ function addComment(reportID, text) { addActions(reportID, text); @@ -442,12 +439,12 @@ function reportActionsExist(reportID) { * Gets the latest page of report actions and updates the last read message * If a chat with the passed reportID is not found, we will create a chat based on the passed participantList * - * @param {String} reportID - * @param {Array} participantLoginList The list of users that are included in a new chat, not including the user creating it - * @param {Object} newReportObject The optimistic report object created when making a new chat, saved as optimistic data - * @param {String} parentReportActionID The parent report action that a thread was created from (only passed for new threads) - * @param {Boolean} isFromDeepLink Whether or not this report is being opened from a deep link - * @param {Array} participantAccountIDList The list of accountIDs that are included in a new chat, not including the user creating it + * @param reportID + * @param participantLoginList The list of users that are included in a new chat, not including the user creating it + * @param newReportObject The optimistic report object created when making a new chat, saved as optimistic data + * @param parentReportActionID The parent report action that a thread was created from (only passed for new threads) + * @param isFromDeepLink Whether or not this report is being opened from a deep link + * @param participantAccountIDList The list of accountIDs that are included in a new chat, not including the user creating it */ function openReport(reportID, participantLoginList = [], newReportObject = {}, parentReportActionID = '0', isFromDeepLink = false, participantAccountIDList = []) { const optimisticReportData = [ @@ -623,8 +620,8 @@ function openReport(reportID, participantLoginList = [], newReportObject = {}, p /** * This will find an existing chat, or create a new one if none exists, for the given user or set of users. It will then navigate to this chat. * - * @param {Array} userLogins list of user logins to start a chat report with. - * @param {Boolean} shouldDismissModal a flag to determine if we should dismiss modal before navigate to report or navigate to report directly. + * @param userLogins list of user logins to start a chat report with. + * @param shouldDismissModal a flag to determine if we should dismiss modal before navigate to report or navigate to report directly. */ function navigateToAndOpenReport(userLogins, shouldDismissModal = true) { let newChat = {}; @@ -649,7 +646,7 @@ function navigateToAndOpenReport(userLogins, shouldDismissModal = true) { /** * This will find an existing chat, or create a new one if none exists, for the given accountID or set of accountIDs. It will then navigate to this chat. * - * @param {Array} participantAccountIDs of user logins to start a chat report with. + * @param participantAccountIDs of user logins to start a chat report with. */ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs) { let newChat = {}; @@ -667,9 +664,9 @@ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs) { /** * This will navigate to an existing thread, or create a new one if necessary * - * @param {String} childReportID The reportID we are trying to open - * @param {Object} parentReportAction the parent comment of a thread - * @param {String} parentReportID The reportID of the parent + * @param childReportID The reportID we are trying to open + * @param parentReportAction the parent comment of a thread + * @param parentReportID The reportID of the parent * */ function navigateToAndOpenChildReport(childReportID = '0', parentReportAction = {}, parentReportID = '0') { @@ -703,7 +700,7 @@ function navigateToAndOpenChildReport(childReportID = '0', parentReportAction = /** * Get the latest report history without marking the report as read. * - * @param {String} reportID + * @param reportID */ function reconnect(reportID) { API.write( @@ -755,8 +752,8 @@ function reconnect(reportID) { * Gets the older actions that have not been read yet. * Normally happens when you scroll up on a chat, and the actions have not been read yet. * - * @param {String} reportID - * @param {String} reportActionID + * @param reportID + * @param reportActionID */ function readOldestAction(reportID, reportActionID) { API.read( @@ -800,8 +797,8 @@ function readOldestAction(reportID, reportActionID) { /** * Gets metadata info about links in the provided report action * - * @param {String} reportID - * @param {String} reportActionID + * @param reportID + * @param reportActionID */ function expandURLPreview(reportID, reportActionID) { API.read('ExpandURLPreview', { @@ -813,7 +810,7 @@ function expandURLPreview(reportID, reportActionID) { /** * Marks the new report actions as read * - * @param {String} reportID + * @param reportID */ function readNewestAction(reportID) { const lastReadTime = DateUtils.getDBTime(); @@ -840,8 +837,8 @@ function readNewestAction(reportID) { /** * Sets the last read time on a report * - * @param {String} reportID - * @param {String} reportActionCreated + * @param reportID + * @param reportActionCreated */ function markCommentAsUnread(reportID, reportActionCreated) { // If no action created date is provided, use the last action's @@ -874,8 +871,8 @@ function markCommentAsUnread(reportID, reportActionCreated) { /** * Toggles the pinned state of the report. * - * @param {Object} reportID - * @param {Boolean} isPinnedChat + * @param reportID + * @param isPinnedChat */ function togglePinnedState(reportID, isPinnedChat) { const pinnedValue = !isPinnedChat; @@ -903,8 +900,8 @@ function togglePinnedState(reportID, isPinnedChat) { * Saves the comment left by the user as they are typing. By saving this data the user can switch between chats, close * tab, refresh etc without worrying about loosing what they typed out. * - * @param {String} reportID - * @param {String} comment + * @param reportID + * @param comment */ function saveReportComment(reportID, comment) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, comment); @@ -912,8 +909,8 @@ function saveReportComment(reportID, comment) { /** * Saves the number of lines for the comment - * @param {String} reportID - * @param {Number} numberOfLines + * @param reportID + * @param numberOfLines */ function saveReportCommentNumberOfLines(reportID, numberOfLines) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}`, numberOfLines); @@ -922,9 +919,9 @@ function saveReportCommentNumberOfLines(reportID, numberOfLines) { /** * Immediate indication whether the report has a draft comment. * - * @param {String} reportID - * @param {Boolean} hasDraft - * @returns {Promise} + * @param reportID + * @param hasDraft + * @returns */ function setReportWithDraft(reportID, hasDraft) { return Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {hasDraft}); @@ -933,7 +930,7 @@ function setReportWithDraft(reportID, hasDraft) { /** * Broadcasts whether or not a user is typing on a report over the report's private pusher channel. * - * @param {String} reportID + * @param reportID */ function broadcastUserIsTyping(reportID) { const privateReportChannelName = getReportChannelName(reportID); @@ -944,7 +941,7 @@ function broadcastUserIsTyping(reportID) { /** * Broadcasts to the report's private pusher channel whether a user is leaving a report * - * @param {String} reportID + * @param reportID */ function broadcastUserIsLeavingRoom(reportID) { const privateReportChannelName = getReportChannelName(reportID); @@ -956,7 +953,7 @@ function broadcastUserIsLeavingRoom(reportID) { /** * When a report changes in Onyx, this fetches the report from the API if the report doesn't have a name * - * @param {Object} report + * @param report */ function handleReportChanged(report) { if (!report) { @@ -966,7 +963,7 @@ function handleReportChanged(report) { // It is possible that we optimistically created a DM/group-DM for a set of users for which a report already exists. // In this case, the API will let us know by returning a preexistingReportID. // We should clear out the optimistically created report and re-route the user to the preexisting report. - if (report && report.reportID && report.preexistingReportID) { + if (report?.reportID && report.preexistingReportID) { Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, null); // Only re-route them if they are still looking at the optimistically created report @@ -977,7 +974,7 @@ function handleReportChanged(report) { return; } - if (report && report.reportID) { + if (report?.reportID) { allReports[report.reportID] = report; if (ReportUtils.isConciergeChatReport(report)) { @@ -1000,8 +997,8 @@ Onyx.connect({ /** * Deletes a comment from the report, basically sets it as empty string * - * @param {String} reportID - * @param {Object} reportAction + * @param reportID + * @param reportAction */ function deleteReportComment(reportID, reportAction) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); @@ -1130,9 +1127,9 @@ function deleteReportComment(reportID, reportAction) { * links=["https://www.google.com"] * returns: "test https://www.google.com test" * - * @param {String} html - * @param {Array} links - * @returns {String} + * @param html + * @param links + * @returns */ const removeLinksFromHtml = (html, links) => { let htmlCopy = html.slice(); @@ -1147,9 +1144,9 @@ const removeLinksFromHtml = (html, links) => { /** * This function will handle removing only links that were purposely removed by the user while editing. * - * @param {String} newCommentText text of the comment after editing. - * @param {String} originalHtml original html of the comment before editing. - * @returns {String} + * @param newCommentText text of the comment after editing. + * @param originalHtml original html of the comment before editing. + * @returns */ const handleUserDeletedLinksInHtml = (newCommentText, originalHtml) => { const parser = new ExpensiMark(); @@ -1165,9 +1162,9 @@ const handleUserDeletedLinksInHtml = (newCommentText, originalHtml) => { /** * Saves a new message for a comment. Marks the comment as edited, which will be reflected in the UI. * - * @param {String} reportID - * @param {Object} originalReportAction - * @param {String} textForNewComment + * @param reportID + * @param originalReportAction + * @param textForNewComment */ function editReportComment(reportID, originalReportAction, textForNewComment) { const parser = new ExpensiMark(); @@ -1274,9 +1271,9 @@ function editReportComment(reportID, originalReportAction, textForNewComment) { /** * Saves the draft for a comment report action. This will put the comment into "edit mode" * - * @param {String} reportID - * @param {Object} reportAction - * @param {String} draftMessage + * @param reportID + * @param reportAction + * @param draftMessage */ function saveReportActionDraft(reportID, reportAction, draftMessage) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); @@ -1285,19 +1282,19 @@ function saveReportActionDraft(reportID, reportAction, draftMessage) { /** * Saves the number of lines for the report action draft - * @param {String} reportID - * @param {Number} reportActionID - * @param {Number} numberOfLines + * @param reportID + * @param reportActionID + * @param numberOfLines */ function saveReportActionDraftNumberOfLines(reportID, reportActionID, numberOfLines) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}_${reportActionID}`, numberOfLines); } /** - * @param {String} reportID - * @param {String} previousValue - * @param {String} newValue - * @param {boolean} navigate + * @param reportID + * @param previousValue + * @param newValue + * @param navigate */ function updateNotificationPreference(reportID, previousValue, newValue, navigate) { if (previousValue === newValue) { @@ -1327,9 +1324,9 @@ function updateNotificationPreference(reportID, previousValue, newValue, navigat } /** - * @param {String} reportID - * @param {String} previousValue - * @param {String} newValue + * @param reportID + * @param previousValue + * @param newValue */ function updateWelcomeMessage(reportID, previousValue, newValue) { // No change needed, navigate back @@ -1358,8 +1355,8 @@ function updateWelcomeMessage(reportID, previousValue, newValue) { } /** - * @param {Object} report - * @param {String} newValue + * @param report + * @param newValue */ function updateWriteCapabilityAndNavigate(report, newValue) { if (report.writeCapability === newValue) { @@ -1405,11 +1402,11 @@ function navigateToConciergeChat() { /** * Add a policy report (workspace room) optimistically and navigate to it. * - * @param {String} policyID - * @param {String} reportName - * @param {String} visibility - * @param {Array} policyMembersAccountIDs - * @param {String} writeCapability + * @param policyID + * @param reportName + * @param visibility + * @param policyMembersAccountIDs + * @param writeCapability */ function addPolicyReport(policyID, reportName, visibility, policyMembersAccountIDs, writeCapability = CONST.REPORT.WRITE_CAPABILITIES.ALL) { // The participants include the current user (admin), and for restricted rooms, the policy members. Participants must not be empty. @@ -1501,7 +1498,7 @@ function addPolicyReport(policyID, reportName, visibility, policyMembersAccountI /** * Deletes a report, along with its reportActions, any linked reports, and any linked IOU report. * - * @param {String} reportID + * @param reportID */ function deleteReport(reportID) { const report = allReports[reportID]; @@ -1521,13 +1518,13 @@ function deleteReport(reportID) { Onyx.multiSet(onyxData); // Delete linked IOU report - if (report && report.iouReportID) { + if (report?.iouReportID) { deleteReport(report.iouReportID); } } /** - * @param {String} reportID The reportID of the policy report (workspace room) + * @param reportID The reportID of the policy report (workspace room) */ function navigateToConciergeChatAndDeleteReport(reportID) { // Dismiss the current report screen and replace it with Concierge Chat @@ -1537,10 +1534,10 @@ function navigateToConciergeChatAndDeleteReport(reportID) { } /** - * @param {Object} policyRoomReport - * @param {Number} policyRoomReport.reportID - * @param {String} policyRoomReport.reportName - * @param {String} policyRoomName The updated name for the policy room + * @param policyRoomReport + * @param policyRoomReport.reportID + * @param policyRoomReport.reportName + * @param policyRoomName The updated name for the policy room */ function updatePolicyRoomNameAndNavigate(policyRoomReport, policyRoomName) { const reportID = policyRoomReport.reportID; @@ -1591,7 +1588,7 @@ function updatePolicyRoomNameAndNavigate(policyRoomReport, policyRoomName) { } /** - * @param {String} reportID The reportID of the policy room. + * @param reportID The reportID of the policy room. */ function clearPolicyRoomNameErrors(reportID) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { @@ -1605,18 +1602,18 @@ function clearPolicyRoomNameErrors(reportID) { } /** - * @param {String} reportID - * @param {Boolean} isComposerFullSize + * @param reportID + * @param isComposerFullSize */ function setIsComposerFullSize(reportID, isComposerFullSize) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`, isComposerFullSize); } /** - * @param {String} reportID - * @param {Object} action the associated report action (optional) - * @param {Boolean} isRemote whether or not this notification is a remote push notification - * @returns {Boolean} + * @param reportID + * @param action the associated report action (optional) + * @param isRemote whether or not this notification is a remote push notification + * @returns */ function shouldShowReportActionNotification(reportID, action = null, isRemote = false) { const tag = isRemote ? '[PushNotification]' : '[LocalNotification]'; @@ -1669,8 +1666,8 @@ function shouldShowReportActionNotification(reportID, action = null, isRemote = } /** - * @param {String} reportID - * @param {Object} reportAction + * @param reportID + * @param reportAction */ function showReportActionNotification(reportID, reportAction) { if (!shouldShowReportActionNotification(reportID, reportAction)) { @@ -1697,7 +1694,7 @@ function showReportActionNotification(reportID, reportAction) { /** * Clear the errors associated with the IOUs of a given report. * - * @param {String} reportID + * @param reportID */ function clearIOUError(reportID) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {errorFields: {iou: null}}); @@ -1706,17 +1703,17 @@ function clearIOUError(reportID) { /** * Returns true if the accountID has reacted to the report action (with the given skin tone). * Uses the NEW FORMAT for "emojiReactions" - * @param {String} accountID - * @param {Array} users - * @param {Number} [skinTone] - * @returns {boolean} + * @param accountID + * @param users + * @param [skinTone] + * @returns */ function hasAccountIDEmojiReacted(accountID, users, skinTone) { if (_.isUndefined(skinTone)) { return Boolean(users[accountID]); } const usersReaction = users[accountID]; - if (!usersReaction || !usersReaction.skinTones || !_.size(usersReaction.skinTones)) { + if (!usersReaction?.skinTones || !_.size(usersReaction.skinTones)) { return false; } return Boolean(usersReaction.skinTones[skinTone]); @@ -1725,13 +1722,13 @@ function hasAccountIDEmojiReacted(accountID, users, skinTone) { /** * Adds a reaction to the report action. * Uses the NEW FORMAT for "emojiReactions" - * @param {String} reportID - * @param {String} reportActionID - * @param {Object} emoji - * @param {String} emoji.name - * @param {String} emoji.code - * @param {String[]} [emoji.types] - * @param {Number} [skinTone] + * @param reportID + * @param reportActionID + * @param emoji + * @param emoji.name + * @param emoji.code + * @param [emoji.types] + * @param [skinTone] */ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredSkinTone) { const createdAt = moment().utc().format(CONST.DATE.SQL_DATE_TIME); @@ -1769,12 +1766,12 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS /** * Removes a reaction to the report action. * Uses the NEW FORMAT for "emojiReactions" - * @param {String} reportID - * @param {String} reportActionID - * @param {Object} emoji - * @param {String} emoji.name - * @param {String} emoji.code - * @param {String[]} [emoji.types] + * @param reportID + * @param reportActionID + * @param emoji + * @param emoji.name + * @param emoji.code + * @param [emoji.types] */ function removeEmojiReaction(reportID, reportActionID, emoji) { const optimisticData = [ @@ -1804,11 +1801,11 @@ function removeEmojiReaction(reportID, reportActionID, emoji) { /** * Calls either addEmojiReaction or removeEmojiReaction depending on if the current user has reacted to the report action. * Uses the NEW FORMAT for "emojiReactions" - * @param {String} reportID - * @param {Object} reportAction - * @param {Object} reactionObject - * @param {Object} existingReactions - * @param {Number} [paramSkinTone] + * @param reportID + * @param reportAction + * @param reactionObject + * @param existingReactions + * @param [paramSkinTone] */ function toggleEmojiReaction(reportID, reportAction, reactionObject, existingReactions, paramSkinTone = preferredSkinTone) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); @@ -1835,8 +1832,8 @@ function toggleEmojiReaction(reportID, reportAction, reactionObject, existingRea } /** - * @param {String|null} url - * @param {Boolean} isAuthenticated + * @param url + * @param isAuthenticated */ function openReportFromDeepLink(url, isAuthenticated) { const route = ReportUtils.getRouteFromLink(url); @@ -1874,7 +1871,7 @@ function getCurrentUserAccountID() { /** * Leave a report by setting the state to submitted and closed * - * @param {String} reportID + * @param reportID */ function leaveRoom(reportID) { const report = lodashGet(allReports, [reportID], {}); @@ -1934,7 +1931,7 @@ function leaveRoom(reportID) { } /** - * @param {String} reportID + * @param reportID */ function setLastOpenedPublicRoom(reportID) { Onyx.set(ONYXKEYS.LAST_OPENED_PUBLIC_ROOM_ID, reportID); @@ -1943,7 +1940,7 @@ function setLastOpenedPublicRoom(reportID) { /** * Navigates to the last opened public room * - * @param {String} lastOpenedPublicRoomID + * @param lastOpenedPublicRoomID */ function openLastOpenedPublicRoom(lastOpenedPublicRoomID) { Navigation.isNavigationReady().then(() => { @@ -1955,9 +1952,9 @@ function openLastOpenedPublicRoom(lastOpenedPublicRoomID) { /** * Flag a comment as offensive * - * @param {String} reportID - * @param {Object} reportAction - * @param {String} severity + * @param reportID + * @param reportAction + * @param severity */ function flagComment(reportID, reportAction, severity) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); @@ -2040,9 +2037,9 @@ function flagComment(reportID, reportAction, severity) { /** * Updates a given user's private notes on a report * - * @param {String} reportID - * @param {Number} accountID - * @param {String} note + * @param reportID + * @param accountID + * @param note */ const updatePrivateNotes = (reportID, accountID, note) => { const optimisticData = [ @@ -2103,7 +2100,7 @@ const updatePrivateNotes = (reportID, accountID, note) => { /** * Fetches all the private notes for a given report * - * @param {String} reportID + * @param reportID */ function getReportPrivateNote(reportID) { if (_.isEmpty(reportID)) { @@ -2149,8 +2146,8 @@ function getReportPrivateNote(reportID) { /** * Checks if there are any errors in the private notes for a given report * - * @param {Object} report - * @returns {Boolean} Returns true if there are errors in any of the private notes on the report + * @param report + * @returns Returns true if there are errors in any of the private notes on the report */ function hasErrorInPrivateNotes(report) { const privateNotes = lodashGet(report, 'privateNotes', {}); @@ -2160,8 +2157,8 @@ function hasErrorInPrivateNotes(report) { /** * Clears all errors associated with a given private note * - * @param {String} reportID - * @param {Number} accountID + * @param reportID + * @param accountID */ function clearPrivateNotesError(reportID, accountID) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {privateNotes: {[accountID]: {errors: null}}}); From 12691108a23949c93c006c480af23f03848231f3 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 6 Oct 2023 16:46:52 +0200 Subject: [PATCH 02/28] Migrate first 1000 lines of Report.ts --- src/libs/API.js | 2 +- src/libs/Navigation/Navigation.js | 2 +- src/libs/ReportUtils.js | 2 +- src/libs/actions/Report.ts | 159 +++++++++++------------------- src/libs/actions/Task.js | 1 - src/types/onyx/Report.ts | 2 + 6 files changed, 65 insertions(+), 103 deletions(-) diff --git a/src/libs/API.js b/src/libs/API.js index 2ad1f32347d9..50d5fa3ad3fa 100644 --- a/src/libs/API.js +++ b/src/libs/API.js @@ -128,7 +128,7 @@ function makeRequestWithSideEffects(command, apiCommandParameters = {}, onyxData * * @param {String} command - Name of API command to call. * @param {Object} apiCommandParameters - Parameters to send to the API. - * @param {Object} onyxData - Object containing errors, loading states, and optimistic UI data that will be merged + * @param {Object} [onyxData] - Object containing errors, loading states, and optimistic UI data that will be merged * into Onyx before and after a request is made. Each nested object will be formatted in * the same way as an API response. * @param {Object} [onyxData.optimisticData] - Onyx instructions that will be passed to Onyx.update() before the request is made. diff --git a/src/libs/Navigation/Navigation.js b/src/libs/Navigation/Navigation.js index de6162685079..5cbd8e9b0af6 100644 --- a/src/libs/Navigation/Navigation.js +++ b/src/libs/Navigation/Navigation.js @@ -77,7 +77,7 @@ const getActiveRouteIndex = function (route, index) { /** * Main navigation method for redirecting to a route. * @param {String} route - * @param {String} type - Type of action to perform. Currently UP is supported. + * @param {String} [type] - Type of action to perform. Currently UP is supported. */ function navigate(route = ROUTES.HOME, type) { if (!canNavigate('navigate', {route})) { diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 7d3e1a2b7098..f498e59537db 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -2039,7 +2039,7 @@ function updateOptimisticParentReportAction(parentReportAction, lastVisibleActio /** * Get optimistic data of parent report action * @param {String} reportID The reportID of the report that is updated - * @param {String} lastVisibleActionCreated Last visible action created of the child report + * @param {String} [lastVisibleActionCreated] Last visible action created of the child report * @param {String} type The type of action in the child report * @param {String} parentReportID Custom reportID to be updated * @param {String} parentReportActionID Custom reportActionID to be updated diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 552a093fde76..9d09440136eb 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2,7 +2,7 @@ import {InteractionManager} from 'react-native'; import _ from 'underscore'; import lodashGet from 'lodash/get'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; -import Onyx from 'react-native-onyx'; +import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; import moment from 'moment'; import ONYXKEYS from '../../ONYXKEYS'; @@ -30,6 +30,7 @@ import * as Session from './Session'; import ReportAction from '../../types/onyx/ReportAction'; import Report from '../../types/onyx/Report'; import PersonalDetails from '../../types/onyx/PersonalDetails'; +import {type} from 'os'; let currentUserAccountID: number | undefined; Onyx.connect({ @@ -52,7 +53,7 @@ Onyx.connect({ }, }); -const allReportActions: Record = {}; +const allReportActions: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, callback: (action, key) => { @@ -64,7 +65,7 @@ Onyx.connect({ }, }); -const currentReportData: Record = {}; +const currentReportData: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT, callback: (report, key) => { @@ -84,7 +85,7 @@ Onyx.connect({ }, }); -let allPersonalDetails: Record = {}; +let allPersonalDetails: OnyxEntry> = {}; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => { @@ -231,31 +232,32 @@ function unsubscribeFromLeavingRoomReportChannel(reportID: string) { Pusher.unsubscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM); } +type SubscriberCallback = (isFromCurrentUser: boolean, reportActionID: string) => void; + +type ActionSubscriber = { + reportID: string; + callback: SubscriberCallback; +}; + // New action subscriber array for report pages -let newActionSubscribers = []; +let newActionSubscribers: ActionSubscriber[] = []; /** * Enables the Report actions file to let the ReportActionsView know that a new comment has arrived in realtime for the current report * Add subscriber for report id - * @param reportID - * @param callback * @returns Remove subscriber for report id */ -function subscribeToNewActionEvent(reportID, callback) { +function subscribeToNewActionEvent(reportID: string, callback: SubscriberCallback): () => void { newActionSubscribers.push({callback, reportID}); return () => { - newActionSubscribers = _.filter(newActionSubscribers, (subscriber) => subscriber.reportID !== reportID); + newActionSubscribers = newActionSubscribers.filter((subscriber) => subscriber.reportID !== reportID); }; } /** * Notify the ReportActionsView that a new comment has arrived - * - * @param reportID - * @param accountID - * @param reportActionID */ -function notifyNewAction(reportID, accountID, reportActionID) { +function notifyNewAction(reportID: string, accountID: number, reportActionID: string) { const actionSubscriber = _.find(newActionSubscribers, (subscriber) => subscriber.reportID === reportID); if (!actionSubscriber) { return; @@ -270,12 +272,8 @@ function notifyNewAction(reportID, accountID, reportActionID) { * - Adding one comment * - Adding one attachment * - Add both a comment and attachment simultaneously - * - * @param reportID - * @param [text] - * @param [file] */ -function addActions(reportID, text = '', file) { +function addActions(reportID: string, text = '', file?: File) { let reportCommentText = ''; let reportCommentAction; let attachmentAction; @@ -417,7 +415,7 @@ function addActions(reportID, text = '', file) { * @param file * @param [text] */ -function addAttachment(reportID, file, text = '') { +function addAttachment(reportID: string, file: File, text = '') { addActions(reportID, text, file); } @@ -427,12 +425,12 @@ function addAttachment(reportID, file, text = '') { * @param reportID * @param text */ -function addComment(reportID, text) { +function addComment(reportID: string, text: string) { addActions(reportID, text); } -function reportActionsExist(reportID) { - return allReportActions[reportID] !== undefined; +function reportActionsExist(reportID: string): boolean { + return allReportActions?.[reportID] !== undefined; } /** @@ -446,8 +444,8 @@ function reportActionsExist(reportID) { * @param isFromDeepLink Whether or not this report is being opened from a deep link * @param participantAccountIDList The list of accountIDs that are included in a new chat, not including the user creating it */ -function openReport(reportID, participantLoginList = [], newReportObject = {}, parentReportActionID = '0', isFromDeepLink = false, participantAccountIDList = []) { - const optimisticReportData = [ +function openReport(reportID: string, participantLoginList = [], newReportObject = {}, parentReportActionID = '0', isFromDeepLink = false, participantAccountIDList = []) { + const optimisticReportData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -467,7 +465,7 @@ function openReport(reportID, participantLoginList = [], newReportObject = {}, p }, ]; - const reportSuccessData = [ + const reportSuccessData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -490,7 +488,7 @@ function openReport(reportID, participantLoginList = [], newReportObject = {}, p }, ]; - const reportFailureData = [ + const reportFailureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, @@ -623,7 +621,7 @@ function openReport(reportID, participantLoginList = [], newReportObject = {}, p * @param userLogins list of user logins to start a chat report with. * @param shouldDismissModal a flag to determine if we should dismiss modal before navigate to report or navigate to report directly. */ -function navigateToAndOpenReport(userLogins, shouldDismissModal = true) { +function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true) { let newChat = {}; const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(userLogins); @@ -648,7 +646,7 @@ function navigateToAndOpenReport(userLogins, shouldDismissModal = true) { * * @param participantAccountIDs of user logins to start a chat report with. */ -function navigateToAndOpenReportWithAccountIDs(participantAccountIDs) { +function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[]) { let newChat = {}; const chat = ReportUtils.getChatByParticipants(participantAccountIDs); if (!chat) { @@ -667,7 +665,6 @@ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs) { * @param childReportID The reportID we are trying to open * @param parentReportAction the parent comment of a thread * @param parentReportID The reportID of the parent - * */ function navigateToAndOpenChildReport(childReportID = '0', parentReportAction = {}, parentReportID = '0') { if (childReportID !== '0') { @@ -699,10 +696,8 @@ function navigateToAndOpenChildReport(childReportID = '0', parentReportAction = /** * Get the latest report history without marking the report as read. - * - * @param reportID */ -function reconnect(reportID) { +function reconnect(reportID: string) { API.write( 'ReconnectToReport', { @@ -751,11 +746,8 @@ function reconnect(reportID) { /** * Gets the older actions that have not been read yet. * Normally happens when you scroll up on a chat, and the actions have not been read yet. - * - * @param reportID - * @param reportActionID */ -function readOldestAction(reportID, reportActionID) { +function readOldestAction(reportID: string, reportActionID: string) { API.read( 'ReadOldestAction', { @@ -796,11 +788,8 @@ function readOldestAction(reportID, reportActionID) { /** * Gets metadata info about links in the provided report action - * - * @param reportID - * @param reportActionID */ -function expandURLPreview(reportID, reportActionID) { +function expandURLPreview(reportID: string, reportActionID: string) { API.read('ExpandURLPreview', { reportID, reportActionID, @@ -809,10 +798,8 @@ function expandURLPreview(reportID, reportActionID) { /** * Marks the new report actions as read - * - * @param reportID */ -function readNewestAction(reportID) { +function readNewestAction(reportID: string) { const lastReadTime = DateUtils.getDBTime(); API.write( 'ReadNewestAction', @@ -836,11 +823,8 @@ function readNewestAction(reportID) { /** * Sets the last read time on a report - * - * @param reportID - * @param reportActionCreated */ -function markCommentAsUnread(reportID, reportActionCreated) { +function markCommentAsUnread(reportID: string, reportActionCreated: boolean) { // If no action created date is provided, use the last action's const actionCreationTime = reportActionCreated || lodashGet(allReports, [reportID, 'lastVisibleActionCreated'], DateUtils.getDBTime(new Date(0))); @@ -870,11 +854,8 @@ function markCommentAsUnread(reportID, reportActionCreated) { /** * Toggles the pinned state of the report. - * - * @param reportID - * @param isPinnedChat */ -function togglePinnedState(reportID, isPinnedChat) { +function togglePinnedState(reportID: string, isPinnedChat: boolean) { const pinnedValue = !isPinnedChat; // Optimistically pin/unpin the report before we send out the command @@ -899,63 +880,55 @@ function togglePinnedState(reportID, isPinnedChat) { /** * Saves the comment left by the user as they are typing. By saving this data the user can switch between chats, close * tab, refresh etc without worrying about loosing what they typed out. - * - * @param reportID - * @param comment */ -function saveReportComment(reportID, comment) { +function saveReportComment(reportID: string, comment: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, comment); } /** * Saves the number of lines for the comment - * @param reportID - * @param numberOfLines */ -function saveReportCommentNumberOfLines(reportID, numberOfLines) { +function saveReportCommentNumberOfLines(reportID: string, numberOfLines: number) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}`, numberOfLines); } /** * Immediate indication whether the report has a draft comment. - * - * @param reportID - * @param hasDraft - * @returns */ -function setReportWithDraft(reportID, hasDraft) { +function setReportWithDraft(reportID: string, hasDraft: boolean) { return Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {hasDraft}); } /** * Broadcasts whether or not a user is typing on a report over the report's private pusher channel. - * - * @param reportID */ -function broadcastUserIsTyping(reportID) { +function broadcastUserIsTyping(reportID: string) { const privateReportChannelName = getReportChannelName(reportID); - const typingStatus = {}; - typingStatus[currentUserAccountID] = true; + const typingStatus: Record = currentUserAccountID + ? { + [currentUserAccountID]: true, + } + : {}; Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_TYPING, typingStatus); } + /** * Broadcasts to the report's private pusher channel whether a user is leaving a report - * - * @param reportID */ -function broadcastUserIsLeavingRoom(reportID) { +function broadcastUserIsLeavingRoom(reportID: string) { const privateReportChannelName = getReportChannelName(reportID); - const leavingStatus = {}; - leavingStatus[currentUserAccountID] = true; + const leavingStatus: Record = currentUserAccountID + ? { + [currentUserAccountID]: true, + } + : {}; Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, leavingStatus); } /** * When a report changes in Onyx, this fetches the report from the API if the report doesn't have a name - * - * @param report */ -function handleReportChanged(report) { +function handleReportChanged(report: OnyxEntry) { if (!report) { return; } @@ -996,13 +969,15 @@ Onyx.connect({ /** * Deletes a comment from the report, basically sets it as empty string - * - * @param reportID - * @param reportAction */ -function deleteReportComment(reportID, reportAction) { +function deleteReportComment(reportID: string, reportAction: ReportAction) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); const reportActionID = reportAction.reportActionID; + + if (!reportActionID) { + return; + } + const deletedMessage = [ { translationKey: '', @@ -1025,7 +1000,7 @@ function deleteReportComment(reportID, reportAction) { // If we are deleting the last visible message, let's find the previous visible one (or set an empty one if there are none) and update the lastMessageText in the LHN. // Similarly, if we are deleting the last read comment we will want to update the lastVisibleActionCreated to use the previous visible message. - let optimisticReport = { + let optimisticReport: Partial = { lastMessageTranslationKey: '', lastMessageText: '', lastVisibleActionCreated: '', @@ -1053,7 +1028,7 @@ function deleteReportComment(reportID, reportAction) { // If the API call fails we must show the original message again, so we revert the message content back to how it was // and and remove the pendingAction so the strike-through clears - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${originalReportID}`, @@ -1067,7 +1042,7 @@ function deleteReportComment(reportID, reportAction) { }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${originalReportID}`, @@ -1080,7 +1055,7 @@ function deleteReportComment(reportID, reportAction) { }, ]; - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${originalReportID}`, @@ -1103,16 +1078,6 @@ function deleteReportComment(reportID, reportAction) { optimisticData.push(optimisticParentReportData); } - // Check to see if the report action we are deleting is the first comment on a thread report. In this case, we need to trigger - // an update to let the LHN know that the parentReportAction is now deleted. - if (ReportUtils.isThreadFirstChat(reportAction, reportID)) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: {updateReportInLHN: true}, - }); - } - const parameters = { reportID: originalReportID, reportActionID, @@ -1126,10 +1091,6 @@ function deleteReportComment(reportID, reportAction) { * html="test https://www.google.com test" * links=["https://www.google.com"] * returns: "test https://www.google.com test" - * - * @param html - * @param links - * @returns */ const removeLinksFromHtml = (html, links) => { let htmlCopy = html.slice(); diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 8e9a76f4096a..a4639e3e7498 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -766,7 +766,6 @@ function cancelTask(taskReportID, taskTitle, originalStateNum, originalStatusNum lastVisibleActionCreated: optimisticCancelReportAction.created, lastMessageText: message, lastActorAccountID: optimisticCancelReportAction.actorAccountID, - updateReportInLHN: true, isDeletedParentAction: true, }, }, diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index ca81e2c2946a..c43525f3bca4 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -74,6 +74,8 @@ type Report = { preexistingReportID?: string; /** If the report contains nonreimbursable expenses, send the nonreimbursable total */ nonReimbursableTotal?: number; + lastMessageTranslationKey?: string; + isLastMessageDeletedParentAction?: boolean; }; export default Report; From dcb222db133d1c4058d1dcd0e3a6518a26822801 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 9 Oct 2023 12:51:03 +0200 Subject: [PATCH 03/28] Finish migrating Report.ts --- src/ONYXKEYS.ts | 2 +- src/libs/actions/Report.ts | 435 ++++++++++-------------- src/types/onyx/OriginalMessage.ts | 7 +- src/types/onyx/Report.ts | 13 +- src/types/onyx/ReportAction.ts | 16 +- src/types/onyx/ReportActionReactions.ts | 2 + 6 files changed, 209 insertions(+), 266 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 97508a154647..20737e41a68a 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -380,7 +380,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT]: Record; [ONYXKEYS.COLLECTION.REPORT]: OnyxTypes.Report; [ONYXKEYS.COLLECTION.REPORT_METADATA]: OnyxTypes.ReportMetadata; - [ONYXKEYS.COLLECTION.REPORT_ACTIONS]: OnyxTypes.ReportAction; + [ONYXKEYS.COLLECTION.REPORT_ACTIONS]: Record; [ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS]: string; [ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS]: OnyxTypes.ReportActionReactions; [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT]: string; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 9d09440136eb..a62e5b68ed44 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -5,6 +5,7 @@ import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Str from 'expensify-common/lib/str'; import moment from 'moment'; +import isEmpty from 'lodash/isEmpty'; import ONYXKEYS from '../../ONYXKEYS'; import * as Pusher from '../Pusher/pusher'; import LocalNotification from '../Notification/LocalNotification'; @@ -27,10 +28,11 @@ import * as Welcome from './Welcome'; import * as PersonalDetailsUtils from '../PersonalDetailsUtils'; import * as Environment from '../Environment/Environment'; import * as Session from './Session'; -import ReportAction from '../../types/onyx/ReportAction'; -import Report from '../../types/onyx/Report'; +import ReportAction, {Message, ModerationDecision, ReportActionBase} from '../../types/onyx/ReportAction'; +import Report, {NotificationPreference, WriteCapability} from '../../types/onyx/Report'; import PersonalDetails from '../../types/onyx/PersonalDetails'; -import {type} from 'os'; +import {OriginalMessageIOU} from '../../types/onyx/OriginalMessage'; +import {User} from '../../types/onyx/ReportActionReactions'; let currentUserAccountID: number | undefined; Onyx.connect({ @@ -53,7 +55,7 @@ Onyx.connect({ }, }); -const allReportActions: OnyxCollection = {}; +const allReportActions: OnyxCollection> = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, callback: (action, key) => { @@ -258,7 +260,7 @@ function subscribeToNewActionEvent(reportID: string, callback: SubscriberCallbac * Notify the ReportActionsView that a new comment has arrived */ function notifyNewAction(reportID: string, accountID: number, reportActionID: string) { - const actionSubscriber = _.find(newActionSubscribers, (subscriber) => subscriber.reportID === reportID); + const actionSubscriber = newActionSubscribers.find((subscriber) => subscriber.reportID === reportID); if (!actionSubscriber) { return; } @@ -344,7 +346,10 @@ function addActions(reportID: string, text = '', file?: File) { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: _.mapObject(optimisticReportActions, () => ({pendingAction: null})), + value: Object.entries(optimisticReportActions).reduce((prev, [key]) => { + prev[key] = {pendingAction: null}; + return prev; + }, {}), }, ]; @@ -374,16 +379,19 @@ function addActions(reportID: string, text = '', file?: File) { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: _.mapObject(optimisticReportActions, (action) => ({ - ...action, - errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), - })), + value: Object.entries(optimisticReportActions).reduce((prev, [key, action]) => { + prev[key] = { + ...action, + errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), + }; + return prev; + }, {}), }, ]; // Update optimistic data for parent report action if the report is a child report const optimisticParentReportData = ReportUtils.getOptimisticDataForParentReportAction(reportID, currentTime, CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); - if (!_.isEmpty(optimisticParentReportData)) { + if (!isEmpty(optimisticParentReportData)) { optimisticData.push(optimisticParentReportData); } @@ -410,10 +418,6 @@ function addActions(reportID: string, text = '', file?: File) { /** * * Add an attachment and optional comment. - * - * @param reportID - * @param file - * @param [text] */ function addAttachment(reportID: string, file: File, text = '') { addActions(reportID, text, file); @@ -421,9 +425,6 @@ function addAttachment(reportID: string, file: File, text = '') { /** * Add a single comment to a report - * - * @param reportID - * @param text */ function addComment(reportID: string, text: string) { addActions(reportID, text); @@ -437,7 +438,6 @@ function reportActionsExist(reportID: string): boolean { * Gets the latest page of report actions and updates the last read message * If a chat with the passed reportID is not found, we will create a chat based on the passed participantList * - * @param reportID * @param participantLoginList The list of users that are included in a new chat, not including the user creating it * @param newReportObject The optimistic report object created when making a new chat, saved as optimistic data * @param parentReportActionID The parent report action that a thread was created from (only passed for new threads) @@ -1092,9 +1092,9 @@ function deleteReportComment(reportID: string, reportAction: ReportAction) { * links=["https://www.google.com"] * returns: "test https://www.google.com test" */ -const removeLinksFromHtml = (html, links) => { +const removeLinksFromHtml = (html: string, links: string[]): string => { let htmlCopy = html.slice(); - _.forEach(links, (link) => { + links.forEach((link) => { // We want to match the anchor tag of the link and replace the whole anchor tag with the text of the anchor tag const regex = new RegExp(`<(a)[^><]*href\\s*=\\s*(['"])(${Str.escapeForRegExp(link)})\\2(?:".*?"|'.*?'|[^'"><])*>([\\s\\S]*?)<\\/\\1>(?![^<]*(<\\/pre>|<\\/code>))`, 'g'); htmlCopy = htmlCopy.replace(regex, '$4'); @@ -1107,9 +1107,8 @@ const removeLinksFromHtml = (html, links) => { * * @param newCommentText text of the comment after editing. * @param originalHtml original html of the comment before editing. - * @returns */ -const handleUserDeletedLinksInHtml = (newCommentText, originalHtml) => { +const handleUserDeletedLinksInHtml = (newCommentText: string, originalHtml: string): string => { const parser = new ExpensiMark(); if (newCommentText.length > CONST.MAX_MARKUP_LENGTH) { return newCommentText; @@ -1122,19 +1121,15 @@ const handleUserDeletedLinksInHtml = (newCommentText, originalHtml) => { /** * Saves a new message for a comment. Marks the comment as edited, which will be reflected in the UI. - * - * @param reportID - * @param originalReportAction - * @param textForNewComment */ -function editReportComment(reportID, originalReportAction, textForNewComment) { +function editReportComment(reportID: string, originalReportAction: ReportAction, textForNewComment: string) { const parser = new ExpensiMark(); const originalReportID = ReportUtils.getOriginalReportID(reportID, originalReportAction); // Do not autolink if someone explicitly tries to remove a link from message. // https://github.com/Expensify/App/issues/9090 // https://github.com/Expensify/App/issues/13221 - const originalCommentHTML = lodashGet(originalReportAction, 'message[0].html'); + const originalCommentHTML = originalReportAction?.message?.[0]?.html ?? ''; const htmlForNewComment = handleUserDeletedLinksInHtml(textForNewComment, originalCommentHTML); const reportComment = parser.htmlToText(htmlForNewComment); @@ -1142,7 +1137,7 @@ function editReportComment(reportID, originalReportAction, textForNewComment) { // For longer comments, skip parsing and display plaintext for performance reasons. It takes over 40s to parse a 100k long string!! let parsedOriginalCommentHTML = originalCommentHTML; if (textForNewComment.length <= CONST.MAX_MARKUP_LENGTH) { - const autolinkFilter = {filterRules: _.filter(_.pluck(parser.rules, 'name'), (name) => name !== 'autolink')}; + const autolinkFilter = {filterRules: parser.rules.map((rule) => rule.name).filter((name) => name !== 'autolink')}; parsedOriginalCommentHTML = parser.replace(parser.htmlToMarkdown(originalCommentHTML).trim(), autolinkFilter); } @@ -1231,47 +1226,34 @@ function editReportComment(reportID, originalReportAction, textForNewComment) { /** * Saves the draft for a comment report action. This will put the comment into "edit mode" - * - * @param reportID - * @param reportAction - * @param draftMessage */ -function saveReportActionDraft(reportID, reportAction, draftMessage) { +function saveReportActionDraft(reportID: string, reportAction: ReportAction, draftMessage: string) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${originalReportID}_${reportAction.reportActionID}`, draftMessage); } /** * Saves the number of lines for the report action draft - * @param reportID - * @param reportActionID - * @param numberOfLines */ -function saveReportActionDraftNumberOfLines(reportID, reportActionID, numberOfLines) { +function saveReportActionDraftNumberOfLines(reportID: string, reportActionID: string, numberOfLines: number) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}_${reportActionID}`, numberOfLines); } -/** - * @param reportID - * @param previousValue - * @param newValue - * @param navigate - */ -function updateNotificationPreference(reportID, previousValue, newValue, navigate) { +function updateNotificationPreference(reportID: string, previousValue: NotificationPreference, newValue: NotificationPreference, navigate: boolean) { if (previousValue === newValue) { if (navigate) { Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(reportID)); } return; } - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: {notificationPreference: newValue}, }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -1284,12 +1266,7 @@ function updateNotificationPreference(reportID, previousValue, newValue, navigat } } -/** - * @param reportID - * @param previousValue - * @param newValue - */ -function updateWelcomeMessage(reportID, previousValue, newValue) { +function updateWelcomeMessage(reportID: string, previousValue: string, newValue: string) { // No change needed, navigate back if (previousValue === newValue) { Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(reportID)); @@ -1297,14 +1274,14 @@ function updateWelcomeMessage(reportID, previousValue, newValue) { } const parsedWelcomeMessage = ReportUtils.getParsedComment(newValue); - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: {welcomeMessage: parsedWelcomeMessage}, }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -1315,30 +1292,27 @@ function updateWelcomeMessage(reportID, previousValue, newValue) { Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(reportID)); } -/** - * @param report - * @param newValue - */ -function updateWriteCapabilityAndNavigate(report, newValue) { +function updateWriteCapabilityAndNavigate(report: Report, newValue: WriteCapability) { if (report.writeCapability === newValue) { Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report.reportID)); return; } - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, value: {writeCapability: newValue}, }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, value: {writeCapability: report.writeCapability}, }, ]; + API.write('UpdateReportWriteCapability', {reportID: report.reportID, writeCapability: newValue}, {optimisticData, failureData}); // Return to the report settings page since this field utilizes push-to-page Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report.reportID)); @@ -1362,14 +1336,14 @@ function navigateToConciergeChat() { /** * Add a policy report (workspace room) optimistically and navigate to it. - * - * @param policyID - * @param reportName - * @param visibility - * @param policyMembersAccountIDs - * @param writeCapability */ -function addPolicyReport(policyID, reportName, visibility, policyMembersAccountIDs, writeCapability = CONST.REPORT.WRITE_CAPABILITIES.ALL) { +function addPolicyReport( + policyID: string, + reportName: string, + visibility: string, + policyMembersAccountIDs: number[], + writeCapability: WriteCapability = CONST.REPORT.WRITE_CAPABILITIES.ALL, +) { // The participants include the current user (admin), and for restricted rooms, the policy members. Participants must not be empty. const members = visibility === CONST.REPORT.VISIBILITY.RESTRICTED ? policyMembersAccountIDs : []; const participants = _.unique([currentUserAccountID, ...members]); @@ -1392,7 +1366,7 @@ function addPolicyReport(policyID, reportName, visibility, policyMembersAccountI // Onyx.set is used on the optimistic data so that it is present before navigating to the workspace room. With Onyx.merge the workspace room reportID is not present when // fetchReportIfNeeded is called on the ReportScreen, so openReport is called which is unnecessary since the optimistic data will be stored in Onyx. // Therefore, Onyx.set is used instead of Onyx.merge. - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT}${policyReport.reportID}`, @@ -1409,7 +1383,7 @@ function addPolicyReport(policyID, reportName, visibility, policyMembersAccountI value: {[createdReportAction.reportActionID]: createdReportAction}, }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${policyReport.reportID}`, @@ -1429,7 +1403,7 @@ function addPolicyReport(policyID, reportName, visibility, policyMembersAccountI }, }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${policyReport.reportID}`, @@ -1458,10 +1432,8 @@ function addPolicyReport(policyID, reportName, visibility, policyMembersAccountI /** * Deletes a report, along with its reportActions, any linked reports, and any linked IOU report. - * - * @param reportID */ -function deleteReport(reportID) { +function deleteReport(reportID: string) { const report = allReports[reportID]; const onyxData = { [`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]: null, @@ -1469,12 +1441,15 @@ function deleteReport(reportID) { }; // Delete linked transactions - const reportActionsForReport = allReportActions[reportID]; - _.chain(reportActionsForReport) - .filter((reportAction) => reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) - .map((reportAction) => reportAction.originalMessage.IOUTransactionID) - .uniq() - .each((transactionID) => (onyxData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] = null)); + const reportActionsForReport = allReportActions?.[reportID]; + + const transactionIDs = Object.values(reportActionsForReport ?? {}) + .filter((reportAction): reportAction is ReportActionBase & OriginalMessageIOU => reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU) + .map((reportAction) => reportAction.originalMessage.IOUTransactionID); + + [...new Set(transactionIDs)].forEach((transactionID) => { + onyxData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] = null; + }); Onyx.multiSet(onyxData); @@ -1487,7 +1462,7 @@ function deleteReport(reportID) { /** * @param reportID The reportID of the policy report (workspace room) */ -function navigateToConciergeChatAndDeleteReport(reportID) { +function navigateToConciergeChatAndDeleteReport(reportID: string) { // Dismiss the current report screen and replace it with Concierge Chat Navigation.goBack(ROUTES.HOME); navigateToConciergeChat(); @@ -1495,12 +1470,9 @@ function navigateToConciergeChatAndDeleteReport(reportID) { } /** - * @param policyRoomReport - * @param policyRoomReport.reportID - * @param policyRoomReport.reportName * @param policyRoomName The updated name for the policy room */ -function updatePolicyRoomNameAndNavigate(policyRoomReport, policyRoomName) { +function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomName: string) { const reportID = policyRoomReport.reportID; const previousName = policyRoomReport.reportName; @@ -1509,7 +1481,7 @@ function updatePolicyRoomNameAndNavigate(policyRoomReport, policyRoomName) { Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(reportID)); return; } - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -1524,7 +1496,7 @@ function updatePolicyRoomNameAndNavigate(policyRoomReport, policyRoomName) { }, }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -1535,7 +1507,7 @@ function updatePolicyRoomNameAndNavigate(policyRoomReport, policyRoomName) { }, }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -1551,7 +1523,7 @@ function updatePolicyRoomNameAndNavigate(policyRoomReport, policyRoomName) { /** * @param reportID The reportID of the policy room. */ -function clearPolicyRoomNameErrors(reportID) { +function clearPolicyRoomNameErrors(reportID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { errorFields: { reportName: null, @@ -1562,21 +1534,15 @@ function clearPolicyRoomNameErrors(reportID) { }); } -/** - * @param reportID - * @param isComposerFullSize - */ -function setIsComposerFullSize(reportID, isComposerFullSize) { +function setIsComposerFullSize(reportID: string, isComposerFullSize: boolean) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`, isComposerFullSize); } /** - * @param reportID * @param action the associated report action (optional) * @param isRemote whether or not this notification is a remote push notification - * @returns */ -function shouldShowReportActionNotification(reportID, action = null, isRemote = false) { +function shouldShowReportActionNotification(reportID: string, action: ReportAction | null = null, isRemote = false): boolean { const tag = isRemote ? '[PushNotification]' : '[LocalNotification]'; // Due to payload size constraints, some push notifications may have their report action stripped @@ -1626,11 +1592,7 @@ function shouldShowReportActionNotification(reportID, action = null, isRemote = return true; } -/** - * @param reportID - * @param reportAction - */ -function showReportActionNotification(reportID, reportAction) { +function showReportActionNotification(reportID: string, reportAction: ReportAction) { if (!shouldShowReportActionNotification(reportID, reportAction)) { return; } @@ -1654,46 +1616,33 @@ function showReportActionNotification(reportID, reportAction) { /** * Clear the errors associated with the IOUs of a given report. - * - * @param reportID */ -function clearIOUError(reportID) { +function clearIOUError(reportID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {errorFields: {iou: null}}); } /** * Returns true if the accountID has reacted to the report action (with the given skin tone). * Uses the NEW FORMAT for "emojiReactions" - * @param accountID - * @param users - * @param [skinTone] - * @returns */ -function hasAccountIDEmojiReacted(accountID, users, skinTone) { - if (_.isUndefined(skinTone)) { - return Boolean(users[accountID]); +function hasAccountIDEmojiReacted(accountID: number, users: Record, skinTone?: number) { + if (skinTone === undefined) { + return !!users[accountID]; } const usersReaction = users[accountID]; - if (!usersReaction?.skinTones || !_.size(usersReaction.skinTones)) { + if (!usersReaction?.skinTones?.length) { return false; } - return Boolean(usersReaction.skinTones[skinTone]); + return !!usersReaction.skinTones[skinTone]; } /** * Adds a reaction to the report action. * Uses the NEW FORMAT for "emojiReactions" - * @param reportID - * @param reportActionID - * @param emoji - * @param emoji.name - * @param emoji.code - * @param [emoji.types] - * @param [skinTone] */ -function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredSkinTone) { +function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji, skinTone: number = preferredSkinTone) { const createdAt = moment().utc().format(CONST.DATE.SQL_DATE_TIME); - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, @@ -1704,7 +1653,7 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS users: { [currentUserAccountID]: { skinTones: { - [!_.isUndefined(skinTone) ? skinTone : -1]: createdAt, + [skinTone ?? -1]: createdAt, }, }, }, @@ -1713,7 +1662,7 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, @@ -1725,7 +1674,7 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, @@ -1752,14 +1701,8 @@ function addEmojiReaction(reportID, reportActionID, emoji, skinTone = preferredS /** * Removes a reaction to the report action. * Uses the NEW FORMAT for "emojiReactions" - * @param reportID - * @param reportActionID - * @param emoji - * @param emoji.name - * @param emoji.code - * @param [emoji.types] */ -function removeEmojiReaction(reportID, reportActionID, emoji) { +function removeEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji) { const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -1787,13 +1730,8 @@ function removeEmojiReaction(reportID, reportActionID, emoji) { /** * Calls either addEmojiReaction or removeEmojiReaction depending on if the current user has reacted to the report action. * Uses the NEW FORMAT for "emojiReactions" - * @param reportID - * @param reportAction - * @param reactionObject - * @param existingReactions - * @param [paramSkinTone] */ -function toggleEmojiReaction(reportID, reportAction, reactionObject, existingReactions, paramSkinTone = preferredSkinTone) { +function toggleEmojiReaction(reportID: string, reportAction: ReportAction, reactionObject: {code: string}, existingReactions: string, paramSkinTone: number = preferredSkinTone) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); const originalReportAction = ReportActionsUtils.getReportAction(originalReportID, reportAction.reportActionID); @@ -1804,7 +1742,7 @@ function toggleEmojiReaction(reportID, reportAction, reactionObject, existingRea // This will get cleaned up as part of https://github.com/Expensify/App/issues/16506 once the old emoji // format is no longer being used const emoji = EmojiUtils.findEmojiByCode(reactionObject.code); - const existingReactionObject = lodashGet(existingReactions, [emoji.name]); + const existingReactionObject = existingReactions?.[emoji.name]; // Only use skin tone if emoji supports it const skinTone = emoji.types === undefined ? -1 : paramSkinTone; @@ -1817,11 +1755,7 @@ function toggleEmojiReaction(reportID, reportAction, reactionObject, existingRea addEmojiReaction(originalReportID, reportAction.reportActionID, emoji, skinTone); } -/** - * @param url - * @param isAuthenticated - */ -function openReportFromDeepLink(url, isAuthenticated) { +function openReportFromDeepLink(url: string, isAuthenticated: boolean) { const route = ReportUtils.getRouteFromLink(url); const reportID = ReportUtils.getReportIDFromLink(url); @@ -1850,76 +1784,78 @@ function openReportFromDeepLink(url, isAuthenticated) { }); } -function getCurrentUserAccountID() { +function getCurrentUserAccountID(): number | undefined { return currentUserAccountID; } /** * Leave a report by setting the state to submitted and closed - * - * @param reportID */ -function leaveRoom(reportID) { - const report = lodashGet(allReports, [reportID], {}); - const reportKeys = _.keys(report); +function leaveRoom(reportID: string) { + const report = allReports?.[reportID] ?? {}; + const reportKeys = Object.keys(report); // Pusher's leavingStatus should be sent earlier. // Place the broadcast before calling the LeaveRoom API to prevent a race condition // between Onyx report being null and Pusher's leavingStatus becoming true. broadcastUserIsLeavingRoom(reportID); + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, + statusNum: CONST.REPORT.STATUS.CLOSED, + }, + }, + ]; + + // Manually clear the report using merge. Should not use set here since it would cause race condition + // if it was called right after a merge. + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: reportKeys.reduce>((prev, key) => { + prev[key] = null; + return prev; + }, {}), + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + stateNum: CONST.REPORT.STATE_NUM.OPEN, + statusNum: CONST.REPORT.STATUS.OPEN, + }, + }, + ]; + API.write( 'LeaveRoom', { reportID, }, { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, - statusNum: CONST.REPORT.STATUS.CLOSED, - }, - }, - ], - // Manually clear the report using merge. Should not use set here since it would cause race condition - // if it was called right after a merge. - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: _.object(reportKeys, Array(reportKeys.length).fill(null)), - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - stateNum: CONST.REPORT.STATE_NUM.OPEN, - statusNum: CONST.REPORT.STATUS.OPEN, - }, - }, - ], + optimisticData, + successData, + failureData, }, ); } -/** - * @param reportID - */ function setLastOpenedPublicRoom(reportID) { Onyx.set(ONYXKEYS.LAST_OPENED_PUBLIC_ROOM_ID, reportID); } /** * Navigates to the last opened public room - * - * @param lastOpenedPublicRoomID */ -function openLastOpenedPublicRoom(lastOpenedPublicRoomID) { +function openLastOpenedPublicRoom(lastOpenedPublicRoomID: string) { Navigation.isNavigationReady().then(() => { setLastOpenedPublicRoom(''); Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(lastOpenedPublicRoomID)); @@ -1928,17 +1864,18 @@ function openLastOpenedPublicRoom(lastOpenedPublicRoomID) { /** * Flag a comment as offensive - * - * @param reportID - * @param reportAction - * @param severity */ -function flagComment(reportID, reportAction, severity) { +function flagComment(reportID: string, reportAction: ReportAction, severity: string) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); - const message = reportAction.message[0]; - let updatedDecision; + const message = reportAction?.message?.[0]; + + if (!message) { + return; + } + + let updatedDecision: ModerationDecision; if (severity === CONST.MODERATION.FLAG_SEVERITY_SPAM || severity === CONST.MODERATION.FLAG_SEVERITY_INCONSIDERATE) { - if (!message.moderationDecision) { + if (!message?.moderationDecision) { updatedDecision = { decision: CONST.MODERATION.MODERATOR_DECISION_PENDING, }; @@ -1957,12 +1894,12 @@ function flagComment(reportID, reportAction, severity) { const reportActionID = reportAction.reportActionID; - const updatedMessage = { + const updatedMessage: Message = { ...message, moderationDecision: updatedDecision, }; - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${originalReportID}`, @@ -1975,7 +1912,7 @@ function flagComment(reportID, reportAction, severity) { }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${originalReportID}`, @@ -1988,7 +1925,7 @@ function flagComment(reportID, reportAction, severity) { }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${originalReportID}`, @@ -2013,13 +1950,9 @@ function flagComment(reportID, reportAction, severity) { /** * Updates a given user's private notes on a report - * - * @param reportID - * @param accountID - * @param note */ -const updatePrivateNotes = (reportID, accountID, note) => { - const optimisticData = [ +const updatePrivateNotes = (reportID: string, accountID: number, note: string) => { + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -2035,7 +1968,7 @@ const updatePrivateNotes = (reportID, accountID, note) => { }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -2050,7 +1983,7 @@ const updatePrivateNotes = (reportID, accountID, note) => { }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -2064,80 +1997,64 @@ const updatePrivateNotes = (reportID, accountID, note) => { }, ]; - API.write( - 'UpdateReportPrivateNote', - { - reportID, - privateNotes: note, - }, - {optimisticData, successData, failureData}, - ); + API.write('UpdateReportPrivateNote', {reportID, privateNotes: note}, {optimisticData, successData, failureData}); }; /** * Fetches all the private notes for a given report - * - * @param reportID */ -function getReportPrivateNote(reportID) { - if (_.isEmpty(reportID)) { +function getReportPrivateNote(reportID: string) { + if (!reportID) { return; } - API.read( - 'GetReportPrivateNote', + + const optimisticData: OnyxUpdate[] = [ { - reportID, + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + isLoadingPrivateNotes: true, + }, }, + ]; + + const successData: OnyxUpdate[] = [ { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - isLoadingPrivateNotes: true, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - isLoadingPrivateNotes: false, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - isLoadingPrivateNotes: false, - }, - }, - ], + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + isLoadingPrivateNotes: false, + }, }, - ); + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + isLoadingPrivateNotes: false, + }, + }, + ]; + + API.read('GetReportPrivateNote', {reportID}, {optimisticData, successData, failureData}); } /** * Checks if there are any errors in the private notes for a given report * - * @param report * @returns Returns true if there are errors in any of the private notes on the report */ -function hasErrorInPrivateNotes(report) { - const privateNotes = lodashGet(report, 'privateNotes', {}); - return _.some(privateNotes, (privateNote) => !_.isEmpty(privateNote.errors)); +function hasErrorInPrivateNotes(report: Report): boolean { + const privateNotes = report?.privateNotes ?? {}; + return Object.values(privateNotes).some((privateNote) => !isEmpty(privateNote.errors)); } /** * Clears all errors associated with a given private note - * - * @param reportID - * @param accountID */ -function clearPrivateNotesError(reportID, accountID) { +function clearPrivateNotesError(reportID: string, accountID: number) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {privateNotes: {[accountID]: {errors: null}}}); } diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 143b70127de5..5b4773a215f2 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -5,7 +5,7 @@ type OriginalMessageIOU = { actionName: typeof CONST.REPORT.ACTIONS.TYPE.IOU; originalMessage: { /** The ID of the iou transaction */ - IOUTransactionID?: string; + IOUTransactionID: string; IOUReportID?: number; amount: number; @@ -120,7 +120,8 @@ type OriginalMessagePolicyTask = { | typeof CONST.REPORT.ACTIONS.TYPE.TASKEDITED | typeof CONST.REPORT.ACTIONS.TYPE.TASKCANCELLED | typeof CONST.REPORT.ACTIONS.TYPE.TASKCOMPLETED - | typeof CONST.REPORT.ACTIONS.TYPE.TASKREOPENED; + | typeof CONST.REPORT.ACTIONS.TYPE.TASKREOPENED + | typeof CONST.REPORT.ACTIONS.TYPE.MODIFIEDEXPENSE; originalMessage: unknown; }; @@ -136,4 +137,4 @@ type OriginalMessage = | OriginalMessagePolicyTask; export default OriginalMessage; -export type {Reaction, ChronosOOOEvent}; +export type {Reaction, ChronosOOOEvent, OriginalMessageIOU}; diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index c43525f3bca4..2152f2c1689c 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -2,6 +2,10 @@ import {ValueOf} from 'type-fest'; import CONST from '../../CONST'; import * as OnyxCommon from './OnyxCommon'; +type NotificationPreference = ValueOf; + +type WriteCapability = ValueOf; + type Report = { /** The specific type of chat */ chatType?: ValueOf; @@ -28,7 +32,7 @@ type Report = { lastReadTime?: string; /** The current user's notification preference for this report */ - notificationPreference?: string | number; + notificationPreference?: NotificationPreference; /** The policy name to use for an archived report */ oldPolicyName?: string; @@ -52,7 +56,7 @@ type Report = { statusNum?: ValueOf; /** Which user role is capable of posting messages on the report */ - writeCapability?: ValueOf; + writeCapability?: WriteCapability; /** The report type */ type?: string; @@ -76,6 +80,11 @@ type Report = { nonReimbursableTotal?: number; lastMessageTranslationKey?: string; isLastMessageDeletedParentAction?: boolean; + iouReportID?: string; + privateNotes?: Record; + isLoadingPrivateNotes?: boolean; }; export default Report; + +export type {NotificationPreference, WriteCapability}; diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index ec505a7e8d07..755fca821f85 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -1,5 +1,15 @@ import OriginalMessage, {Reaction} from './OriginalMessage'; import * as OnyxCommon from './OnyxCommon'; +import CONST from '../../CONST'; + +type ModerationDecision = { + decision: + | typeof CONST.MODERATION.MODERATOR_DECISION_APPROVED + | typeof CONST.MODERATION.MODERATOR_DECISION_HIDDEN + | typeof CONST.MODERATION.MODERATOR_DECISION_PENDING + | typeof CONST.MODERATION.MODERATOR_DECISION_PENDING_HIDE + | typeof CONST.MODERATION.MODERATOR_DECISION_PENDING_REMOVE; +}; type Message = { /** The type of the action item fragment. Used to render a corresponding component */ @@ -34,6 +44,8 @@ type Message = { isDeletedParentAction: boolean; whisperedTo: number[]; reactions: Reaction[]; + html?: string; + moderationDecision?: ModerationDecision; }; type Person = { @@ -44,7 +56,7 @@ type Person = { type ReportActionBase = { /** The ID of the reportAction. It is the string representation of the a 64-bit integer. */ - reportActionID?: string; + reportActionID: string; /** The ID of the previous reportAction on the report. It is a string represenation of a 64-bit integer (or null for CREATED actions). */ previousReportActionID?: string; @@ -86,3 +98,5 @@ type ReportActionBase = { type ReportAction = ReportActionBase & OriginalMessage; export default ReportAction; + +export type {ReportActionBase, Message, ModerationDecision}; diff --git a/src/types/onyx/ReportActionReactions.ts b/src/types/onyx/ReportActionReactions.ts index 196e2707bbd2..99b437ea7242 100644 --- a/src/types/onyx/ReportActionReactions.ts +++ b/src/types/onyx/ReportActionReactions.ts @@ -13,3 +13,5 @@ type ReportActionReaction = { type ReportActionReactions = Record; export default ReportActionReactions; + +export type {User}; From 4fea06b925da356ab6cc09563836e5b1966d339d Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 09:33:50 +0100 Subject: [PATCH 04/28] Adjust imports, comments up to line 100 --- src/ONYXKEYS.ts | 1 + src/libs/EmojiUtils.ts | 2 +- src/libs/PusherUtils.ts | 2 +- src/libs/actions/Report.ts | 40 +++++++++++-------------------- src/types/onyx/OriginalMessage.ts | 2 +- src/types/onyx/ReportAction.ts | 2 +- 6 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index b56468dabc1a..577ae0c95a25 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -448,6 +448,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction; [ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags; [ONYXKEYS.COLLECTION.SELECTED_TAB]: string; + [ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT]: string; // Forms [ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM]: OnyxTypes.AddDebitCardForm; diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index 22e84921b1ee..f249632fdc06 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -436,7 +436,7 @@ function suggestEmojis(text: string, lang: keyof typeof emojisTrie, limit = CONS /** * Retrieve preferredSkinTone as Number to prevent legacy 'default' String value */ -const getPreferredSkinToneIndex = (val: string | number): number | string => { +const getPreferredSkinToneIndex = (val: string | number | null): number | string => { if (val !== null && Number.isInteger(Number(val))) { return val; } diff --git a/src/libs/PusherUtils.ts b/src/libs/PusherUtils.ts index e5782d12acd3..9f7e21ef9005 100644 --- a/src/libs/PusherUtils.ts +++ b/src/libs/PusherUtils.ts @@ -26,7 +26,7 @@ function triggerMultiEventHandler(eventType: string, data: OnyxUpdate[]): Promis * Abstraction around subscribing to private user channel events. Handles all logs and errors automatically. */ function subscribeToPrivateUserChannelEvent(eventName: string, accountID: string, onEvent: (pushJSON: PushJSON) => void) { - const pusherChannelName = `${CONST.PUSHER.PRIVATE_USER_CHANNEL_PREFIX}${accountID}${CONFIG.PUSHER.SUFFIX}`; + const pusherChannelName = `${CONST.PUSHER.PRIVATE_USER_CHANNEL_PREFIX}${accountID}${CONFIG.PUSHER.SUFFIX}` as const; function logPusherEvent(pushJSON: PushJSON) { Log.info(`[Report] Handled ${eventName} event sent by Pusher`, false, pushJSON); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 9e8df0657af4..fd5370e44ee6 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2,7 +2,6 @@ import {format as timezoneFormat, utcToZonedTime} from 'date-fns-tz'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; import isEmpty from 'lodash/isEmpty'; -import moment from 'moment'; import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import * as API from '@libs/API'; @@ -24,11 +23,9 @@ import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import {OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; -import PersonalDetails from '@src/types/onyx/PersonalDetails'; +import {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import Report, {NotificationPreference, WriteCapability} from '@src/types/onyx/Report'; -import ReportAction, {Message, ModerationDecision, ReportActionBase} from '@src/types/onyx/ReportAction'; -import {User} from '@src/types/onyx/ReportActionReactions'; +import ReportAction, {Message, ReportActionBase} from '@src/types/onyx/ReportAction'; import * as Session from './Session'; import * as Welcome from './Welcome'; @@ -45,7 +42,7 @@ Onyx.connect({ }, }); -let preferredSkinTone: number; +let preferredSkinTone: string | number; Onyx.connect({ key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, callback: (val) => { @@ -85,7 +82,7 @@ Onyx.connect({ }, }); -const draftNoteMap = {}; +const draftNoteMap: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT, callback: (value, key) => { @@ -98,13 +95,11 @@ Onyx.connect({ }, }); -const allReports = {}; -let conciergeChatReportID; -const typingWatchTimers = {}; +const allReports: OnyxCollection = {}; +let conciergeChatReportID: string | undefined; +const typingWatchTimers: Record = {}; -/** - * Get the private pusher channel name for a Report. - */ +/** Get the private pusher channel name for a Report. */ function getReportChannelName(reportID: string): string { return `${CONST.PUSHER.PRIVATE_REPORT_CHANNEL_PREFIX}${reportID}${CONFIG.PUSHER.SUFFIX}`; } @@ -133,9 +128,7 @@ function getNormalizedStatus(status: NonNormalizedStatus): Record; export default ReportAction; -export type {ReportActions, Message}; +export type {ReportActions, ReportActionBase, Message}; From d686dcc591021775bb12742cbcc9be9f48275888 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 10:21:05 +0100 Subject: [PATCH 05/28] Update pusher types --- src/libs/Pusher/pusher.ts | 28 ++++++++++++++++++++++------ src/libs/actions/Report.ts | 8 +++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index dd8af08db229..efbf3d9e2d6c 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -24,12 +24,23 @@ type Args = { type PushJSON = OnyxUpdateEvent[] | OnyxUpdatesFromServer; +type UserIsTypingEvent = Record & { + userLogin?: string; +}; + +type UserIsLeavingRoomEvent = Record; + +type PusherEventMap = { + [TYPE.USER_IS_TYPING]: UserIsTypingEvent; + [TYPE.USER_IS_LEAVING_ROOM]: UserIsLeavingRoomEvent; +}; + +type EventData = EventName extends keyof PusherEventMap ? PusherEventMap[EventName] : PushJSON; + type EventCallbackError = {type: ValueOf; data: {code: number}}; type ChunkedDataEvents = {chunks: unknown[]; receivedFinal: boolean}; -type EventData = {id?: string; chunk?: unknown; final?: boolean; index: number}; - type SocketEventCallback = (eventName: SocketEventName, data?: States | EventCallbackError) => void; type PusherWithAuthParams = InstanceType & { @@ -139,13 +150,13 @@ function getChannel(channelName: string): Channel | undefined { /** * Binds an event callback to a channel + eventName */ -function bindEventToChannel(channel: Channel | undefined, eventName: PusherEventName, eventCallback: (data: PushJSON) => void = () => {}) { +function bindEventToChannel(channel: Channel | undefined, eventName: EventName, eventCallback: (data: EventData) => void = () => {}) { if (!eventName) { return; } const chunkedDataEvents: Record = {}; - const callback = (eventData: string | Record | EventData) => { + const callback = (eventData: EventData) => { if (shouldForceOffline) { Log.info('[Pusher] Ignoring a Push event because shouldForceOffline = true'); return; @@ -207,7 +218,12 @@ function bindEventToChannel(channel: Channel | undefined, eventName: PusherEvent * Subscribe to a channel and an event * @param [onResubscribe] Callback to be called when reconnection happen */ -function subscribe(channelName: string, eventName: PusherEventName, eventCallback: (data: PushJSON) => void = () => {}, onResubscribe = () => {}): Promise { +function subscribe( + channelName: string, + eventName: EventName, + eventCallback: (data: EventData) => void = () => {}, + onResubscribe = () => {}, +): Promise { return new Promise((resolve, reject) => { // We cannot call subscribe() before init(). Prevent any attempt to do this on dev. if (!socket) { @@ -307,7 +323,7 @@ function isSubscribed(channelName: string): boolean { /** * Sends an event over a specific event/channel in pusher. */ -function sendEvent(channelName: string, eventName: PusherEventName, payload: Record) { +function sendEvent(channelName: string, eventName: EventName, payload: EventData) { // Check to see if we are subscribed to this channel before sending the event. Sending client events over channels // we are not subscribed too will throw errors and cause reconnection attempts. Subscriptions are not instant and // can happen later than we expect. diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index fd5370e44ee6..15cd35423174 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -1,6 +1,8 @@ import {format as timezoneFormat, utcToZonedTime} from 'date-fns-tz'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; +import lodashDebounce from 'lodash/debounce'; +import lodashGet from 'lodash/get'; import isEmpty from 'lodash/isEmpty'; import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import * as ActiveClientManager from '@libs/ActiveClientManager'; @@ -104,10 +106,6 @@ function getReportChannelName(reportID: string): string { return `${CONST.PUSHER.PRIVATE_REPORT_CHANNEL_PREFIX}${reportID}${CONFIG.PUSHER.SUFFIX}`; } -type NonNormalizedStatus = Record & { - userLogin?: string; -}; - /** * There are 2 possibilities that we can receive via pusher for a user's typing/leaving status: * 1. The "new" way from New Expensify is passed as {[login]: Boolean} (e.g. {yuwen@expensify.com: true}), where the value @@ -138,7 +136,7 @@ function subscribeToReportTypingEvents(reportID: string) { Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, {}); const pusherChannelName = getReportChannelName(reportID); - Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_TYPING, (typingStatus: NonNormalizedStatus) => { + Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_TYPING, (typingStatus) => { // If the pusher message comes from OldDot, we expect the typing status to be keyed by user // login OR by 'Concierge'. If the pusher message comes from NewDot, it is keyed by accountID // since personal details are keyed by accountID. From 321f78dfdcbd6f6912e881346ba38b2e8fcf7295 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 10:59:34 +0100 Subject: [PATCH 06/28] Migrate report actions, up to line 400 --- src/libs/Pusher/pusher.ts | 6 +- src/libs/ReportUtils.ts | 2 +- src/libs/actions/Report.ts | 120 ++++++++++++++++++--------------- src/types/modules/pusher.d.ts | 5 +- src/types/onyx/ReportAction.ts | 2 +- 5 files changed, 73 insertions(+), 62 deletions(-) diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index efbf3d9e2d6c..a15ddc66e743 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -28,7 +28,9 @@ type UserIsTypingEvent = Record & { userLogin?: string; }; -type UserIsLeavingRoomEvent = Record; +type UserIsLeavingRoomEvent = Record & { + userLogin?: string; +}; type PusherEventMap = { [TYPE.USER_IS_TYPING]: UserIsTypingEvent; @@ -410,4 +412,4 @@ export { getPusherSocketID, }; -export type {EventCallbackError, States, PushJSON}; +export type {EventCallbackError, States, PushJSON, UserIsTypingEvent, UserIsLeavingRoomEvent}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index d93661778b83..c37f5f8bd212 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2349,7 +2349,7 @@ function getParsedComment(text: string): string { return text.length <= CONST.MAX_MARKUP_LENGTH ? parser.replace(text) : lodashEscape(text); } -function buildOptimisticAddCommentReportAction(text?: string, file?: File & {source: string; uri: string}): OptimisticReportAction { +function buildOptimisticAddCommentReportAction(text?: string, file?: File): OptimisticReportAction { const parser = new ExpensiMark(); const commentText = getParsedComment(text ?? ''); const isAttachment = !text && file !== undefined; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 15cd35423174..1ea6c9598c26 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -5,6 +5,7 @@ import lodashDebounce from 'lodash/debounce'; import lodashGet from 'lodash/get'; import isEmpty from 'lodash/isEmpty'; import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; +import {NullishDeep} from 'react-native-onyx/lib/types'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import * as API from '@libs/API'; import * as CollectionUtils from '@libs/CollectionUtils'; @@ -28,6 +29,7 @@ import ROUTES from '@src/ROUTES'; import {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import Report, {NotificationPreference, WriteCapability} from '@src/types/onyx/Report'; import ReportAction, {Message, ReportActionBase} from '@src/types/onyx/ReportAction'; +import {isNotEmptyObject} from '@src/types/utils/EmptyObject'; import * as Session from './Session'; import * as Welcome from './Welcome'; @@ -114,13 +116,13 @@ function getReportChannelName(reportID: string): string { * * This method makes sure that no matter which we get, we return the "new" format */ -function getNormalizedStatus(status: NonNormalizedStatus): Record { - let normalizedStatus: Record; +function getNormalizedStatus(typingStatus: Pusher.UserIsTypingEvent | Pusher.UserIsLeavingRoomEvent): Record { + let normalizedStatus: Record; - if (status.userLogin) { - normalizedStatus = {[status.userLogin]: true}; + if (typingStatus.userLogin) { + normalizedStatus = {[typingStatus.userLogin]: true}; } else { - normalizedStatus = status; + normalizedStatus = typingStatus; } return normalizedStatus; @@ -169,9 +171,7 @@ function subscribeToReportTypingEvents(reportID: string) { }); } -/** - * Initialize our pusher subscriptions to listen for someone leaving a room. - */ +/** Initialize our pusher subscriptions to listen for someone leaving a room. */ function subscribeToReportLeavingEvents(reportID: string) { if (!reportID) { return; @@ -181,7 +181,7 @@ function subscribeToReportLeavingEvents(reportID: string) { Onyx.set(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM}${reportID}`, false); const pusherChannelName = getReportChannelName(reportID); - Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, (leavingStatus: NonNormalizedStatus) => { + Pusher.subscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, (leavingStatus: Pusher.UserIsLeavingRoomEvent) => { // If the pusher message comes from OldDot, we expect the leaving status to be keyed by user // login OR by 'Concierge'. If the pusher message comes from NewDot, it is keyed by accountID // since personal details are keyed by accountID. @@ -228,7 +228,7 @@ function unsubscribeFromLeavingRoomReportChannel(reportID: string) { Pusher.unsubscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM); } -type SubscriberCallback = (isFromCurrentUser: boolean, reportActionID: string) => void; +type SubscriberCallback = (isFromCurrentUser: boolean, reportActionID: string | undefined) => void; type ActionSubscriber = { reportID: string; @@ -250,10 +250,8 @@ function subscribeToNewActionEvent(reportID: string, callback: SubscriberCallbac }; } -/** - * Notify the ReportActionsView that a new comment has arrived - */ -function notifyNewAction(reportID: string, accountID: number, reportActionID: string) { +/** Notify the ReportActionsView that a new comment has arrived */ +function notifyNewAction(reportID: string, accountID: number | undefined, reportActionID: string | undefined) { const actionSubscriber = newActionSubscribers.find((subscriber) => subscriber.reportID === reportID); if (!actionSubscriber) { return; @@ -271,8 +269,8 @@ function notifyNewAction(reportID: string, accountID: number, reportActionID: st */ function addActions(reportID: string, text = '', file?: File) { let reportCommentText = ''; - let reportCommentAction; - let attachmentAction; + let reportCommentAction: Partial | undefined; + let attachmentAction: Partial | undefined; let commandName = 'AddComment'; if (text) { @@ -290,43 +288,53 @@ function addActions(reportID: string, text = '', file?: File) { } // Always prefer the file as the last action over text - const lastAction = attachmentAction || reportCommentAction; - + const lastAction = attachmentAction ?? reportCommentAction; const currentTime = DateUtils.getDBTime(); + const lastComment = lastAction?.message?.[0]; + const lastCommentText = ReportUtils.formatReportLastMessageText(lastComment?.text ?? ''); - const lastCommentText = ReportUtils.formatReportLastMessageText(lastAction.message[0].text); - - const optimisticReport = { + const optimisticReport: Partial = { lastVisibleActionCreated: currentTime, - lastMessageTranslationKey: lodashGet(lastAction, 'message[0].translationKey', ''), + lastMessageTranslationKey: lastComment?.translationKey ?? '', lastMessageText: lastCommentText, lastMessageHtml: lastCommentText, lastActorAccountID: currentUserAccountID, lastReadTime: currentTime, }; - if (ReportUtils.getReportNotificationPreference(ReportUtils.getReport(reportID)) === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) { + const report = ReportUtils.getReport(reportID); + + if (isNotEmptyObject(report) && ReportUtils.getReportNotificationPreference(report) === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) { optimisticReport.notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS; } // Optimistically add the new actions to the store before waiting to save them to the server - const optimisticReportActions = {}; - if (text) { + const optimisticReportActions: OnyxCollection> = {}; + if (text && reportCommentAction?.reportActionID) { optimisticReportActions[reportCommentAction.reportActionID] = reportCommentAction; } - if (file) { + if (file && attachmentAction?.reportActionID) { optimisticReportActions[attachmentAction.reportActionID] = attachmentAction; } - const parameters = { + type Parameters = { + reportID: string; + reportActionID?: string; + commentReportActionID?: string | null; + reportComment?: string; + file?: File; + timezone?: string; + }; + + const parameters: Parameters = { reportID, - reportActionID: file ? attachmentAction.reportActionID : reportCommentAction.reportActionID, + reportActionID: file ? attachmentAction?.reportActionID : reportCommentAction?.reportActionID, commentReportActionID: file && reportCommentAction ? reportCommentAction.reportActionID : null, reportComment: reportCommentText, file, }; - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -339,18 +347,21 @@ function addActions(reportID: string, text = '', file?: File) { }, ]; - const successData = [ + const successReportActions: OnyxCollection> = {}; + + Object.entries(optimisticReportActions).forEach(([actionKey]) => { + optimisticReportActions[actionKey] = {pendingAction: null}; + }); + + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: Object.entries(optimisticReportActions).reduce((prev, [key]) => { - prev[key] = {pendingAction: null}; - return prev; - }, {}), + value: successReportActions, }, ]; - let failureReport = { + let failureReport: Partial = { lastMessageTranslationKey: '', lastMessageText: '', lastVisibleActionCreated: '', @@ -358,8 +369,8 @@ function addActions(reportID: string, text = '', file?: File) { const {lastMessageText = '', lastMessageTranslationKey = ''} = ReportActionsUtils.getLastVisibleMessage(reportID); if (lastMessageText || lastMessageTranslationKey) { const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(reportID); - const lastVisibleActionCreated = lodashGet(lastVisibleAction, 'created'); - const lastActorAccountID = lodashGet(lastVisibleAction, 'actorAccountID'); + const lastVisibleActionCreated = lastVisibleAction?.created; + const lastActorAccountID = lastVisibleAction?.actorAccountID; failureReport = { lastMessageTranslationKey, lastMessageText, @@ -367,7 +378,17 @@ function addActions(reportID: string, text = '', file?: File) { lastActorAccountID, }; } - const failureData = [ + + const failureReportActions: OnyxCollection> = {}; + + Object.entries(optimisticReportActions).forEach(([actionKey, action]) => { + optimisticReportActions[actionKey] = { + ...action, + errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), + }; + }); + + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -376,24 +397,18 @@ function addActions(reportID: string, text = '', file?: File) { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: Object.entries(optimisticReportActions).reduce((prev, [key, action]) => { - prev[key] = { - ...action, - errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), - }; - return prev; - }, {}), + value: failureReportActions, }, ]; // Update optimistic data for parent report action if the report is a child report const optimisticParentReportData = ReportUtils.getOptimisticDataForParentReportAction(reportID, currentTime, CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD); - if (!isEmpty(optimisticParentReportData)) { + if (isNotEmptyObject(optimisticParentReportData)) { optimisticData.push(optimisticParentReportData); } // Update the timezone if it's been 5 minutes from the last time the user added a comment - if (DateUtils.canUpdateTimezone()) { + if (DateUtils.canUpdateTimezone() && currentUserAccountID) { const timezone = DateUtils.getCurrentTimezone(); parameters.timezone = JSON.stringify(timezone); optimisticData.push({ @@ -409,20 +424,15 @@ function addActions(reportID: string, text = '', file?: File) { successData, failureData, }); - notifyNewAction(reportID, lastAction.actorAccountID, lastAction.reportActionID); + notifyNewAction(reportID, lastAction?.actorAccountID, lastAction?.reportActionID); } -/** - * - * Add an attachment and optional comment. - */ +/** Add an attachment and optional comment. */ function addAttachment(reportID: string, file: File, text = '') { addActions(reportID, text, file); } -/** - * Add a single comment to a report - */ +/** Add a single comment to a report */ function addComment(reportID: string, text: string) { addActions(reportID, text); } diff --git a/src/types/modules/pusher.d.ts b/src/types/modules/pusher.d.ts index fb7bbaa97f79..9705ec4935c8 100644 --- a/src/types/modules/pusher.d.ts +++ b/src/types/modules/pusher.d.ts @@ -8,8 +8,7 @@ declare global { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions interface File { - source?: string; - - uri?: string; + source: string; + uri: string; } } diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index d5eae4354472..1bc21a433203 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -121,7 +121,7 @@ type ReportActionBase = { isFirstItem?: boolean; /** Informations about attachments of report action */ - attachmentInfo?: (File & {source: string; uri: string}) | Record; + attachmentInfo?: File | Record; /** Receipt tied to report action */ receipt?: Receipt; From f8a3e31011973e75ef0e0cba250324c23ef4f58c Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 11:21:29 +0100 Subject: [PATCH 07/28] Migrate openReport --- src/libs/ReportUtils.ts | 11 ++- src/libs/actions/Report.ts | 112 +++++++++++++++++++----------- src/types/onyx/OriginalMessage.ts | 2 +- 3 files changed, 76 insertions(+), 49 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c37f5f8bd212..53d3e9a067ff 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -16,8 +16,8 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import {Beta, Login, PersonalDetails, Policy, PolicyTags, Report, ReportAction, Transaction} from '@src/types/onyx'; import {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; -import {ChangeLog, IOUMessage, OriginalMessageActionName} from '@src/types/onyx/OriginalMessage'; -import {Message, ReportActions} from '@src/types/onyx/ReportAction'; +import {ChangeLog, IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage'; +import {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; import {Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; import DeepValueOf from '@src/types/utils/DeepValueOf'; import {EmptyObject, isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; @@ -183,10 +183,8 @@ type OptimisticClosedReportAction = Pick< 'actionName' | 'actorAccountID' | 'automatic' | 'avatar' | 'created' | 'message' | 'originalMessage' | 'pendingAction' | 'person' | 'reportActionID' | 'shouldShow' >; -type OptimisticCreatedReportAction = Pick< - ReportAction, - 'actionName' | 'actorAccountID' | 'automatic' | 'avatar' | 'created' | 'message' | 'person' | 'reportActionID' | 'shouldShow' | 'pendingAction' ->; +type OptimisticCreatedReportAction = OriginalMessageCreated & + Pick; type OptimisticChatReport = Pick< Report, @@ -3059,6 +3057,7 @@ function buildOptimisticCreatedReportAction(emailCreatingAction: string, created return { reportActionID: NumberUtils.rand64(), actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, + originalMessage: undefined, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, actorAccountID: currentUserAccountID, message: [ diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 1ea6c9598c26..6cabb50c18ef 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -26,10 +26,11 @@ import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import {PersonalDetails} from '@src/types/onyx'; import {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import Report, {NotificationPreference, WriteCapability} from '@src/types/onyx/Report'; import ReportAction, {Message, ReportActionBase} from '@src/types/onyx/ReportAction'; -import {isNotEmptyObject} from '@src/types/utils/EmptyObject'; +import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; import * as Session from './Session'; import * as Welcome from './Welcome'; @@ -86,6 +87,14 @@ Onyx.connect({ }, }); +let allPersonalDetails: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (val) => { + allPersonalDetails = val ?? {}; + }, +}); + const draftNoteMap: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT, @@ -451,20 +460,29 @@ function reportActionsExist(reportID: string): boolean { * @param isFromDeepLink Whether or not this report is being opened from a deep link * @param participantAccountIDList The list of accountIDs that are included in a new chat, not including the user creating it */ -function openReport(reportID: string, participantLoginList = [], newReportObject = {}, parentReportActionID = '0', isFromDeepLink = false, participantAccountIDList = []) { +function openReport( + reportID: string, + participantLoginList: string[] = [], + newReportObject: Partial = {}, + parentReportActionID = '0', + isFromDeepLink = false, + participantAccountIDList: number[] = [], +) { if (!reportID) { return; } - const optimisticReportData: OnyxUpdate[] = [ + const optimisticReport = reportActionsExist(reportID) + ? {} + : { + reportName: allReports?.[reportID]?.reportName ?? CONST.REPORT.DEFAULT_REPORT_NAME, + }; + + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: reportActionsExist(reportID) - ? {} - : { - reportName: lodashGet(allReports, [reportID, 'reportName'], CONST.REPORT.DEFAULT_REPORT_NAME), - }, + value: optimisticReport, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -477,7 +495,7 @@ function openReport(reportID: string, participantLoginList = [], newReportObject }, ]; - const reportSuccessData: OnyxUpdate[] = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -500,7 +518,7 @@ function openReport(reportID: string, participantLoginList = [], newReportObject }, ]; - const reportFailureData: OnyxUpdate[] = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, @@ -510,13 +528,17 @@ function openReport(reportID: string, participantLoginList = [], newReportObject }, ]; - const onyxData = { - optimisticData: optimisticReportData, - successData: reportSuccessData, - failureData: reportFailureData, + type Parameters = { + reportID: string; + emailList?: string; + accountIDList?: string; + parentReportActionID?: string; + shouldRetry?: boolean; + createdReportActionID?: string; + clientLastReadTime?: string; }; - const params = { + const parameters: Parameters = { reportID, emailList: participantLoginList ? participantLoginList.join(',') : '', accountIDList: participantAccountIDList ? participantAccountIDList.join(',') : '', @@ -524,23 +546,24 @@ function openReport(reportID: string, participantLoginList = [], newReportObject }; if (isFromDeepLink) { - params.shouldRetry = false; + parameters.shouldRetry = false; } + const report = ReportUtils.getReport(reportID); // If we open an exist report, but it is not present in Onyx yet, we should change the method to set for this report // and we need data to be available when we navigate to the chat page - if (_.isEmpty(ReportUtils.getReport(reportID))) { - onyxData.optimisticData[0].onyxMethod = Onyx.METHOD.SET; + if (isEmptyObject(report)) { + optimisticData[0].onyxMethod = Onyx.METHOD.SET; } // If we are creating a new report, we need to add the optimistic report data and a report action - if (!_.isEmpty(newReportObject)) { + if (isNotEmptyObject(newReportObject)) { // Change the method to set for new reports because it doesn't exist yet, is faster, // and we need the data to be available when we navigate to the chat page - onyxData.optimisticData[0].onyxMethod = Onyx.METHOD.SET; - onyxData.optimisticData[0].value = { + optimisticData[0].onyxMethod = Onyx.METHOD.SET; + optimisticData[0].value = { + ...optimisticReport, reportName: CONST.REPORT.DEFAULT_REPORT_NAME, - ...onyxData.optimisticData[0].value, ...newReportObject, pendingFields: { createChat: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, @@ -548,28 +571,33 @@ function openReport(reportID: string, participantLoginList = [], newReportObject isOptimisticReport: true, }; - let emailCreatingAction = CONST.REPORT.OWNER_EMAIL_FAKE; + let emailCreatingAction: string = CONST.REPORT.OWNER_EMAIL_FAKE; if (newReportObject.ownerAccountID && newReportObject.ownerAccountID !== CONST.REPORT.OWNER_ACCOUNT_ID_FAKE) { - emailCreatingAction = lodashGet(allPersonalDetails, [newReportObject.ownerAccountID, 'login'], ''); + emailCreatingAction = allPersonalDetails?.[newReportObject.ownerAccountID]?.login ?? ''; } const optimisticCreatedAction = ReportUtils.buildOptimisticCreatedReportAction(emailCreatingAction); - onyxData.optimisticData.push({ + optimisticData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, value: {[optimisticCreatedAction.reportActionID]: optimisticCreatedAction}, }); - onyxData.successData.push({ + successData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, value: {[optimisticCreatedAction.reportActionID]: {pendingAction: null}}, }); // Add optimistic personal details for new participants - const optimisticPersonalDetails = {}; - const settledPersonalDetails = {}; - _.map(participantLoginList, (login, index) => { - const accountID = newReportObject.participantAccountIDs[index]; - optimisticPersonalDetails[accountID] = allPersonalDetails[accountID] || { + const optimisticPersonalDetails: OnyxCollection = {}; + const settledPersonalDetails: OnyxCollection = {}; + participantLoginList.forEach((login, index) => { + const accountID = newReportObject?.participantAccountIDs?.[index]; + + if (!accountID) { + return; + } + + optimisticPersonalDetails[accountID] = allPersonalDetails?.[accountID] ?? { login, accountID, avatar: UserUtils.getDefaultAvatarURL(accountID), @@ -577,36 +605,36 @@ function openReport(reportID: string, participantLoginList = [], newReportObject isOptimisticPersonalDetail: true, }; - settledPersonalDetails[accountID] = allPersonalDetails[accountID] || null; + settledPersonalDetails[accountID] = allPersonalDetails?.[accountID] ?? null; }); - onyxData.optimisticData.push({ + + optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: optimisticPersonalDetails, }); - - onyxData.successData.push({ + successData.push({ onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: settledPersonalDetails, }); - onyxData.failureData.push({ + failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS_LIST, value: settledPersonalDetails, }); // Add the createdReportActionID parameter to the API call - params.createdReportActionID = optimisticCreatedAction.reportActionID; + parameters.createdReportActionID = optimisticCreatedAction.reportActionID; // If we are creating a thread, ensure the report action has childReportID property added if (newReportObject.parentReportID && parentReportActionID) { - onyxData.optimisticData.push({ + optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newReportObject.parentReportID}`, value: {[parentReportActionID]: {childReportID: reportID, childType: CONST.REPORT.TYPE.CHAT}}, }); - onyxData.failureData.push({ + failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${newReportObject.parentReportID}`, value: {[parentReportActionID]: {childReportID: '0', childType: ''}}, @@ -614,16 +642,16 @@ function openReport(reportID: string, participantLoginList = [], newReportObject } } - params.clientLastReadTime = lodashGet(currentReportData, [reportID, 'lastReadTime'], ''); + parameters.clientLastReadTime = currentReportData?.[reportID]?.lastReadTime ?? ''; if (isFromDeepLink) { // eslint-disable-next-line rulesdir/no-api-side-effects-method - API.makeRequestWithSideEffects('OpenReport', params, onyxData).finally(() => { + API.makeRequestWithSideEffects('OpenReport', parameters, {optimisticData, successData, failureData}).finally(() => { Onyx.set(ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, false); }); } else { // eslint-disable-next-line rulesdir/no-multiple-api-calls - API.write('OpenReport', params, onyxData); + API.write('OpenReport', parameters, {optimisticData, successData, failureData}); } } diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index 145fac3ea85a..fc0e1c4d0e69 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -226,4 +226,4 @@ type OriginalMessage = | OriginalMessageMoved; export default OriginalMessage; -export type {ChronosOOOEvent, Decision, Reaction, ActionName, IOUMessage, Closed, OriginalMessageActionName, ChangeLog, OriginalMessageIOU}; +export type {ChronosOOOEvent, Decision, Reaction, ActionName, IOUMessage, Closed, OriginalMessageActionName, ChangeLog, OriginalMessageIOU, OriginalMessageCreated}; From aa93c7e399903ec64e13782eb26e714122c12ee1 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 11:25:55 +0100 Subject: [PATCH 08/28] Fix type problems with NotificationPreference --- src/libs/ReportUtils.ts | 5 +++-- src/libs/actions/Report.ts | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 53d3e9a067ff..c2da244c6f35 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -17,6 +17,7 @@ import ROUTES from '@src/ROUTES'; import {Beta, Login, PersonalDetails, Policy, PolicyTags, Report, ReportAction, Transaction} from '@src/types/onyx'; import {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; import {ChangeLog, IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage'; +import {NotificationPreference} from '@src/types/onyx/Report'; import {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; import {Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; import DeepValueOf from '@src/types/utils/DeepValueOf'; @@ -3013,7 +3014,7 @@ function buildOptimisticChatReport( oldPolicyName = '', visibility: ValueOf | undefined = undefined, writeCapability: ValueOf | undefined = undefined, - notificationPreference: string | number = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, + notificationPreference: NotificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, parentReportActionID = '', parentReportID = '', welcomeMessage = '', @@ -4382,4 +4383,4 @@ export { canEditWriteCapability, }; -export type {OptionData}; +export type {OptionData, OptimisticChatReport}; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 6cabb50c18ef..e3b5a733202b 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -30,7 +30,7 @@ import {PersonalDetails} from '@src/types/onyx'; import {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import Report, {NotificationPreference, WriteCapability} from '@src/types/onyx/Report'; import ReportAction, {Message, ReportActionBase} from '@src/types/onyx/ReportAction'; -import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; +import {EmptyObject, isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; import * as Session from './Session'; import * as Welcome from './Welcome'; @@ -662,7 +662,7 @@ function openReport( * @param shouldDismissModal a flag to determine if we should dismiss modal before navigate to report or navigate to report directly. */ function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true) { - let newChat = {}; + let newChat: ReportUtils.OptimisticChatReport | EmptyObject = {}; const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(userLogins); const chat = ReportUtils.getChatByParticipants(participantAccountIDs); @@ -687,7 +687,7 @@ function navigateToAndOpenReport(userLogins: string[], shouldDismissModal = true * @param participantAccountIDs of user logins to start a chat report with. */ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[]) { - let newChat = {}; + let newChat: ReportUtils.OptimisticChatReport | EmptyObject = {}; const chat = ReportUtils.getChatByParticipants(participantAccountIDs); if (!chat) { newChat = ReportUtils.buildOptimisticChatReport(participantAccountIDs); From 475bbb8ac92ad546e2492cb9964d8155f8db6659 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 12:41:16 +0100 Subject: [PATCH 09/28] Migrate API actions --- src/libs/PersonalDetailsUtils.js | 2 +- src/libs/actions/Report.ts | 399 ++++++++++++++++--------------- 2 files changed, 207 insertions(+), 194 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 560480dcec9d..88b476a03100 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -80,7 +80,7 @@ function getAccountIDsByLogins(logins) { * Given a list of accountIDs, find the associated personal detail and return related logins. * * @param {Array} accountIDs Array of user accountIDs - * @returns {Array} - Array of logins according to passed accountIDs + * @returns {Array} - Array of logins according to passed accountIDs */ function getLoginsByAccountIDs(accountIDs) { return _.reduce( diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index e3b5a733202b..bc595630ea4e 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -4,6 +4,7 @@ import Str from 'expensify-common/lib/str'; import lodashDebounce from 'lodash/debounce'; import lodashGet from 'lodash/get'; import isEmpty from 'lodash/isEmpty'; +import {DeviceEventEmitter} from 'react-native'; import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import {NullishDeep} from 'react-native-onyx/lib/types'; import * as ActiveClientManager from '@libs/ActiveClientManager'; @@ -326,7 +327,7 @@ function addActions(reportID: string, text = '', file?: File) { optimisticReportActions[attachmentAction.reportActionID] = attachmentAction; } - type Parameters = { + type AddCommentOrAttachementParameters = { reportID: string; reportActionID?: string; commentReportActionID?: string | null; @@ -335,7 +336,7 @@ function addActions(reportID: string, text = '', file?: File) { timezone?: string; }; - const parameters: Parameters = { + const parameters: AddCommentOrAttachementParameters = { reportID, reportActionID: file ? attachmentAction?.reportActionID : reportCommentAction?.reportActionID, commentReportActionID: file && reportCommentAction ? reportCommentAction.reportActionID : null, @@ -528,7 +529,7 @@ function openReport( }, ]; - type Parameters = { + type OpenReportParameters = { reportID: string; emailList?: string; accountIDList?: string; @@ -538,7 +539,7 @@ function openReport( clientLastReadTime?: string; }; - const parameters: Parameters = { + const parameters: OpenReportParameters = { reportID, emailList: participantLoginList ? participantLoginList.join(',') : '', accountIDList: participantAccountIDList ? participantAccountIDList.join(',') : '', @@ -706,18 +707,20 @@ function navigateToAndOpenReportWithAccountIDs(participantAccountIDs: number[]) * @param parentReportAction the parent comment of a thread * @param parentReportID The reportID of the parent */ -function navigateToAndOpenChildReport(childReportID = '0', parentReportAction = {}, parentReportID = '0') { +function navigateToAndOpenChildReport(childReportID = '0', parentReportAction: Partial = {}, parentReportID = '0') { if (childReportID !== '0') { openReport(childReportID); Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(childReportID)); } else { - const participantAccountIDs = _.uniq([currentUserAccountID, Number(parentReportAction.actorAccountID)]); - const parentReport = allReports[parentReportID]; + const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction.actorAccountID)])].filter( + (accountID): accountID is number => typeof accountID === 'number', + ); + const parentReport = allReports?.[parentReportID]; const newChat = ReportUtils.buildOptimisticChatReport( participantAccountIDs, - lodashGet(parentReportAction, ['message', 0, 'text']), - lodashGet(parentReport, 'chatType', ''), - lodashGet(parentReport, 'policyID', CONST.POLICY.OWNER_EMAIL_FAKE), + parentReportAction?.message?.[0]?.text, + parentReport?.chatType, + parentReport?.policyID ?? CONST.POLICY.OWNER_EMAIL_FAKE, CONST.POLICY.OWNER_ACCOUNT_ID_FAKE, false, '', @@ -728,60 +731,62 @@ function navigateToAndOpenChildReport(childReportID = '0', parentReportAction = parentReportID, ); - const participantLogins = PersonalDetailsUtils.getLoginsByAccountIDs(newChat.participantAccountIDs); + const participantLogins = PersonalDetailsUtils.getLoginsByAccountIDs(newChat?.participantAccountIDs ?? []); openReport(newChat.reportID, participantLogins, newChat, parentReportAction.reportActionID); Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(newChat.reportID)); } } -/** - * Get the latest report history without marking the report as read. - */ +/** Get the latest report history without marking the report as read. */ function reconnect(reportID: string) { - API.write( - 'ReconnectToReport', + const optimisticData: OnyxUpdate[] = [ { - reportID, + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + reportName: allReports?.[reportID]?.reportName ?? CONST.REPORT.DEFAULT_REPORT_NAME, + }, }, { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - reportName: lodashGet(allReports, [reportID, 'reportName'], CONST.REPORT.DEFAULT_REPORT_NAME), - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, - value: { - isLoadingInitialReportActions: true, - isLoadingNewerReportActions: false, - isLoadingOlderReportActions: false, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, - value: { - isLoadingInitialReportActions: false, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, - value: { - isLoadingInitialReportActions: false, - }, - }, - ], + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, + value: { + isLoadingInitialReportActions: true, + isLoadingNewerReportActions: false, + isLoadingOlderReportActions: false, + }, }, - ); + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, + value: { + isLoadingInitialReportActions: false, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, + value: { + isLoadingInitialReportActions: false, + }, + }, + ]; + + type ReconnectToReportParameters = { + reportID: string; + }; + + const parameters: ReconnectToReportParameters = { + reportID, + }; + + API.write('ReconnectToReport', parameters, {optimisticData, successData, failureData}); } /** @@ -789,165 +794,182 @@ function reconnect(reportID: string) { * Normally happens when you scroll up on a chat, and the actions have not been read yet. */ function getOlderActions(reportID: string, reportActionID: string) { - API.read( - 'GetOlderActions', + const optimisticData: OnyxUpdate[] = [ { - reportID, - reportActionID, + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, + value: { + isLoadingOlderReportActions: true, + }, }, + ]; + + const successData: OnyxUpdate[] = [ { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, - value: { - isLoadingOlderReportActions: true, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, - value: { - isLoadingOlderReportActions: false, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, - value: { - isLoadingOlderReportActions: false, - }, - }, - ], + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, + value: { + isLoadingOlderReportActions: false, + }, }, - ); + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, + value: { + isLoadingOlderReportActions: false, + }, + }, + ]; + + type GetOlderActionsParameters = { + reportID: string; + reportActionID: string; + }; + + const parameters: GetOlderActionsParameters = { + reportID, + reportActionID, + }; + API.read('GetOlderActions', parameters, {optimisticData, successData, failureData}); } /** * Gets the newer actions that have not been read yet. * Normally happens when you are not located at the bottom of the list and scroll down on a chat. - * - * @param reportID - * @param reportActionID */ -function getNewerActions(reportID, reportActionID) { - API.read( - 'GetNewerActions', +function getNewerActions(reportID: string, reportActionID: string) { + const optimisticData: OnyxUpdate[] = [ { - reportID, - reportActionID, + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, + value: { + isLoadingNewerReportActions: true, + }, }, + ]; + + const successData: OnyxUpdate[] = [ { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, - value: { - isLoadingNewerReportActions: true, - }, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, - value: { - isLoadingNewerReportActions: false, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, - value: { - isLoadingNewerReportActions: false, - }, - }, - ], + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, + value: { + isLoadingNewerReportActions: false, + }, }, - ); + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportID}`, + value: { + isLoadingNewerReportActions: false, + }, + }, + ]; + + type GetNewerActionsParameters = { + reportID: string; + reportActionID: string; + }; + + const parameters: GetNewerActionsParameters = { + reportID, + reportActionID, + }; + + API.read('GetNewerActions', parameters, {optimisticData, successData, failureData}); } /** * Gets metadata info about links in the provided report action */ function expandURLPreview(reportID: string, reportActionID: string) { - API.read('ExpandURLPreview', { + type ExpandURLPreviewParameters = { + reportID: string; + reportActionID: string; + }; + + const parameters: ExpandURLPreviewParameters = { reportID, reportActionID, - }); + }; + + API.read('ExpandURLPreview', parameters); } -/** - * Marks the new report actions as read - */ +/** Marks the new report actions as read */ function readNewestAction(reportID: string) { const lastReadTime = DateUtils.getDBTime(); - API.write( - 'ReadNewestAction', - { - reportID, - lastReadTime, - }, + + const optimisticData: OnyxUpdate[] = [ { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - lastReadTime, - }, - }, - ], + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + lastReadTime, + }, }, - ); + ]; + + type ReadNewestActionParameters = { + reportID: string; + lastReadTime: string; + }; + + const parameters: ReadNewestActionParameters = { + reportID, + lastReadTime, + }; + + API.write('ReadNewestAction', parameters, {optimisticData}); } /** * Sets the last read time on a report */ -function markCommentAsUnread(reportID: string, reportActionCreated: boolean) { +function markCommentAsUnread(reportID: string, reportActionCreated: string) { // If no action created date is provided, use the last action's - const actionCreationTime = reportActionCreated || lodashGet(allReports, [reportID, 'lastVisibleActionCreated'], DateUtils.getDBTime(new Date(0))); + const actionCreationTime = reportActionCreated || (allReports?.[reportID]?.lastVisibleActionCreated ?? DateUtils.getDBTime(0)); // We subtract 1 millisecond so that the lastReadTime is updated to just before a given reportAction's created date // For example, if we want to mark a report action with ID 100 and created date '2014-04-01 16:07:02.999' unread, we set the lastReadTime to '2014-04-01 16:07:02.998' // Since the report action with ID 100 will be the first with a timestamp above '2014-04-01 16:07:02.998', it's the first one that will be shown as unread const lastReadTime = DateUtils.subtractMillisecondsFromDateTime(actionCreationTime, 1); - API.write( - 'MarkAsUnread', - { - reportID, - lastReadTime, - }, + + const optimisticData: OnyxUpdate[] = [ { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - lastReadTime, - }, - }, - ], + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + lastReadTime, + }, }, - ); + ]; + + type MarkAsUnreadParameters = { + reportID: string; + lastReadTime: string; + }; + + const parameters: MarkAsUnreadParameters = { + reportID, + lastReadTime, + }; + + API.write('MarkAsUnread', parameters, {optimisticData}); DeviceEventEmitter.emit(`unreadAction_${reportID}`, lastReadTime); } -/** - * Toggles the pinned state of the report. - */ +/** Toggles the pinned state of the report. */ function togglePinnedState(reportID: string, isPinnedChat: boolean) { const pinnedValue = !isPinnedChat; // Optimistically pin/unpin the report before we send out the command - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, @@ -955,14 +977,17 @@ function togglePinnedState(reportID: string, isPinnedChat: boolean) { }, ]; - API.write( - 'TogglePinnedChat', - { - reportID, - pinnedValue, - }, - {optimisticData}, - ); + type TogglePinnedChatParameters = { + reportID: string; + pinnedValue: boolean; + }; + + const parameters: TogglePinnedChatParameters = { + reportID, + pinnedValue, + }; + + API.write('TogglePinnedChat', parameters, {optimisticData}); } /** @@ -973,23 +998,17 @@ function saveReportComment(reportID: string, comment: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`, comment); } -/** - * Saves the number of lines for the comment - */ +/** Saves the number of lines for the comment */ function saveReportCommentNumberOfLines(reportID: string, numberOfLines: number) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}`, numberOfLines); } -/** - * Immediate indication whether the report has a draft comment. - */ -function setReportWithDraft(reportID: string, hasDraft: boolean) { +/** Immediate indication whether the report has a draft comment. */ +function setReportWithDraft(reportID: string, hasDraft: boolean): Promise { return Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {hasDraft}); } -/** - * Broadcasts whether or not a user is typing on a report over the report's private pusher channel. - */ +/** Broadcasts whether or not a user is typing on a report over the report's private pusher channel. */ function broadcastUserIsTyping(reportID: string) { const privateReportChannelName = getReportChannelName(reportID); const typingStatus: Record = currentUserAccountID @@ -1000,9 +1019,7 @@ function broadcastUserIsTyping(reportID: string) { Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_TYPING, typingStatus); } -/** - * Broadcasts to the report's private pusher channel whether a user is leaving a report - */ +/** Broadcasts to the report's private pusher channel whether a user is leaving a report */ function broadcastUserIsLeavingRoom(reportID: string) { const privateReportChannelName = getReportChannelName(reportID); const leavingStatus: Record = currentUserAccountID @@ -1013,9 +1030,7 @@ function broadcastUserIsLeavingRoom(reportID: string) { Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, leavingStatus); } -/** - * When a report changes in Onyx, this fetches the report from the API if the report doesn't have a name - */ +/** When a report changes in Onyx, this fetches the report from the API if the report doesn't have a name */ function handleReportChanged(report: OnyxEntry) { if (!report) { return; @@ -1035,7 +1050,7 @@ function handleReportChanged(report: OnyxEntry) { return; } - if (report?.reportID) { + if (allReports && report?.reportID) { allReports[report.reportID] = report; if (ReportUtils.isConciergeChatReport(report)) { @@ -1055,9 +1070,7 @@ Onyx.connect({ callback: handleReportChanged, }); -/** - * Deletes a comment from the report, basically sets it as empty string - */ +/** Deletes a comment from the report, basically sets it as empty string */ function deleteReportComment(reportID: string, reportAction: ReportAction) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); const reportActionID = reportAction.reportActionID; From a66d6f735fd4987f5bd0d21ad952780045668975 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 13:47:14 +0100 Subject: [PATCH 10/28] Migrate next portion of Report utils --- src/libs/actions/Report.ts | 107 ++++++++++++++++++++------------- src/types/onyx/ReportAction.ts | 8 +++ 2 files changed, 73 insertions(+), 42 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index bc595630ea4e..7e1ee906b4d8 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -7,6 +7,7 @@ import isEmpty from 'lodash/isEmpty'; import {DeviceEventEmitter} from 'react-native'; import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import {NullishDeep} from 'react-native-onyx/lib/types'; +import {PartialDeep} from 'type-fest'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import * as API from '@libs/API'; import * as CollectionUtils from '@libs/CollectionUtils'; @@ -30,7 +31,7 @@ import ROUTES from '@src/ROUTES'; import {PersonalDetails} from '@src/types/onyx'; import {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import Report, {NotificationPreference, WriteCapability} from '@src/types/onyx/Report'; -import ReportAction, {Message, ReportActionBase} from '@src/types/onyx/ReportAction'; +import ReportAction, {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; import {EmptyObject, isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; import * as Session from './Session'; import * as Welcome from './Welcome'; @@ -1075,11 +1076,11 @@ function deleteReportComment(reportID: string, reportAction: ReportAction) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); const reportActionID = reportAction.reportActionID; - if (!reportActionID) { + if (!reportActionID || !originalReportID) { return; } - const deletedMessage = [ + const deletedMessage: Message[] = [ { translationKey: '', type: 'COMMENT', @@ -1089,7 +1090,7 @@ function deleteReportComment(reportID: string, reportAction: ReportAction) { isDeletedParentAction: ReportActionsUtils.isThreadParentMessage(reportAction, reportID), }, ]; - const optimisticReportActions = { + const optimisticReportActions: NullishDeep = { [reportActionID]: { pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, previousMessage: reportAction.message, @@ -1106,11 +1107,11 @@ function deleteReportComment(reportID: string, reportAction: ReportAction) { lastMessageText: '', lastVisibleActionCreated: '', }; - const {lastMessageText = '', lastMessageTranslationKey = ''} = ReportUtils.getLastVisibleMessage(originalReportID, optimisticReportActions); + const {lastMessageText = '', lastMessageTranslationKey = ''} = ReportUtils.getLastVisibleMessage(originalReportID, optimisticReportActions as ReportActions); if (lastMessageText || lastMessageTranslationKey) { - const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(originalReportID, optimisticReportActions); - const lastVisibleActionCreated = lodashGet(lastVisibleAction, 'created'); - const lastActorAccountID = lodashGet(lastVisibleAction, 'actorAccountID'); + const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(originalReportID, optimisticReportActions as ReportActions); + const lastVisibleActionCreated = lastVisibleAction?.created; + const lastActorAccountID = lastVisibleAction?.actorAccountID; optimisticReport = { lastMessageTranslationKey, lastMessageText, @@ -1162,22 +1163,28 @@ function deleteReportComment(reportID: string, reportAction: ReportAction) { ]; // Update optimistic data for parent report action if the report is a child report and the reportAction has no visible child - const childVisibleActionCount = reportAction.childVisibleActionCount || 0; + const childVisibleActionCount = reportAction.childVisibleActionCount ?? 0; if (childVisibleActionCount === 0) { const optimisticParentReportData = ReportUtils.getOptimisticDataForParentReportAction( originalReportID, - optimisticReport.lastVisibleActionCreated, + optimisticReport?.lastVisibleActionCreated ?? '', CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); - if (!_.isEmpty(optimisticParentReportData)) { + if (isNotEmptyObject(optimisticParentReportData)) { optimisticData.push(optimisticParentReportData); } } - const parameters = { + type DeleteCommentParameters = { + reportID: string; + reportActionID: string; + }; + + const parameters: DeleteCommentParameters = { reportID: originalReportID, reportActionID, }; + API.write('DeleteComment', parameters, {optimisticData, successData, failureData}); } @@ -1188,7 +1195,7 @@ function deleteReportComment(reportID: string, reportAction: ReportAction) { * links=["https://www.google.com"] * returns: "test https://www.google.com test" */ -const removeLinksFromHtml = (html: string, links: string[]): string => { +function removeLinksFromHtml(html: string, links: string[]): string { let htmlCopy = html.slice(); links.forEach((link) => { // We want to match the anchor tag of the link and replace the whole anchor tag with the text of the anchor tag @@ -1196,16 +1203,15 @@ const removeLinksFromHtml = (html: string, links: string[]): string => { htmlCopy = htmlCopy.replace(regex, '$4'); }); return htmlCopy; -}; +} /** * This function will handle removing only links that were purposely removed by the user while editing. * * @param newCommentText text of the comment after editing. * @param originalCommentMarkdown original markdown of the comment before editing. - * @returns */ -const handleUserDeletedLinksInHtml = (newCommentText: string, originalHtml: string): string => { +function handleUserDeletedLinksInHtml(newCommentText: string, originalCommentMarkdown: string): string { const parser = new ExpensiMark(); if (newCommentText.length > CONST.MAX_MARKUP_LENGTH) { return newCommentText; @@ -1213,20 +1219,22 @@ const handleUserDeletedLinksInHtml = (newCommentText: string, originalHtml: stri const htmlForNewComment = parser.replace(newCommentText); const removedLinks = parser.getRemovedMarkdownLinks(originalCommentMarkdown, newCommentText); return removeLinksFromHtml(htmlForNewComment, removedLinks); -}; +} -/** - * Saves a new message for a comment. Marks the comment as edited, which will be reflected in the UI. - */ +/** Saves a new message for a comment. Marks the comment as edited, which will be reflected in the UI. */ function editReportComment(reportID: string, originalReportAction: ReportAction, textForNewComment: string) { const parser = new ExpensiMark(); const originalReportID = ReportUtils.getOriginalReportID(reportID, originalReportAction); + if (!originalReportID) { + return; + } + // Do not autolink if someone explicitly tries to remove a link from message. // https://github.com/Expensify/App/issues/9090 // https://github.com/Expensify/App/issues/13221 - const originalCommentHTML = lodashGet(originalReportAction, 'message[0].html'); - const originalCommentMarkdown = parser.htmlToMarkdown(originalCommentHTML).trim(); + const originalCommentHTML = originalReportAction?.message?.[0]?.html; + const originalCommentMarkdown = parser.htmlToMarkdown(originalCommentHTML ?? '').trim(); // Skip the Edit if draft is not changed if (originalCommentMarkdown === textForNewComment) { @@ -1240,12 +1248,12 @@ function editReportComment(reportID: string, originalReportAction: ReportAction, // For longer comments, skip parsing and display plaintext for performance reasons. It takes over 40s to parse a 100k long string!! let parsedOriginalCommentHTML = originalCommentHTML; if (textForNewComment.length <= CONST.MAX_MARKUP_LENGTH) { - const autolinkFilter = {filterRules: _.filter(_.pluck(parser.rules, 'name'), (name) => name !== 'autolink')}; + const autolinkFilter = {filterRules: parser.rules.map((rule) => rule.name).filter((name) => name !== 'autolink')}; parsedOriginalCommentHTML = parser.replace(originalCommentMarkdown, autolinkFilter); } // Delete the comment if it's empty - if (_.isEmpty(htmlForNewComment)) { + if (!htmlForNewComment) { deleteReportComment(originalReportID, originalReportAction); return; } @@ -1257,13 +1265,14 @@ function editReportComment(reportID: string, originalReportAction: ReportAction, // Optimistically update the reportAction with the new message const reportActionID = originalReportAction.reportActionID; - const originalMessage = lodashGet(originalReportAction, ['message', 0]); - const optimisticReportActions = { + const originalMessage = originalReportAction?.message?.[0]; + const optimisticReportActions: PartialDeep = { [reportActionID]: { pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, message: [ { ...originalMessage, + type: '', isEdited: true, html: htmlForNewComment, text: reportComment, @@ -1272,7 +1281,7 @@ function editReportComment(reportID: string, originalReportAction: ReportAction, }, }; - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${originalReportID}`, @@ -1280,8 +1289,8 @@ function editReportComment(reportID: string, originalReportAction: ReportAction, }, ]; - const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(originalReportID, optimisticReportActions); - if (reportActionID === lodashGet(lastVisibleAction, 'reportActionID')) { + const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(originalReportID, optimisticReportActions as ReportActions); + if (reportActionID === lastVisibleAction?.reportActionID) { const lastMessageText = ReportUtils.formatReportLastMessageText(reportComment); const optimisticReport = { lastMessageTranslationKey: '', @@ -1294,7 +1303,7 @@ function editReportComment(reportID: string, originalReportAction: ReportAction, }); } - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${originalReportID}`, @@ -1307,7 +1316,7 @@ function editReportComment(reportID: string, originalReportAction: ReportAction, }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${originalReportID}`, @@ -1319,25 +1328,28 @@ function editReportComment(reportID: string, originalReportAction: ReportAction, }, ]; - const parameters = { + type UpdateCommentParameters = { + reportID: string; + reportComment: string; + reportActionID: string; + }; + + const parameters: UpdateCommentParameters = { reportID: originalReportID, reportComment: htmlForNewComment, reportActionID, }; + API.write('UpdateComment', parameters, {optimisticData, successData, failureData}); } -/** - * Saves the draft for a comment report action. This will put the comment into "edit mode" - */ +/** Saves the draft for a comment report action. This will put the comment into "edit mode" */ function saveReportActionDraft(reportID: string, reportAction: ReportAction, draftMessage: string) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${originalReportID}`, {[reportAction.reportActionID]: draftMessage}); } -/** - * Saves the number of lines for the report action draft - */ +/** Saves the number of lines for the report action draft */ function saveReportActionDraftNumberOfLines(reportID: string, reportActionID: string, numberOfLines: number) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES}${reportID}_${reportActionID}`, numberOfLines); } @@ -1349,14 +1361,15 @@ function updateNotificationPreference( navigate: boolean, parentReportID = 0, parentReportActionID = 0, - report = {}, + report: OnyxEntry | EmptyObject = {}, ) { if (previousValue === newValue) { - if (navigate && report.reportID) { + if (navigate && isNotEmptyObject(report) && report.reportID) { ReportUtils.goBackToDetailsPage(report); } return; } + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -1364,6 +1377,7 @@ function updateNotificationPreference( value: {notificationPreference: newValue}, }, ]; + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -1371,6 +1385,7 @@ function updateNotificationPreference( value: {notificationPreference: previousValue}, }, ]; + if (parentReportID && parentReportActionID) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -1383,8 +1398,16 @@ function updateNotificationPreference( value: {[parentReportActionID]: {childReportNotificationPreference: previousValue}}, }); } - API.write('UpdateReportNotificationPreference', {reportID, notificationPreference: newValue}, {optimisticData, failureData}); - if (navigate) { + + type UpdateReportNotificationPreferenceParameters = { + reportID: string; + notificationPreference: NotificationPreference; + }; + + const parameters: UpdateReportNotificationPreferenceParameters = {reportID, notificationPreference: newValue}; + + API.write('UpdateReportNotificationPreference', parameters, {optimisticData, failureData}); + if (navigate && isNotEmptyObject(report)) { ReportUtils.goBackToDetailsPage(report); } } diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 1bc21a433203..0d0b6db63e98 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -4,6 +4,7 @@ import CONST from '@src/CONST'; import * as OnyxCommon from './OnyxCommon'; import OriginalMessage, {Decision, Reaction} from './OriginalMessage'; import {Receipt} from './Transaction'; +import { NotificationPreference } from './Report'; type Message = { /** The type of the action item fragment. Used to render a corresponding component */ @@ -79,6 +80,9 @@ type ReportActionBase = { /** report action message */ message?: Message[]; + /** report action message */ + previousMessage?: Message[]; + /** Whether we have received a response back from the server */ isLoading?: boolean; @@ -138,6 +142,10 @@ type ReportActionBase = { isAttachment?: boolean; childRecentReceiptTransactionIDs?: Record; reportID?: string; + linkMetadata?: string[]; + + /** The current user's notification preference for this report's child */ + childReportNotificationPreference?: NotificationPreference; }; type ReportAction = ReportActionBase & OriginalMessage; From 11c2b2fea4267fce415f3848404c569cb3547d5a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 14:22:13 +0100 Subject: [PATCH 11/28] Start migrating emoji reactions --- src/libs/actions/Report.ts | 152 +++++++++++++++++------- src/types/onyx/ReportActionReactions.ts | 5 + 2 files changed, 113 insertions(+), 44 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7e1ee906b4d8..7d15462347e2 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -4,10 +4,12 @@ import Str from 'expensify-common/lib/str'; import lodashDebounce from 'lodash/debounce'; import lodashGet from 'lodash/get'; import isEmpty from 'lodash/isEmpty'; +import {type} from 'os'; import {DeviceEventEmitter} from 'react-native'; import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import {NullishDeep} from 'react-native-onyx/lib/types'; -import {PartialDeep} from 'type-fest'; +import {PartialDeep, ValueOf} from 'type-fest'; +import {Emoji} from '@assets/emojis/types'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import * as API from '@libs/API'; import * as CollectionUtils from '@libs/CollectionUtils'; @@ -1356,11 +1358,11 @@ function saveReportActionDraftNumberOfLines(reportID: string, reportActionID: st function updateNotificationPreference( reportID: string, - previousValue: NotificationPreference, + previousValue: NotificationPreference | undefined, newValue: NotificationPreference, navigate: boolean, - parentReportID = 0, - parentReportActionID = 0, + parentReportID?: string, + parentReportActionID?: string, report: OnyxEntry | EmptyObject = {}, ) { if (previousValue === newValue) { @@ -1420,23 +1422,25 @@ function updateNotificationPreference( * @param parentReportID The reportID of the parent * @param prevNotificationPreference The previous notification preference for the child report */ -function toggleSubscribeToChildReport(childReportID = '0', parentReportAction = {}, parentReportID = '0', prevNotificationPreference: any) { +function toggleSubscribeToChildReport(childReportID = '0', parentReportAction: Partial = {}, parentReportID = '0', prevNotificationPreference?: NotificationPreference) { if (childReportID !== '0') { openReport(childReportID); - const parentReportActionID = lodashGet(parentReportAction, 'reportActionID', '0'); + const parentReportActionID = parentReportAction?.reportActionID ?? '0'; if (!prevNotificationPreference || prevNotificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) { updateNotificationPreference(childReportID, prevNotificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, false, parentReportID, parentReportActionID); } else { updateNotificationPreference(childReportID, prevNotificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, false, parentReportID, parentReportActionID); } } else { - const participantAccountIDs = _.uniq([currentUserAccountID, Number(parentReportAction.actorAccountID)]); - const parentReport = allReports[parentReportID]; + const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction?.actorAccountID)])].filter( + (accountID): accountID is number => typeof accountID === 'number', + ); + const parentReport = allReports?.[parentReportID]; const newChat = ReportUtils.buildOptimisticChatReport( participantAccountIDs, - lodashGet(parentReportAction, ['message', 0, 'text']), - lodashGet(parentReport, 'chatType', ''), - lodashGet(parentReport, 'policyID', CONST.POLICY.OWNER_EMAIL_FAKE), + parentReportAction?.message?.[0]?.text, + parentReport?.chatType, + parentReport?.policyID ?? CONST.POLICY.OWNER_EMAIL_FAKE, CONST.POLICY.OWNER_ACCOUNT_ID_FAKE, false, '', @@ -1447,11 +1451,11 @@ function toggleSubscribeToChildReport(childReportID = '0', parentReportAction = parentReportID, ); - const participantLogins = PersonalDetailsUtils.getLoginsByAccountIDs(newChat.participantAccountIDs); + const participantLogins = PersonalDetailsUtils.getLoginsByAccountIDs(participantAccountIDs); openReport(newChat.reportID, participantLogins, newChat, parentReportAction.reportActionID); const notificationPreference = prevNotificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN ? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS : CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; - updateNotificationPreference(newChat.reportID, prevNotificationPreference, notificationPreference, false, parentReportID, parentReportAction.reportActionID); + updateNotificationPreference(newChat.reportID, prevNotificationPreference, notificationPreference, false, parentReportID, parentReportAction?.reportActionID); } } @@ -1463,6 +1467,7 @@ function updateWelcomeMessage(reportID: string, previousValue: string, newValue: } const parsedWelcomeMessage = ReportUtils.getParsedComment(newValue); + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -1477,7 +1482,15 @@ function updateWelcomeMessage(reportID: string, previousValue: string, newValue: value: {welcomeMessage: previousValue}, }, ]; - API.write('UpdateWelcomeMessage', {reportID, welcomeMessage: parsedWelcomeMessage}, {optimisticData, failureData}); + + type UpdateWelcomeMessageParameters = { + reportID: string; + welcomeMessage: string; + }; + + const parameters: UpdateWelcomeMessageParameters = {reportID, welcomeMessage: parsedWelcomeMessage}; + + API.write('UpdateWelcomeMessage', parameters, {optimisticData, failureData}); Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(reportID)); } @@ -1502,7 +1515,14 @@ function updateWriteCapabilityAndNavigate(report: Report, newValue: WriteCapabil }, ]; - API.write('UpdateReportWriteCapability', {reportID: report.reportID, writeCapability: newValue}, {optimisticData, failureData}); + type UpdateReportWriteCapabilityParameters = { + reportID: string; + writeCapability: WriteCapability; + }; + + const parameters: UpdateReportWriteCapabilityParameters = {reportID: report.reportID, writeCapability: newValue}; + + API.write('UpdateReportWriteCapability', parameters, {optimisticData, failureData}); // Return to the report settings page since this field utilizes push-to-page Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(report.reportID)); } @@ -1533,11 +1553,14 @@ function navigateToConciergeChat(ignoreConciergeReportID = false) { function addPolicyReport( policyID: string, reportName: string, - visibility: string, - policyMembersAccountIDs: number[], + visibility: ValueOf, writeCapability: WriteCapability = CONST.REPORT.WRITE_CAPABILITIES.ALL, welcomeMessage = '', ) { + if (!currentUserAccountID) { + return; + } + const participants = [currentUserAccountID]; const parsedWelcomeMessage = ReportUtils.getParsedComment(welcomeMessage); const policyReport = ReportUtils.buildOptimisticChatReport( @@ -1611,26 +1634,34 @@ function addPolicyReport( }, ]; - API.write( - 'AddWorkspaceRoom', - { - policyID: policyReport.policyID, - reportName, - visibility, - reportID: policyReport.reportID, - createdReportActionID: createdReportAction.reportActionID, - writeCapability, - welcomeMessage: parsedWelcomeMessage, - }, - {optimisticData, successData, failureData}, - ); + type AddWorkspaceRoomParameters = { + policyID?: string; + reportName: string; + visibility: ValueOf; + reportID: string; + createdReportActionID: string; + writeCapability: WriteCapability; + welcomeMessage: string; + }; + + const parameters: AddWorkspaceRoomParameters = { + policyID: policyReport.policyID, + reportName, + visibility, + reportID: policyReport.reportID, + createdReportActionID: createdReportAction.reportActionID, + writeCapability, + welcomeMessage: parsedWelcomeMessage, + }; + + API.write('AddWorkspaceRoom', parameters, {optimisticData, successData, failureData}); Navigation.dismissModal(policyReport.reportID); } /** Deletes a report, along with its reportActions, any linked reports, and any linked IOU report. */ function deleteReport(reportID: string) { - const report = allReports[reportID]; - const onyxData = { + const report = allReports?.[reportID]; + const onyxData: Record = { [`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]: null, [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`]: null, }; @@ -1676,6 +1707,7 @@ function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomNam Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(reportID)); return; } + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -1711,7 +1743,15 @@ function updatePolicyRoomNameAndNavigate(policyRoomReport: Report, policyRoomNam }, }, ]; - API.write('UpdatePolicyRoomName', {reportID, policyRoomName}, {optimisticData, successData, failureData}); + + type UpdatePolicyRoomNameParameters = { + reportID: string; + policyRoomName: string; + }; + + const parameters: UpdatePolicyRoomNameParameters = {reportID, policyRoomName}; + + API.write('UpdatePolicyRoomName', parameters, {optimisticData, successData, failureData}); Navigation.goBack(ROUTES.REPORT_SETTINGS.getRoute(reportID)); } @@ -1772,8 +1812,8 @@ function shouldShowReportActionNotification(reportID: string, action: ReportActi } // If this notification was delayed and the user saw the message already, don't show it - const report = allReports[reportID]; - if (action && report && report.lastReadTime >= action.created) { + const report = allReports?.[reportID]; + if (action && report?.lastReadTime && report.lastReadTime >= action.created) { Log.info(`${tag} No notification because the comment was already read`, false, {created: action.created, lastReadTime: report.lastReadTime}); return false; } @@ -1793,7 +1833,7 @@ function showReportActionNotification(reportID: string, reportAction: ReportActi } Log.info('[LocalNotification] Creating notification'); - const report = allReports[reportID]; + const report = allReports?.[reportID]; const notificationParams = { report, @@ -1809,9 +1849,7 @@ function showReportActionNotification(reportID: string, reportAction: ReportActi notifyNewAction(reportID, reportAction.actorAccountID, reportAction.reportActionID); } -/** - * Clear the errors associated with the IOUs of a given report. - */ +/** Clear the errors associated with the IOUs of a given report. */ function clearIOUError(reportID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {errorFields: {iou: null}}); } @@ -1820,9 +1858,13 @@ function clearIOUError(reportID: string) { * Adds a reaction to the report action. * Uses the NEW FORMAT for "emojiReactions" */ -function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji, skinTone: number = preferredSkinTone) { +function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji, skinTone: string | number = preferredSkinTone) { + if (!currentUserAccountID) { + return; + } + const createdAt = timezoneFormat(utcToZonedTime(new Date(), 'UTC'), CONST.DATE.FNS_DB_FORMAT_STRING); - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, @@ -1866,7 +1908,16 @@ function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji }, ]; - const parameters = { + type AddEmojiReactionParameters = { + reportID: string; + skinTone: string | number; + emojiCode: string; + reportActionID: string; + createdAt: string; + useEmojiReactions: boolean; + }; + + const parameters: AddEmojiReactionParameters = { reportID, skinTone, emojiCode: emoji.name, @@ -1875,6 +1926,7 @@ function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji // This will be removed as part of https://github.com/Expensify/App/issues/19535 useEmojiReactions: true, }; + API.write('AddEmojiReaction', parameters, {optimisticData, successData, failureData}); } @@ -1883,7 +1935,11 @@ function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji * Uses the NEW FORMAT for "emojiReactions" */ function removeEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji) { - const optimisticData = [ + if (!currentUserAccountID) { + return; + } + + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_REACTIONS}${reportActionID}`, @@ -1897,13 +1953,21 @@ function removeEmojiReaction(reportID: string, reportActionID: string, emoji: Em }, ]; - const parameters = { + type RemoveEmojiReactionParameters = { + reportID: string; + reportActionID: string; + emojiCode: string; + useEmojiReactions: boolean; + }; + + const parameters: RemoveEmojiReactionParameters = { reportID, reportActionID, emojiCode: emoji.name, // This will be removed as part of https://github.com/Expensify/App/issues/19535 useEmojiReactions: true, }; + API.write('RemoveEmojiReaction', parameters, {optimisticData}); } diff --git a/src/types/onyx/ReportActionReactions.ts b/src/types/onyx/ReportActionReactions.ts index 99b437ea7242..e5421725fc1f 100644 --- a/src/types/onyx/ReportActionReactions.ts +++ b/src/types/onyx/ReportActionReactions.ts @@ -1,3 +1,5 @@ +import * as OnyxCommon from './OnyxCommon'; + type User = { /** The skin tone which was used and also the timestamp of when it was added */ skinTones: Record; @@ -6,8 +8,11 @@ type User = { type ReportActionReaction = { /** The time the emoji was added */ createdAt: string; + /** All the users who have added this emoji */ users: Record; + + pendingAction?: OnyxCommon.PendingAction; }; type ReportActionReactions = Record; From 55a955fda68e8d01ffdd656fb58a3f2ec8cae9b7 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 14:24:51 +0100 Subject: [PATCH 12/28] Remove unused imports --- src/libs/actions/Report.ts | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7d15462347e2..37ae53876884 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2,10 +2,8 @@ import {format as timezoneFormat, utcToZonedTime} from 'date-fns-tz'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; import lodashDebounce from 'lodash/debounce'; -import lodashGet from 'lodash/get'; import isEmpty from 'lodash/isEmpty'; -import {type} from 'os'; -import {DeviceEventEmitter} from 'react-native'; +import {DeviceEventEmitter, InteractionManager} from 'react-native'; import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import {NullishDeep} from 'react-native-onyx/lib/types'; import {PartialDeep, ValueOf} from 'type-fest'; @@ -1793,7 +1791,7 @@ function shouldShowReportActionNotification(reportID: string, action: ReportActi } // We don't want to send a local notification if the user preference is daily, mute or hidden. - const notificationPreference = lodashGet(allReports, [reportID, 'notificationPreference'], CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS); + const notificationPreference = allReports?.[reportID]?.notificationPreference ?? CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS; if (notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS) { Log.info(`${tag} No notification because user preference is to be notified: ${notificationPreference}`); return false; @@ -1820,7 +1818,7 @@ function shouldShowReportActionNotification(reportID: string, action: ReportActi // Only show notifications for supported types of report actions if (!ReportActionsUtils.isNotifiableReportAction(action)) { - Log.info(`${tag} No notification because this action type is not supported`, false, {actionName: lodashGet(action, 'actionName')}); + Log.info(`${tag} No notification because this action type is not supported`, false, {actionName: action?.actionName}); return false; } @@ -2040,7 +2038,7 @@ function getCurrentUserAccountID(): number | undefined { /** Leave a report by setting the state to submitted and closed */ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = false) { - const report = lodashGet(allReports, [reportID], {}); + const report = allReports?.[reportID] ?? {}; const reportKeys = _.keys(report); // Pusher's leavingStatus should be sent earlier. @@ -2102,14 +2100,9 @@ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = fal } } -/** - * Invites people to a room - * - * @param reportID - * @param inviteeEmailsToAccountIDs - */ +/** Invites people to a room */ function inviteToRoom(reportID, inviteeEmailsToAccountIDs) { - const report = lodashGet(allReports, [reportID], {}); + const report = allReports?.[reportID] ?? {}; const inviteeEmails = _.keys(inviteeEmailsToAccountIDs); const inviteeAccountIDs = _.values(inviteeEmailsToAccountIDs); @@ -2146,14 +2139,9 @@ function inviteToRoom(reportID, inviteeEmailsToAccountIDs) { ); } -/** - * Removes people from a room - * - * @param reportID - * @param targetAccountIDs - */ +/** Removes people from a room */ function removeFromRoom(reportID, targetAccountIDs) { - const report = lodashGet(allReports, [reportID], {}); + const report = allReports?.[reportID] ?? {}; const {participantAccountIDs} = report; const participantAccountIDsAfterRemoval = _.difference(participantAccountIDs, targetAccountIDs); From 5ea6bd748610d6cd7cc9721d8dbf2ed73e791370 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 15:44:14 +0100 Subject: [PATCH 13/28] Migrate the rest of Report.js --- src/libs/EmojiUtils.ts | 36 +- .../{index.website.js => index.js} | 4 +- src/libs/SidebarUtils.ts | 2 +- src/libs/actions/Report.ts | 428 +++++++++--------- src/types/onyx/OriginalMessage.ts | 2 +- src/types/onyx/Report.ts | 8 +- src/types/onyx/ReportActionReactions.ts | 14 +- 7 files changed, 254 insertions(+), 240 deletions(-) rename src/libs/Notification/LocalNotification/{index.website.js => index.js} (89%) diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index f249632fdc06..749064066975 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -8,29 +8,13 @@ import {Emoji, HeaderEmoji, PickerEmojis} from '@assets/emojis/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {FrequentlyUsedEmoji} from '@src/types/onyx'; +import {ReportActionReaction, UserReactions} from '@src/types/onyx/ReportActionReactions'; import emojisTrie from './EmojiTrie'; type HeaderIndice = {code: string; index: number; icon: React.FC}; type EmojiSpacer = {code: string; spacer: boolean}; type EmojiPickerList = Array; type ReplacedEmoji = {text: string; emojis: Emoji[]; cursorPosition?: number}; -type UserReactions = { - id: string; - skinTones: Record; -}; -type UserReactionsWithTimestamps = UserReactions & { - oldestTimestamp: string; -}; -type UsersReactionsList = { - createdAt: string; - users: Record; -}; -type TimestampedUsersReactions = Record; -type EnrichedUserReactions = { - createdAt: string; - oldestTimestamp: string; - users: TimestampedUsersReactions; -}; let frequentlyUsedEmojis: FrequentlyUsedEmoji[] = []; Onyx.connect({ @@ -436,9 +420,9 @@ function suggestEmojis(text: string, lang: keyof typeof emojisTrie, limit = CONS /** * Retrieve preferredSkinTone as Number to prevent legacy 'default' String value */ -const getPreferredSkinToneIndex = (val: string | number | null): number | string => { +const getPreferredSkinToneIndex = (val: string | number | null): number => { if (val !== null && Number.isInteger(Number(val))) { - return val; + return Number(val); } return CONST.EMOJI_DEFAULT_SKIN_TONE; @@ -467,7 +451,7 @@ const getPreferredEmojiCode = (emoji: Emoji, preferredSkinTone: number): string * array of emoji codes, that represents all used variations of the * emoji, sorted by the reaction timestamp. */ -const getUniqueEmojiCodes = (emojiAsset: Emoji, users: TimestampedUsersReactions): string[] => { +const getUniqueEmojiCodes = (emojiAsset: Emoji, users: Record): string[] => { const emojiCodes: Record = Object.values(users ?? {}).reduce((result: Record, userSkinTones) => { Object.keys(userSkinTones?.skinTones ?? {}).forEach((skinTone) => { const createdAt = userSkinTones.skinTones[Number(skinTone)]; @@ -486,12 +470,12 @@ const getUniqueEmojiCodes = (emojiAsset: Emoji, users: TimestampedUsersReactions /** * Given an emoji reaction object and its name, it populates it with the oldest reaction timestamps. */ -const enrichEmojiReactionWithTimestamps = (emoji: UsersReactionsList, emojiName: string): EnrichedUserReactions => { +const enrichEmojiReactionWithTimestamps = (emoji: ReportActionReaction, emojiName: string): ReportActionReaction => { let oldestEmojiTimestamp: string | null = null; - const usersWithTimestamps: Record = {}; + const usersWithTimestamps: Record = {}; Object.keys(emoji.users ?? {}).forEach((id) => { - const user = emoji?.users?.[id]; + const user = emoji?.users?.[Number(id)]; const userTimestamps = Object.values(user?.skinTones ?? {}); const oldestUserTimestamp = userTimestamps.reduce((min, curr) => (curr < min ? curr : min), userTimestamps[0]); @@ -499,7 +483,7 @@ const enrichEmojiReactionWithTimestamps = (emoji: UsersReactionsList, emojiName: oldestEmojiTimestamp = oldestUserTimestamp; } - usersWithTimestamps[id] = { + usersWithTimestamps[Number(id)] = { ...user, id, oldestTimestamp: oldestUserTimestamp, @@ -521,7 +505,7 @@ const enrichEmojiReactionWithTimestamps = (emoji: UsersReactionsList, emojiName: * Uses the NEW FORMAT for "emojiReactions" * @param usersReactions - all the users reactions */ -function hasAccountIDEmojiReacted(accountID: string, usersReactions: TimestampedUsersReactions, skinTone?: number) { +function hasAccountIDEmojiReacted(accountID: number, usersReactions: Record, skinTone?: number) { if (skinTone === undefined) { return Boolean(usersReactions[accountID]); } @@ -535,7 +519,7 @@ function hasAccountIDEmojiReacted(accountID: string, usersReactions: Timestamped /** * Given an emoji reaction and current user's account ID, it returns the reusable details of the emoji reaction. */ -const getEmojiReactionDetails = (emojiName: string, reaction: UsersReactionsList, currentUserAccountID: string) => { +const getEmojiReactionDetails = (emojiName: string, reaction: ReportActionReaction, currentUserAccountID: number) => { const {users, oldestTimestamp} = enrichEmojiReactionWithTimestamps(reaction, emojiName); const emoji = findEmojiByName(emojiName); diff --git a/src/libs/Notification/LocalNotification/index.website.js b/src/libs/Notification/LocalNotification/index.js similarity index 89% rename from src/libs/Notification/LocalNotification/index.website.js rename to src/libs/Notification/LocalNotification/index.js index 0ecb8bcbb81d..0573324e7812 100644 --- a/src/libs/Notification/LocalNotification/index.website.js +++ b/src/libs/Notification/LocalNotification/index.js @@ -2,7 +2,7 @@ import BrowserNotifications from './BrowserNotifications'; /** * @param {Object} options - * @param {Object} options.report + * @param {Object|null|undefined} options.report * @param {Object} options.reportAction * @param {Function} options.onClick */ @@ -16,7 +16,7 @@ function showUpdateAvailableNotification() { /** * @param {Object} options - * @param {Object} options.report + * @param {Object|null|undefined} options.report * @param {Object} options.reportAction * @param {Function} options.onClick */ diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index ff9486159947..f1ba77fda116 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -307,7 +307,7 @@ function getOptionData( result.hasOutstandingChildRequest = report.hasOutstandingChildRequest; result.parentReportID = report.parentReportID ?? ''; result.isWaitingOnBankAccount = report.isWaitingOnBankAccount; - result.notificationPreference = report.notificationPreference ?? ''; + result.notificationPreference = report.notificationPreference; result.isAllowedToComment = ReportUtils.canUserPerformWriteAction(report); result.chatType = report.chatType; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 37ae53876884..23f207d8ca62 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -28,7 +28,7 @@ import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import {PersonalDetails} from '@src/types/onyx'; +import {PersonalDetails, ReportActionReactions} from '@src/types/onyx'; import {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import Report, {NotificationPreference, WriteCapability} from '@src/types/onyx/Report'; import ReportAction, {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; @@ -36,12 +36,12 @@ import {EmptyObject, isEmptyObject, isNotEmptyObject} from '@src/types/utils/Emp import * as Session from './Session'; import * as Welcome from './Welcome'; -let currentUserAccountID: number | undefined; +let currentUserAccountID: number; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { // When signed out, val is undefined - if (!val) { + if (!val?.accountID) { return; } @@ -49,7 +49,7 @@ Onyx.connect({ }, }); -let preferredSkinTone: string | number; +let preferredSkinTone: number; Onyx.connect({ key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, callback: (val) => { @@ -713,9 +713,7 @@ function navigateToAndOpenChildReport(childReportID = '0', parentReportAction: P openReport(childReportID); Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(childReportID)); } else { - const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction.actorAccountID)])].filter( - (accountID): accountID is number => typeof accountID === 'number', - ); + const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction.actorAccountID)])]; const parentReport = allReports?.[parentReportID]; const newChat = ReportUtils.buildOptimisticChatReport( participantAccountIDs, @@ -1012,22 +1010,18 @@ function setReportWithDraft(reportID: string, hasDraft: boolean): Promise /** Broadcasts whether or not a user is typing on a report over the report's private pusher channel. */ function broadcastUserIsTyping(reportID: string) { const privateReportChannelName = getReportChannelName(reportID); - const typingStatus: Record = currentUserAccountID - ? { - [currentUserAccountID]: true, - } - : {}; + const typingStatus: Record = { + [currentUserAccountID]: true, + }; Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_TYPING, typingStatus); } /** Broadcasts to the report's private pusher channel whether a user is leaving a report */ function broadcastUserIsLeavingRoom(reportID: string) { const privateReportChannelName = getReportChannelName(reportID); - const leavingStatus: Record = currentUserAccountID - ? { - [currentUserAccountID]: true, - } - : {}; + const leavingStatus: Record = { + [currentUserAccountID]: true, + }; Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, leavingStatus); } @@ -1430,9 +1424,7 @@ function toggleSubscribeToChildReport(childReportID = '0', parentReportAction: P updateNotificationPreference(childReportID, prevNotificationPreference, CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, false, parentReportID, parentReportActionID); } } else { - const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction?.actorAccountID)])].filter( - (accountID): accountID is number => typeof accountID === 'number', - ); + const participantAccountIDs = [...new Set([currentUserAccountID, Number(parentReportAction?.actorAccountID)])]; const parentReport = allReports?.[parentReportID]; const newChat = ReportUtils.buildOptimisticChatReport( participantAccountIDs, @@ -1555,10 +1547,6 @@ function addPolicyReport( writeCapability: WriteCapability = CONST.REPORT.WRITE_CAPABILITIES.ALL, welcomeMessage = '', ) { - if (!currentUserAccountID) { - return; - } - const participants = [currentUserAccountID]; const parsedWelcomeMessage = ReportUtils.getParsedComment(welcomeMessage); const policyReport = ReportUtils.buildOptimisticChatReport( @@ -1857,10 +1845,6 @@ function clearIOUError(reportID: string) { * Uses the NEW FORMAT for "emojiReactions" */ function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji, skinTone: string | number = preferredSkinTone) { - if (!currentUserAccountID) { - return; - } - const createdAt = timezoneFormat(utcToZonedTime(new Date(), 'UTC'), CONST.DATE.FNS_DB_FORMAT_STRING); const optimisticData: OnyxUpdate[] = [ { @@ -1933,10 +1917,6 @@ function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji * Uses the NEW FORMAT for "emojiReactions" */ function removeEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji) { - if (!currentUserAccountID) { - return; - } - const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -1973,11 +1953,22 @@ function removeEmojiReaction(reportID: string, reportActionID: string, emoji: Em * Calls either addEmojiReaction or removeEmojiReaction depending on if the current user has reacted to the report action. * Uses the NEW FORMAT for "emojiReactions" */ -function toggleEmojiReaction(reportID: string, reportAction: ReportAction, reactionObject: {code: string}, existingReactions: string, paramSkinTone: number = preferredSkinTone) { +function toggleEmojiReaction( + reportID: string, + reportAction: ReportAction, + reactionObject: Emoji, + existingReactions: OnyxCollection, + paramSkinTone: number = preferredSkinTone, +) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); + + if (!originalReportID) { + return; + } + const originalReportAction = ReportActionsUtils.getReportAction(originalReportID, reportAction.reportActionID); - if (_.isEmpty(originalReportAction)) { + if (isEmptyObject(originalReportAction)) { return; } @@ -1989,7 +1980,7 @@ function toggleEmojiReaction(reportID: string, reportAction: ReportAction, react // Only use skin tone if emoji supports it const skinTone = emoji.types === undefined ? -1 : paramSkinTone; - if (existingReactionObject && EmojiUtils.hasAccountIDEmojiReacted(currentUserAccountID, existingReactionObject.users, skinTone)) { + if (existingReactionObject && EmojiUtils.hasAccountIDEmojiReacted(currentUserAccountID, existingReactionObject?.users, skinTone)) { removeEmojiReaction(originalReportID, reportAction.reportActionID, emoji); return; } @@ -2032,14 +2023,17 @@ function openReportFromDeepLink(url: string, isAuthenticated: boolean) { }); } -function getCurrentUserAccountID(): number | undefined { +function getCurrentUserAccountID(): number { return currentUserAccountID; } /** Leave a report by setting the state to submitted and closed */ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = false) { - const report = allReports?.[reportID] ?? {}; - const reportKeys = _.keys(report); + const report = allReports?.[reportID]; + + if (!report) { + return; + } // Pusher's leavingStatus should be sent earlier. // Place the broadcast before calling the LeaveRoom API to prevent a race condition @@ -2048,152 +2042,173 @@ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = fal // If a workspace member is leaving a workspace room, they don't actually lose the room from Onyx. // Instead, their notification preference just gets set to "hidden". - const optimisticData = [ - isWorkspaceMemberLeavingWorkspaceRoom - ? { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, - }, - } - : { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, - statusNum: CONST.REPORT.STATUS.CLOSED, - }, - }, - ]; - - const successData = [ + const optimisticData: OnyxUpdate[] = []; + const successData: OnyxUpdate[] = []; + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: isWorkspaceMemberLeavingWorkspaceRoom ? {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN} : _.object(reportKeys, Array(reportKeys.length).fill(null)), + value: report, }, ]; - API.write( - 'LeaveRoom', - { - reportID, - }, - { - optimisticData, - successData, - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: report, - }, - ], - }, - ); + if (isWorkspaceMemberLeavingWorkspaceRoom) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + }, + }); + + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN}, + }); + } else { + optimisticData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + reportID, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, + statusNum: CONST.REPORT.STATUS.CLOSED, + }, + }); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: Object.keys(report).reduce>((acc, key) => { + acc[key] = null; + return acc; + }, {}), + }); + } + + type LeaveRoomParameters = { + reportID: string; + }; + + const parameters: LeaveRoomParameters = { + reportID, + }; + + API.write('LeaveRoom', parameters, {optimisticData, successData, failureData}); if (isWorkspaceMemberLeavingWorkspaceRoom) { const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.CONCIERGE]); const chat = ReportUtils.getChatByParticipants(participantAccountIDs); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chat.reportID)); + if (chat?.reportID) { + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chat.reportID)); + } } } /** Invites people to a room */ -function inviteToRoom(reportID, inviteeEmailsToAccountIDs) { - const report = allReports?.[reportID] ?? {}; +function inviteToRoom(reportID: string, inviteeEmailsToAccountIDs: Record) { + const report = allReports?.[reportID]; + + if (!report) { + return; + } - const inviteeEmails = _.keys(inviteeEmailsToAccountIDs); - const inviteeAccountIDs = _.values(inviteeEmailsToAccountIDs); + const inviteeEmails = Object.keys(inviteeEmailsToAccountIDs); + const inviteeAccountIDs = Object.values(inviteeEmailsToAccountIDs); - const {participantAccountIDs} = report; - const participantAccountIDsAfterInvitation = _.uniq([...participantAccountIDs, ...inviteeAccountIDs]); + const participantAccountIDsAfterInvitation = [...new Set([...(report?.participantAccountIDs ?? []), ...inviteeAccountIDs])].filter( + (accountID): accountID is number => typeof accountID === 'number', + ); - API.write( - 'InviteToRoom', + const optimisticData: OnyxUpdate[] = [ { - reportID, - inviteeEmails, + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + participantAccountIDs: participantAccountIDsAfterInvitation, + }, }, + ]; + + const failureData: OnyxUpdate[] = [ { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - participantAccountIDs: participantAccountIDsAfterInvitation, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - participantAccountIDs, - }, - }, - ], + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + participantAccountIDs: report.participantAccountIDs, + }, }, - ); + ]; + + type InviteToRoomParameters = { + reportID: string; + inviteeEmails: string[]; + }; + + const parameters: InviteToRoomParameters = { + reportID, + inviteeEmails, + }; + + API.write('InviteToRoom', parameters, {optimisticData, failureData}); } /** Removes people from a room */ -function removeFromRoom(reportID, targetAccountIDs) { - const report = allReports?.[reportID] ?? {}; +function removeFromRoom(reportID: string, targetAccountIDs: number[]) { + const report = allReports?.[reportID]; - const {participantAccountIDs} = report; - const participantAccountIDsAfterRemoval = _.difference(participantAccountIDs, targetAccountIDs); + const participantAccountIDsAfterRemoval = report?.participantAccountIDs?.filter((id: number) => !targetAccountIDs.includes(id)); - API.write( - 'RemoveFromRoom', + const optimisticData: OnyxUpdate[] = [ { - reportID, - targetAccountIDs, + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + participantAccountIDs: participantAccountIDsAfterRemoval, + }, }, + ]; + + const failureData: OnyxUpdate[] = [ { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - participantAccountIDs: participantAccountIDsAfterRemoval, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - participantAccountIDs, - }, - }, - ], + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + participantAccountIDs: report?.participantAccountIDs, + }, + }, + ]; - // We need to add success data here since in high latency situations, - // the OpenRoomMembersPage call has the chance of overwriting the optimistic data we set above. - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - participantAccountIDs: participantAccountIDsAfterRemoval, - }, - }, - ], + // We need to add success data here since in high latency situations, + // the OpenRoomMembersPage call has the chance of overwriting the optimistic data we set above. + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + participantAccountIDs: participantAccountIDsAfterRemoval, + }, }, - ); + ]; + + type RemoveFromRoomParameters = { + reportID: string; + targetAccountIDs: number[]; + }; + + const parameters: RemoveFromRoomParameters = { + reportID, + targetAccountIDs, + }; + + API.write('RemoveFromRoom', parameters, {optimisticData, failureData, successData}); } -function setLastOpenedPublicRoom(reportID) { +function setLastOpenedPublicRoom(reportID: string) { Onyx.set(ONYXKEYS.LAST_OPENED_PUBLIC_ROOM_ID, reportID); } -/** - * Navigates to the last opened public room - */ +/** Navigates to the last opened public room */ function openLastOpenedPublicRoom(lastOpenedPublicRoomID: string) { Navigation.isNavigationReady().then(() => { setLastOpenedPublicRoom(''); @@ -2201,9 +2216,7 @@ function openLastOpenedPublicRoom(lastOpenedPublicRoomID: string) { }); } -/** - * Flag a comment as offensive - */ +/** Flag a comment as offensive */ function flagComment(reportID: string, reportAction: ReportAction, severity: string) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); const message = reportAction?.message?.[0]; @@ -2276,7 +2289,13 @@ function flagComment(reportID: string, reportAction: ReportAction, severity: str }, ]; - const parameters = { + type FlagCommentParameters = { + severity: string; + reportActionID: string; + isDevRequest: boolean; + }; + + const parameters: FlagCommentParameters = { severity, reportActionID, // This check is to prevent flooding Concierge with test flags @@ -2287,9 +2306,7 @@ function flagComment(reportID: string, reportAction: ReportAction, severity: str API.write('FlagComment', parameters, {optimisticData, successData, failureData}); } -/** - * Updates a given user's private notes on a report - */ +/** Updates a given user's private notes on a report */ const updatePrivateNotes = (reportID: string, accountID: number, note: string) => { const optimisticData: OnyxUpdate[] = [ { @@ -2336,12 +2353,17 @@ const updatePrivateNotes = (reportID: string, accountID: number, note: string) = }, ]; - API.write('UpdateReportPrivateNote', {reportID, privateNotes: note}, {optimisticData, successData, failureData}); + type UpdateReportPrivateNoteParameters = { + reportID: string; + privateNotes: string; + }; + + const parameters: UpdateReportPrivateNoteParameters = {reportID, privateNotes: note}; + + API.write('UpdateReportPrivateNote', parameters, {optimisticData, successData, failureData}); }; -/** - * Fetches all the private notes for a given report - */ +/** Fetches all the private notes for a given report */ function getReportPrivateNote(reportID: string) { if (!reportID) { return; @@ -2377,18 +2399,24 @@ function getReportPrivateNote(reportID: string) { }, ]; - API.read('GetReportPrivateNote', {reportID}, {optimisticData, successData, failureData}); + type GetReportPrivateNoteParameters = { + reportID: string; + }; + + const parameters: GetReportPrivateNoteParameters = {reportID}; + + API.read('GetReportPrivateNote', parameters, {optimisticData, successData, failureData}); } -/** - * Loads necessary data for rendering the RoomMembersPage - * - * @param reportID - */ -function openRoomMembersPage(reportID) { - API.read('OpenRoomMembersPage', { - reportID, - }); +/** Loads necessary data for rendering the RoomMembersPage */ +function openRoomMembersPage(reportID: string) { + type OpenRoomMembersPageParameters = { + reportID: string; + }; + + const parameters: OpenRoomMembersPageParameters = {reportID}; + + API.read('OpenRoomMembersPage', parameters); } /** @@ -2401,15 +2429,13 @@ function hasErrorInPrivateNotes(report: Report): boolean { return Object.values(privateNotes).some((privateNote) => !isEmpty(privateNote.errors)); } -/** - * Clears all errors associated with a given private note - */ +/** Clears all errors associated with a given private note */ function clearPrivateNotesError(reportID: string, accountID: number) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {privateNotes: {[accountID]: {errors: null}}}); } -function getDraftPrivateNote(reportID: string) { - return draftNoteMap[reportID] ?? ''; +function getDraftPrivateNote(reportID: string): string { + return draftNoteMap?.[reportID] ?? ''; } /** @@ -2420,49 +2446,41 @@ function savePrivateNotesDraft(reportID: string, note: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT}${reportID}`, note); } -/** - * @private - * @param searchInput - */ -function searchForReports(searchInput) { +function searchForReports(searchInput: string) { // We do not try to make this request while offline because it sets a loading indicator optimistically if (isNetworkOffline) { Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, false); return; } - API.read( - 'SearchForReports', - {searchInput}, + const successData: OnyxUpdate[] = [ { - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, - value: false, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, - value: false, - }, - ], + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + value: false, }, - ); + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS, + value: false, + }, + ]; + + type SearchForReportsParameters = { + searchInput: string; + }; + + const parameters: SearchForReportsParameters = {searchInput}; + + API.read('SearchForReports', parameters, {successData, failureData}); } -/** - * @private - * @param {string} searchInput - */ const debouncedSearchInServer = lodashDebounce(searchForReports, CONST.TIMING.SEARCH_FOR_REPORTS_DEBOUNCE_TIME, {leading: false}); -/** - * @param searchInput - */ -function searchInServer(searchInput) { +function searchInServer(searchInput: string) { if (isNetworkOffline) { Onyx.set(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, false); return; diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index fc0e1c4d0e69..ff4f3d18bb56 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -70,7 +70,7 @@ type DecisionName = ValueOf< >; type Decision = { decision: DecisionName; - timestamp: string; + timestamp?: string; }; type User = { diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index bf373c6b31cd..9208ae54b388 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -7,6 +7,12 @@ type NotificationPreference = ValueOf; +type Note = { + note: string; + errors?: OnyxCommon.Errors; + pendingAction?: OnyxCommon.PendingAction; +}; + type Report = { /** The specific type of chat */ chatType?: ValueOf; @@ -143,7 +149,7 @@ type Report = { isChatRoom?: boolean; participantsList?: Array>; text?: string; - privateNotes?: Record; + privateNotes?: Record; isLoadingPrivateNotes?: boolean; }; diff --git a/src/types/onyx/ReportActionReactions.ts b/src/types/onyx/ReportActionReactions.ts index e5421725fc1f..f35a6a7d4227 100644 --- a/src/types/onyx/ReportActionReactions.ts +++ b/src/types/onyx/ReportActionReactions.ts @@ -1,16 +1,22 @@ import * as OnyxCommon from './OnyxCommon'; -type User = { +type UserReactions = { + id: string; + /** The skin tone which was used and also the timestamp of when it was added */ - skinTones: Record; + skinTones: Record; + + oldestTimestamp: string; }; type ReportActionReaction = { /** The time the emoji was added */ createdAt: string; + oldestTimestamp: string; + /** All the users who have added this emoji */ - users: Record; + users: Record; pendingAction?: OnyxCommon.PendingAction; }; @@ -19,4 +25,4 @@ type ReportActionReactions = Record; export default ReportActionReactions; -export type {User}; +export type {UserReactions, ReportActionReaction}; From 66fc2dfaff566e954b82c809020d648936790a8c Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 15:51:19 +0100 Subject: [PATCH 14/28] Fix a typo after refactoring --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 23f207d8ca62..69951269912b 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -393,7 +393,7 @@ function addActions(reportID: string, text = '', file?: File) { const failureReportActions: OnyxCollection> = {}; Object.entries(optimisticReportActions).forEach(([actionKey, action]) => { - optimisticReportActions[actionKey] = { + failureReportActions[actionKey] = { ...action, errors: ErrorUtils.getMicroSecondOnyxError('report.genericAddCommentFailureMessage'), }; From 057c3c1f0a28ca2cf26ed7f74d036884887a6fd1 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 16:37:55 +0100 Subject: [PATCH 15/28] Fix imports --- src/types/onyx/ReportAction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 0d0b6db63e98..2ee6e81ec49d 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -3,8 +3,8 @@ import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import * as OnyxCommon from './OnyxCommon'; import OriginalMessage, {Decision, Reaction} from './OriginalMessage'; +import {NotificationPreference} from './Report'; import {Receipt} from './Transaction'; -import { NotificationPreference } from './Report'; type Message = { /** The type of the action item fragment. Used to render a corresponding component */ From e41d5247bbefc1b0050d8a81084a4d883e60d57a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 28 Nov 2023 16:54:40 +0100 Subject: [PATCH 16/28] Fix tests after migration --- src/libs/actions/Report.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 69951269912b..48b4eecaadfd 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -361,7 +361,7 @@ function addActions(reportID: string, text = '', file?: File) { const successReportActions: OnyxCollection> = {}; Object.entries(optimisticReportActions).forEach(([actionKey]) => { - optimisticReportActions[actionKey] = {pendingAction: null}; + successReportActions[actionKey] = {pendingAction: null}; }); const successData: OnyxUpdate[] = [ From 0fca53f344b6e4e341b36a8a5c57d05c94c9056e Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 29 Nov 2023 18:13:04 +0100 Subject: [PATCH 17/28] Adjust the code after internal review --- src/libs/EmojiUtils.ts | 7 +++---- src/libs/actions/Report.ts | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index 749064066975..60f586359dde 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -473,9 +473,8 @@ const getUniqueEmojiCodes = (emojiAsset: Emoji, users: Record { let oldestEmojiTimestamp: string | null = null; - const usersWithTimestamps: Record = {}; - Object.keys(emoji.users ?? {}).forEach((id) => { - const user = emoji?.users?.[Number(id)]; + const usersWithTimestamps: Record = {}; + Object.entries(emoji.users ?? {}).forEach(([id, user]) => { const userTimestamps = Object.values(user?.skinTones ?? {}); const oldestUserTimestamp = userTimestamps.reduce((min, curr) => (curr < min ? curr : min), userTimestamps[0]); @@ -483,7 +482,7 @@ const enrichEmojiReactionWithTimestamps = (emoji: ReportActionReaction, emojiNam oldestEmojiTimestamp = oldestUserTimestamp; } - usersWithTimestamps[Number(id)] = { + usersWithTimestamps[id] = { ...user, id, oldestTimestamp: oldestUserTimestamp, diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 48b4eecaadfd..813788743812 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -262,7 +262,7 @@ function subscribeToNewActionEvent(reportID: string, callback: SubscriberCallbac } /** Notify the ReportActionsView that a new comment has arrived */ -function notifyNewAction(reportID: string, accountID: number | undefined, reportActionID: string | undefined) { +function notifyNewAction(reportID: string, accountID?: number, reportActionID?: string) { const actionSubscriber = newActionSubscribers.find((subscriber) => subscriber.reportID === reportID); if (!actionSubscriber) { return; @@ -1857,7 +1857,7 @@ function addEmojiReaction(reportID: string, reportActionID: string, emoji: Emoji users: { [currentUserAccountID]: { skinTones: { - [skinTone ?? -1]: createdAt, + [skinTone ?? CONST.EMOJI_DEFAULT_SKIN_TONE]: createdAt, }, }, }, From e731ade44900b33e5162db3c94c9fbbd116a68eb Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 29 Nov 2023 19:00:11 +0100 Subject: [PATCH 18/28] Fix typecheck after merging main --- src/libs/actions/Report.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 813788743812..4c01e2ca11c7 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -27,7 +27,7 @@ import Visibility from '@libs/Visibility'; import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; +import ROUTES, {Route} from '@src/ROUTES'; import {PersonalDetails, ReportActionReactions} from '@src/types/onyx'; import {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import Report, {NotificationPreference, WriteCapability} from '@src/types/onyx/Report'; @@ -2017,7 +2017,7 @@ function openReportFromDeepLink(url: string, isAuthenticated: boolean) { Session.signOutAndRedirectToSignIn(); return; } - Navigation.navigate(route, CONST.NAVIGATION.ACTION_TYPE.PUSH); + Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH); }); }); }); From ee66d75725a031469e67f5b498fb84ddaad65910 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 1 Dec 2023 14:51:41 +0100 Subject: [PATCH 19/28] Adjust the code after cross review --- src/libs/Pusher/pusher.ts | 6 ++--- src/libs/actions/Report.ts | 41 +++++++++++++++-------------- src/types/onyx/ReportAction.ts | 10 ++++++- src/types/onyx/UserIsLeavingRoom.ts | 3 +++ src/types/onyx/UserIsTyping.ts | 3 +++ src/types/onyx/index.ts | 4 +++ 6 files changed, 43 insertions(+), 24 deletions(-) create mode 100644 src/types/onyx/UserIsLeavingRoom.ts create mode 100644 src/types/onyx/UserIsTyping.ts diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index a15ddc66e743..0182298806d2 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -5,7 +5,7 @@ import {LiteralUnion, ValueOf} from 'type-fest'; import Log from '@libs/Log'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import {OnyxUpdateEvent, OnyxUpdatesFromServer} from '@src/types/onyx'; +import {OnyxUpdateEvent, OnyxUpdatesFromServer, UserIsLeavingRoom, UserIsTyping} from '@src/types/onyx'; import DeepValueOf from '@src/types/utils/DeepValueOf'; import TYPE from './EventType'; import Pusher from './library'; @@ -24,11 +24,11 @@ type Args = { type PushJSON = OnyxUpdateEvent[] | OnyxUpdatesFromServer; -type UserIsTypingEvent = Record & { +type UserIsTypingEvent = UserIsTyping & { userLogin?: string; }; -type UserIsLeavingRoomEvent = Record & { +type UserIsLeavingRoomEvent = UserIsLeavingRoom & { userLogin?: string; }; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 4c01e2ca11c7..f0f2c21c1472 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -28,7 +28,7 @@ import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES, {Route} from '@src/ROUTES'; -import {PersonalDetails, ReportActionReactions} from '@src/types/onyx'; +import {PersonalDetails, ReportActionReactions, UserIsLeavingRoom, UserIsTyping} from '@src/types/onyx'; import {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import Report, {NotificationPreference, WriteCapability} from '@src/types/onyx/Report'; import ReportAction, {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; @@ -36,6 +36,13 @@ import {EmptyObject, isEmptyObject, isNotEmptyObject} from '@src/types/utils/Emp import * as Session from './Session'; import * as Welcome from './Welcome'; +type SubscriberCallback = (isFromCurrentUser: boolean, reportActionID: string | undefined) => void; + +type ActionSubscriber = { + reportID: string; + callback: SubscriberCallback; +}; + let currentUserAccountID: number; Onyx.connect({ key: ONYXKEYS.SESSION, @@ -57,7 +64,7 @@ Onyx.connect({ }, }); -const allReportActions: OnyxCollection> = {}; +const allReportActions: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.REPORT_ACTIONS, callback: (action, key) => { @@ -127,8 +134,8 @@ function getReportChannelName(reportID: string): string { * * This method makes sure that no matter which we get, we return the "new" format */ -function getNormalizedStatus(typingStatus: Pusher.UserIsTypingEvent | Pusher.UserIsLeavingRoomEvent): Record { - let normalizedStatus: Record; +function getNormalizedStatus(typingStatus: Pusher.UserIsTypingEvent | Pusher.UserIsLeavingRoomEvent): UserIsTyping { + let normalizedStatus: UserIsTyping; if (typingStatus.userLogin) { normalizedStatus = {[typingStatus.userLogin]: true}; @@ -172,7 +179,7 @@ function subscribeToReportTypingEvents(reportID: string) { // Wait for 1.5s of no additional typing events before setting the status back to false. typingWatchTimers[reportUserIdentifier] = setTimeout(() => { - const typingStoppedStatus: Record = {}; + const typingStoppedStatus: UserIsTyping = {}; typingStoppedStatus[accountIDOrLogin] = false; Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, typingStoppedStatus); delete typingWatchTimers[reportUserIdentifier]; @@ -239,13 +246,6 @@ function unsubscribeFromLeavingRoomReportChannel(reportID: string) { Pusher.unsubscribe(pusherChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM); } -type SubscriberCallback = (isFromCurrentUser: boolean, reportActionID: string | undefined) => void; - -type ActionSubscriber = { - reportID: string; - callback: SubscriberCallback; -}; - // New action subscriber array for report pages let newActionSubscribers: ActionSubscriber[] = []; @@ -832,6 +832,7 @@ function getOlderActions(reportID: string, reportActionID: string) { reportID, reportActionID, }; + API.read('GetOlderActions', parameters, {optimisticData, successData, failureData}); } @@ -1010,7 +1011,7 @@ function setReportWithDraft(reportID: string, hasDraft: boolean): Promise /** Broadcasts whether or not a user is typing on a report over the report's private pusher channel. */ function broadcastUserIsTyping(reportID: string) { const privateReportChannelName = getReportChannelName(reportID); - const typingStatus: Record = { + const typingStatus: UserIsTyping = { [currentUserAccountID]: true, }; Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_TYPING, typingStatus); @@ -1019,7 +1020,7 @@ function broadcastUserIsTyping(reportID: string) { /** Broadcasts to the report's private pusher channel whether a user is leaving a report */ function broadcastUserIsLeavingRoom(reportID: string) { const privateReportChannelName = getReportChannelName(reportID); - const leavingStatus: Record = { + const leavingStatus: UserIsLeavingRoom = { [currentUserAccountID]: true, }; Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, leavingStatus); @@ -1216,18 +1217,18 @@ function handleUserDeletedLinksInHtml(newCommentText: string, originalCommentMar } /** Saves a new message for a comment. Marks the comment as edited, which will be reflected in the UI. */ -function editReportComment(reportID: string, originalReportAction: ReportAction, textForNewComment: string) { +function editReportComment(reportID: string, originalReportAction: OnyxEntry, textForNewComment: string) { const parser = new ExpensiMark(); const originalReportID = ReportUtils.getOriginalReportID(reportID, originalReportAction); - if (!originalReportID) { + if (!originalReportID || !originalReportAction) { return; } // Do not autolink if someone explicitly tries to remove a link from message. // https://github.com/Expensify/App/issues/9090 // https://github.com/Expensify/App/issues/13221 - const originalCommentHTML = originalReportAction?.message?.[0]?.html; + const originalCommentHTML = originalReportAction.message?.[0]?.html; const originalCommentMarkdown = parser.htmlToMarkdown(originalCommentHTML ?? '').trim(); // Skip the Edit if draft is not changed @@ -1266,7 +1267,7 @@ function editReportComment(reportID: string, originalReportAction: ReportAction, message: [ { ...originalMessage, - type: '', + type: CONST.REPORT.MESSAGE.TYPE.COMMENT, isEdited: true, html: htmlForNewComment, text: reportComment, @@ -2217,7 +2218,7 @@ function openLastOpenedPublicRoom(lastOpenedPublicRoomID: string) { } /** Flag a comment as offensive */ -function flagComment(reportID: string, reportAction: ReportAction, severity: string) { +function flagComment(reportID: string, reportAction: OnyxEntry, severity: string) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); const message = reportAction?.message?.[0]; @@ -2424,7 +2425,7 @@ function openRoomMembersPage(reportID: string) { * * @returns Returns true if there are errors in any of the private notes on the report */ -function hasErrorInPrivateNotes(report: Report): boolean { +function hasErrorInPrivateNotes(report: OnyxEntry): boolean { const privateNotes = report?.privateNotes ?? {}; return Object.values(privateNotes).some((privateNote) => !isEmpty(privateNote.errors)); } diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index 4fb6327bcdea..eabe771f9815 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -1,6 +1,7 @@ import {ValueOf} from 'type-fest'; import {AvatarSource} from '@libs/UserUtils'; import CONST from '@src/CONST'; +import {EmptyObject} from '@src/types/utils/EmptyObject'; import * as OnyxCommon from './OnyxCommon'; import OriginalMessage, {Decision, Reaction} from './OriginalMessage'; import {NotificationPreference} from './Report'; @@ -125,7 +126,7 @@ type ReportActionBase = { isFirstItem?: boolean; /** Informations about attachments of report action */ - attachmentInfo?: File | Record; + attachmentInfo?: File | EmptyObject; /** Receipt tied to report action */ receipt?: Receipt; @@ -139,9 +140,16 @@ type ReportActionBase = { /** Server side errors keyed by microtime */ errors?: OnyxCommon.Errors; + /** Whether the report action is attachment */ isAttachment?: boolean; + + /** Recent receipt transaction IDs keyed by reportID */ childRecentReceiptTransactionIDs?: Record; + + /** ReportID of the report action */ reportID?: string; + + /** Metadata of the link */ linkMetadata?: string[]; /** The current user's notification preference for this report's child */ diff --git a/src/types/onyx/UserIsLeavingRoom.ts b/src/types/onyx/UserIsLeavingRoom.ts new file mode 100644 index 000000000000..6be4cf92b802 --- /dev/null +++ b/src/types/onyx/UserIsLeavingRoom.ts @@ -0,0 +1,3 @@ +type UserIsLeavingRoomEvent = Record; + +export default UserIsLeavingRoomEvent; diff --git a/src/types/onyx/UserIsTyping.ts b/src/types/onyx/UserIsTyping.ts new file mode 100644 index 000000000000..e80470198f06 --- /dev/null +++ b/src/types/onyx/UserIsTyping.ts @@ -0,0 +1,3 @@ +type UserIsTyping = Record; + +export default UserIsTyping; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index e7b9c7661c79..717d8bbfb0a7 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -43,6 +43,8 @@ import Session from './Session'; import Task from './Task'; import Transaction from './Transaction'; import User from './User'; +import UserIsLeavingRoom from './UserIsLeavingRoom'; +import UserIsTyping from './UserIsTyping'; import UserLocation from './UserLocation'; import UserWallet from './UserWallet'; import WalletAdditionalDetails from './WalletAdditionalDetails'; @@ -110,4 +112,6 @@ export type { WalletStatement, WalletTerms, WalletTransfer, + UserIsLeavingRoom, + UserIsTyping, }; From 111e6552f51bd7fff267a101e0890d1c4ddd0e5b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 1 Dec 2023 16:19:13 +0100 Subject: [PATCH 20/28] Add explanations to onyx properties --- src/types/onyx/ReportAction.ts | 1 + src/types/onyx/ReportActionReactions.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index eabe771f9815..d30d72c4dd19 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -134,6 +134,7 @@ type ReportActionBase = { /** ISO-formatted datetime */ lastModified?: string; + /** Is this action pending? */ pendingAction?: OnyxCommon.PendingAction; delegateAccountID?: string; diff --git a/src/types/onyx/ReportActionReactions.ts b/src/types/onyx/ReportActionReactions.ts index f35a6a7d4227..9cad5ee54624 100644 --- a/src/types/onyx/ReportActionReactions.ts +++ b/src/types/onyx/ReportActionReactions.ts @@ -1,11 +1,13 @@ import * as OnyxCommon from './OnyxCommon'; type UserReactions = { + /** ID of user reaction */ id: string; /** The skin tone which was used and also the timestamp of when it was added */ skinTones: Record; + /** Oldest timestamp of when the emoji was added */ oldestTimestamp: string; }; @@ -13,11 +15,13 @@ type ReportActionReaction = { /** The time the emoji was added */ createdAt: string; + /** Oldest timestamp of when the emoji was added */ oldestTimestamp: string; /** All the users who have added this emoji */ users: Record; + /** Is this action pending? */ pendingAction?: OnyxCommon.PendingAction; }; From d311cc0509c4406233f01ece3df6224ac4feced2 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 4 Dec 2023 10:43:11 +0100 Subject: [PATCH 21/28] Adjust onyx keys --- src/ONYXKEYS.ts | 2 +- src/libs/Pusher/pusher.ts | 6 +++--- src/libs/actions/Report.ts | 18 +++++++++--------- src/types/onyx/ReportUserIsTyping.ts | 3 +++ src/types/onyx/UserIsLeavingRoom.ts | 3 --- src/types/onyx/UserIsTyping.ts | 3 --- src/types/onyx/index.ts | 6 ++---- 7 files changed, 18 insertions(+), 23 deletions(-) create mode 100644 src/types/onyx/ReportUserIsTyping.ts delete mode 100644 src/types/onyx/UserIsLeavingRoom.ts delete mode 100644 src/types/onyx/UserIsTyping.ts diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 577ae0c95a25..4942397081f7 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -442,7 +442,7 @@ type OnyxValues = { [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT]: string; [ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT_NUMBER_OF_LINES]: number; [ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE]: boolean; - [ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING]: Record; + [ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING]: OnyxTypes.ReportUserIsTyping; [ONYXKEYS.COLLECTION.REPORT_USER_IS_LEAVING_ROOM]: boolean; [ONYXKEYS.COLLECTION.SECURITY_GROUP]: OnyxTypes.SecurityGroup; [ONYXKEYS.COLLECTION.TRANSACTION]: OnyxTypes.Transaction; diff --git a/src/libs/Pusher/pusher.ts b/src/libs/Pusher/pusher.ts index 0182298806d2..33219b1dfe73 100644 --- a/src/libs/Pusher/pusher.ts +++ b/src/libs/Pusher/pusher.ts @@ -5,7 +5,7 @@ import {LiteralUnion, ValueOf} from 'type-fest'; import Log from '@libs/Log'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import {OnyxUpdateEvent, OnyxUpdatesFromServer, UserIsLeavingRoom, UserIsTyping} from '@src/types/onyx'; +import {OnyxUpdateEvent, OnyxUpdatesFromServer, ReportUserIsTyping} from '@src/types/onyx'; import DeepValueOf from '@src/types/utils/DeepValueOf'; import TYPE from './EventType'; import Pusher from './library'; @@ -24,11 +24,11 @@ type Args = { type PushJSON = OnyxUpdateEvent[] | OnyxUpdatesFromServer; -type UserIsTypingEvent = UserIsTyping & { +type UserIsTypingEvent = ReportUserIsTyping & { userLogin?: string; }; -type UserIsLeavingRoomEvent = UserIsLeavingRoom & { +type UserIsLeavingRoomEvent = Record & { userLogin?: string; }; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index aca8ca92ab11..ac70e4917871 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -28,7 +28,7 @@ import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES, {Route} from '@src/ROUTES'; -import {PersonalDetails, ReportActionReactions, UserIsLeavingRoom, UserIsTyping} from '@src/types/onyx'; +import {PersonalDetails, ReportActionReactions, ReportUserIsTyping} from '@src/types/onyx'; import {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import Report, {NotificationPreference, WriteCapability} from '@src/types/onyx/Report'; import ReportAction, {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; @@ -134,8 +134,8 @@ function getReportChannelName(reportID: string): string { * * This method makes sure that no matter which we get, we return the "new" format */ -function getNormalizedStatus(typingStatus: Pusher.UserIsTypingEvent | Pusher.UserIsLeavingRoomEvent): UserIsTyping { - let normalizedStatus: UserIsTyping; +function getNormalizedStatus(typingStatus: Pusher.UserIsTypingEvent | Pusher.UserIsLeavingRoomEvent): ReportUserIsTyping { + let normalizedStatus: ReportUserIsTyping; if (typingStatus.userLogin) { normalizedStatus = {[typingStatus.userLogin]: true}; @@ -179,7 +179,7 @@ function subscribeToReportTypingEvents(reportID: string) { // Wait for 1.5s of no additional typing events before setting the status back to false. typingWatchTimers[reportUserIdentifier] = setTimeout(() => { - const typingStoppedStatus: UserIsTyping = {}; + const typingStoppedStatus: ReportUserIsTyping = {}; typingStoppedStatus[accountIDOrLogin] = false; Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_USER_IS_TYPING}${reportID}`, typingStoppedStatus); delete typingWatchTimers[reportUserIdentifier]; @@ -1011,7 +1011,7 @@ function setReportWithDraft(reportID: string, hasDraft: boolean): Promise /** Broadcasts whether or not a user is typing on a report over the report's private pusher channel. */ function broadcastUserIsTyping(reportID: string) { const privateReportChannelName = getReportChannelName(reportID); - const typingStatus: UserIsTyping = { + const typingStatus: Pusher.UserIsTypingEvent = { [currentUserAccountID]: true, }; Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_TYPING, typingStatus); @@ -1020,7 +1020,7 @@ function broadcastUserIsTyping(reportID: string) { /** Broadcasts to the report's private pusher channel whether a user is leaving a report */ function broadcastUserIsLeavingRoom(reportID: string) { const privateReportChannelName = getReportChannelName(reportID); - const leavingStatus: UserIsLeavingRoom = { + const leavingStatus: Pusher.UserIsLeavingRoomEvent = { [currentUserAccountID]: true, }; Pusher.sendEvent(privateReportChannelName, Pusher.TYPE.USER_IS_LEAVING_ROOM, leavingStatus); @@ -1612,12 +1612,12 @@ function addPolicyReport(policyReport: ReportUtils.OptimisticChatReport) { ]; type AddWorkspaceRoomParameters = { + reportID: string; + createdReportActionID: string; policyID?: string; reportName?: string; visibility?: ValueOf; - reportID: string; - createdReportActionID: string; - writeCapability: WriteCapability; + writeCapability?: WriteCapability; welcomeMessage?: string; }; diff --git a/src/types/onyx/ReportUserIsTyping.ts b/src/types/onyx/ReportUserIsTyping.ts new file mode 100644 index 000000000000..1e6f482ffa7a --- /dev/null +++ b/src/types/onyx/ReportUserIsTyping.ts @@ -0,0 +1,3 @@ +type ReportUserIsTyping = Record; + +export default ReportUserIsTyping; diff --git a/src/types/onyx/UserIsLeavingRoom.ts b/src/types/onyx/UserIsLeavingRoom.ts deleted file mode 100644 index 6be4cf92b802..000000000000 --- a/src/types/onyx/UserIsLeavingRoom.ts +++ /dev/null @@ -1,3 +0,0 @@ -type UserIsLeavingRoomEvent = Record; - -export default UserIsLeavingRoomEvent; diff --git a/src/types/onyx/UserIsTyping.ts b/src/types/onyx/UserIsTyping.ts deleted file mode 100644 index e80470198f06..000000000000 --- a/src/types/onyx/UserIsTyping.ts +++ /dev/null @@ -1,3 +0,0 @@ -type UserIsTyping = Record; - -export default UserIsTyping; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 717d8bbfb0a7..ff5938e4748e 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -35,6 +35,7 @@ import ReportAction, {ReportActions} from './ReportAction'; import ReportActionReactions from './ReportActionReactions'; import ReportActionsDrafts from './ReportActionsDrafts'; import ReportMetadata from './ReportMetadata'; +import ReportUserIsTyping from './ReportUserIsTyping'; import Request from './Request'; import Response from './Response'; import ScreenShareRequest from './ScreenShareRequest'; @@ -43,8 +44,6 @@ import Session from './Session'; import Task from './Task'; import Transaction from './Transaction'; import User from './User'; -import UserIsLeavingRoom from './UserIsLeavingRoom'; -import UserIsTyping from './UserIsTyping'; import UserLocation from './UserLocation'; import UserWallet from './UserWallet'; import WalletAdditionalDetails from './WalletAdditionalDetails'; @@ -112,6 +111,5 @@ export type { WalletStatement, WalletTerms, WalletTransfer, - UserIsLeavingRoom, - UserIsTyping, + ReportUserIsTyping, }; From b24460d08f6981d3a5367773224a2373374efbcf Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 4 Dec 2023 11:05:20 +0100 Subject: [PATCH 22/28] Fix emoji types for report.ts --- src/libs/EmojiUtils.ts | 14 +++++++------- src/libs/actions/Report.ts | 22 +++++++++++----------- src/types/onyx/ReportActionReactions.ts | 8 +++++--- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/libs/EmojiUtils.ts b/src/libs/EmojiUtils.ts index c8c9af8774b6..655f6a40609f 100644 --- a/src/libs/EmojiUtils.ts +++ b/src/libs/EmojiUtils.ts @@ -8,7 +8,7 @@ import {Emoji, HeaderEmoji, PickerEmojis} from '@assets/emojis/types'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {FrequentlyUsedEmoji} from '@src/types/onyx'; -import {ReportActionReaction, UserReactions} from '@src/types/onyx/ReportActionReactions'; +import {ReportActionReaction, UsersReactions} from '@src/types/onyx/ReportActionReactions'; import {SupportedLanguage} from './EmojiTrie'; type HeaderIndice = {code: string; index: number; icon: React.FC}; @@ -426,9 +426,9 @@ function suggestEmojis(text: string, lang: keyof SupportedLanguage, limit = CONS /** * Retrieve preferredSkinTone as Number to prevent legacy 'default' String value */ -const getPreferredSkinToneIndex = (val: string | number | null): number => { - if (val !== null && Number.isInteger(Number(val))) { - return Number(val); +const getPreferredSkinToneIndex = (value: string | number | null): number => { + if (value !== null && Number.isInteger(Number(value))) { + return Number(value); } return CONST.EMOJI_DEFAULT_SKIN_TONE; @@ -457,7 +457,7 @@ const getPreferredEmojiCode = (emoji: Emoji, preferredSkinTone: number): string * array of emoji codes, that represents all used variations of the * emoji, sorted by the reaction timestamp. */ -const getUniqueEmojiCodes = (emojiAsset: Emoji, users: Record): string[] => { +const getUniqueEmojiCodes = (emojiAsset: Emoji, users: UsersReactions): string[] => { const emojiCodes: Record = Object.values(users ?? {}).reduce((result: Record, userSkinTones) => { Object.keys(userSkinTones?.skinTones ?? {}).forEach((skinTone) => { const createdAt = userSkinTones.skinTones[Number(skinTone)]; @@ -479,7 +479,7 @@ const getUniqueEmojiCodes = (emojiAsset: Emoji, users: Record { let oldestEmojiTimestamp: string | null = null; - const usersWithTimestamps: Record = {}; + const usersWithTimestamps: UsersReactions = {}; Object.entries(emoji.users ?? {}).forEach(([id, user]) => { const userTimestamps = Object.values(user?.skinTones ?? {}); const oldestUserTimestamp = userTimestamps.reduce((min, curr) => (curr < min ? curr : min), userTimestamps[0]); @@ -510,7 +510,7 @@ const enrichEmojiReactionWithTimestamps = (emoji: ReportActionReaction, emojiNam * Uses the NEW FORMAT for "emojiReactions" * @param usersReactions - all the users reactions */ -function hasAccountIDEmojiReacted(accountID: number, usersReactions: Record, skinTone?: number) { +function hasAccountIDEmojiReacted(accountID: number, usersReactions: UsersReactions, skinTone?: number) { if (skinTone === undefined) { return Boolean(usersReactions[accountID]); } diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index ac70e4917871..faa3d9f047ab 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -46,21 +46,21 @@ type ActionSubscriber = { let currentUserAccountID: number; Onyx.connect({ key: ONYXKEYS.SESSION, - callback: (val) => { + callback: (value) => { // When signed out, val is undefined - if (!val?.accountID) { + if (!value?.accountID) { return; } - currentUserAccountID = val.accountID; + currentUserAccountID = value.accountID; }, }); let preferredSkinTone: number; Onyx.connect({ key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, - callback: (val) => { - preferredSkinTone = EmojiUtils.getPreferredSkinToneIndex(val); + callback: (value) => { + preferredSkinTone = EmojiUtils.getPreferredSkinToneIndex(value); }, }); @@ -91,16 +91,16 @@ Onyx.connect({ let isNetworkOffline = false; Onyx.connect({ key: ONYXKEYS.NETWORK, - callback: (val) => { - isNetworkOffline = val?.isOffline ?? false; + callback: (value) => { + isNetworkOffline = value?.isOffline ?? false; }, }); let allPersonalDetails: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, - callback: (val) => { - allPersonalDetails = val ?? {}; + callback: (value) => { + allPersonalDetails = value ?? {}; }, }); @@ -1948,7 +1948,7 @@ function toggleEmojiReaction( reportID: string, reportAction: ReportAction, reactionObject: Emoji, - existingReactions: OnyxCollection, + existingReactions: ReportActionReactions | undefined, paramSkinTone: number = preferredSkinTone, ) { const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction); @@ -1971,7 +1971,7 @@ function toggleEmojiReaction( // Only use skin tone if emoji supports it const skinTone = emoji.types === undefined ? -1 : paramSkinTone; - if (existingReactionObject && EmojiUtils.hasAccountIDEmojiReacted(currentUserAccountID, existingReactionObject?.users, skinTone)) { + if (existingReactionObject && EmojiUtils.hasAccountIDEmojiReacted(currentUserAccountID, existingReactionObject.users, skinTone)) { removeEmojiReaction(originalReportID, reportAction.reportActionID, emoji); return; } diff --git a/src/types/onyx/ReportActionReactions.ts b/src/types/onyx/ReportActionReactions.ts index 9cad5ee54624..348a4b1baf62 100644 --- a/src/types/onyx/ReportActionReactions.ts +++ b/src/types/onyx/ReportActionReactions.ts @@ -1,6 +1,6 @@ import * as OnyxCommon from './OnyxCommon'; -type UserReactions = { +type UserReaction = { /** ID of user reaction */ id: string; @@ -11,6 +11,8 @@ type UserReactions = { oldestTimestamp: string; }; +type UsersReactions = Record; + type ReportActionReaction = { /** The time the emoji was added */ createdAt: string; @@ -19,7 +21,7 @@ type ReportActionReaction = { oldestTimestamp: string; /** All the users who have added this emoji */ - users: Record; + users: UsersReactions; /** Is this action pending? */ pendingAction?: OnyxCommon.PendingAction; @@ -29,4 +31,4 @@ type ReportActionReactions = Record; export default ReportActionReactions; -export type {UserReactions, ReportActionReaction}; +export type {UsersReactions, ReportActionReaction}; From ae6ec3d2807b4d0ad87d7dd0c7f51d2b3c93514a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 4 Dec 2023 11:37:55 +0100 Subject: [PATCH 23/28] Fix types for linkMetadata and originalMessage --- src/libs/ReportUtils.ts | 1 - src/libs/actions/Report.ts | 2 +- src/types/onyx/OriginalMessage.ts | 2 +- src/types/onyx/ReportAction.ts | 33 ++++++++++++++++++++++++++++++- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9d7727ea3791..8ed16b43ecf4 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3089,7 +3089,6 @@ function buildOptimisticCreatedReportAction(emailCreatingAction: string, created return { reportActionID: NumberUtils.rand64(), actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, - originalMessage: undefined, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, actorAccountID: currentUserAccountID, message: [ diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index faa3d9f047ab..7d9206d71680 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -96,7 +96,7 @@ Onyx.connect({ }, }); -let allPersonalDetails: OnyxCollection = {}; +let allPersonalDetails: OnyxEntry> = {}; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (value) => { diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index c4eba606ec8b..fc7f8eb8ba31 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -119,7 +119,7 @@ type OriginalMessageClosed = { type OriginalMessageCreated = { actionName: typeof CONST.REPORT.ACTIONS.TYPE.CREATED; - originalMessage: unknown; + originalMessage?: unknown; }; type OriginalMessageRenamed = { diff --git a/src/types/onyx/ReportAction.ts b/src/types/onyx/ReportAction.ts index d30d72c4dd19..b193d5e720ec 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -54,6 +54,37 @@ type Message = { taskReportID?: string; }; +type ImageMetadata = { + /** The height of the image. */ + height?: number; + + /** The width of the image. */ + width?: number; + + /** The URL of the image. */ + url?: string; +}; + +type LinkMetadata = { + /** The URL of the link. */ + url?: string; + + /** A description of the link. */ + description?: string; + + /** The title of the link. */ + title?: string; + + /** The publisher of the link. */ + publisher?: string; + + /** The image associated with the link. */ + image?: ImageMetadata; + + /** The provider logo associated with the link. */ + logo?: ImageMetadata; +}; + type Person = { type?: string; style?: string; @@ -151,7 +182,7 @@ type ReportActionBase = { reportID?: string; /** Metadata of the link */ - linkMetadata?: string[]; + linkMetadata?: LinkMetadata[]; /** The current user's notification preference for this report's child */ childReportNotificationPreference?: NotificationPreference; From fd8965b26be001c4cdbdc6fc4d0e874d5144a029 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 5 Dec 2023 11:19:29 +0100 Subject: [PATCH 24/28] Add PersonalDetailsList type, add default values in Report.ts --- src/components/ArchivedReportFooter.tsx | 4 ++-- src/libs/GroupChatUtils.ts | 4 ++-- src/libs/PolicyUtils.ts | 3 +-- src/libs/ReportUtils.ts | 6 +++--- src/libs/actions/PersonalDetails.ts | 4 ++-- src/libs/actions/Report.ts | 8 ++++---- src/types/onyx/PersonalDetails.ts | 4 +++- src/types/onyx/index.ts | 3 ++- 8 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/components/ArchivedReportFooter.tsx b/src/components/ArchivedReportFooter.tsx index 3187bf3604e8..712ef6be769e 100644 --- a/src/components/ArchivedReportFooter.tsx +++ b/src/components/ArchivedReportFooter.tsx @@ -8,7 +8,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import useThemeStyles from '@styles/useThemeStyles'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetails, Report, ReportAction} from '@src/types/onyx'; +import type {PersonalDetailsList, Report, ReportAction} from '@src/types/onyx'; import Banner from './Banner'; type ArchivedReportFooterOnyxProps = { @@ -16,7 +16,7 @@ type ArchivedReportFooterOnyxProps = { reportClosedAction: OnyxEntry; /** Personal details of all users */ - personalDetails: OnyxEntry>; + personalDetails: OnyxEntry; }; type ArchivedReportFooterProps = ArchivedReportFooterOnyxProps & { diff --git a/src/libs/GroupChatUtils.ts b/src/libs/GroupChatUtils.ts index db64f6574824..862c50700c0c 100644 --- a/src/libs/GroupChatUtils.ts +++ b/src/libs/GroupChatUtils.ts @@ -1,10 +1,10 @@ import Onyx, {OnyxEntry} from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; -import {PersonalDetails, Report} from '@src/types/onyx'; +import {PersonalDetailsList, Report} from '@src/types/onyx'; import * as OptionsListUtils from './OptionsListUtils'; import * as ReportUtils from './ReportUtils'; -let allPersonalDetails: OnyxEntry> = {}; +let allPersonalDetails: OnyxEntry = {}; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => (allPersonalDetails = val), diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index 19129959d016..347e5b68e960 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -2,11 +2,10 @@ import Str from 'expensify-common/lib/str'; import {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import {PersonalDetails, Policy, PolicyMembers, PolicyTag, PolicyTags} from '@src/types/onyx'; +import {PersonalDetailsList, Policy, PolicyMembers, PolicyTag, PolicyTags} from '@src/types/onyx'; import {EmptyObject, isEmptyObject} from '@src/types/utils/EmptyObject'; type MemberEmailsToAccountIDs = Record; -type PersonalDetailsList = Record; type UnitRate = {rate: number}; /** diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 8ed16b43ecf4..4fdb335fca99 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -15,7 +15,7 @@ import CONST from '@src/CONST'; import {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import {Beta, Login, PersonalDetails, Policy, PolicyTags, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; +import {Beta, Login, PersonalDetails, PersonalDetailsList, Policy, PolicyTags, Report, ReportAction, Session, Transaction} from '@src/types/onyx'; import {Errors, Icon, PendingAction} from '@src/types/onyx/OnyxCommon'; import {ChangeLog, IOUMessage, OriginalMessageActionName, OriginalMessageCreated} from '@src/types/onyx/OriginalMessage'; import {NotificationPreference} from '@src/types/onyx/Report'; @@ -1404,7 +1404,7 @@ function getDisplayNameForParticipant(accountID?: number, shouldUseShortForm = f } function getDisplayNamesWithTooltips( - personalDetailsList: PersonalDetails[] | Record, + personalDetailsList: PersonalDetails[] | PersonalDetailsList, isMultipleParticipantReport: boolean, shouldFallbackToHidden = true, ): DisplayNameWithTooltips { @@ -1418,7 +1418,7 @@ function getDisplayNamesWithTooltips( const avatar = UserUtils.getDefaultAvatar(accountID); let pronouns = user.pronouns; - if (pronouns && pronouns.startsWith(CONST.PRONOUNS.PREFIX)) { + if (pronouns?.startsWith(CONST.PRONOUNS.PREFIX)) { const pronounTranslationKey = pronouns.replace(CONST.PRONOUNS.PREFIX, ''); pronouns = Localize.translateLocal(`pronouns.${pronounTranslationKey}` as TranslationPaths); } diff --git a/src/libs/actions/PersonalDetails.ts b/src/libs/actions/PersonalDetails.ts index 29d18d543a11..02b5f70db285 100644 --- a/src/libs/actions/PersonalDetails.ts +++ b/src/libs/actions/PersonalDetails.ts @@ -9,7 +9,7 @@ import * as UserUtils from '@libs/UserUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import {DateOfBirthForm, PersonalDetails, PrivatePersonalDetails} from '@src/types/onyx'; +import {DateOfBirthForm, PersonalDetails, PersonalDetailsList, PrivatePersonalDetails} from '@src/types/onyx'; import {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails'; type FirstAndLastName = { @@ -27,7 +27,7 @@ Onyx.connect({ }, }); -let allPersonalDetails: OnyxEntry> = null; +let allPersonalDetails: OnyxEntry = null; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => (allPersonalDetails = val), diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 7d9206d71680..580aa319847f 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -28,7 +28,7 @@ import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES, {Route} from '@src/ROUTES'; -import {PersonalDetails, ReportActionReactions, ReportUserIsTyping} from '@src/types/onyx'; +import {PersonalDetails, PersonalDetailsList, ReportActionReactions, ReportUserIsTyping} from '@src/types/onyx'; import {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import Report, {NotificationPreference, WriteCapability} from '@src/types/onyx/Report'; import ReportAction, {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; @@ -43,7 +43,7 @@ type ActionSubscriber = { callback: SubscriberCallback; }; -let currentUserAccountID: number; +let currentUserAccountID = -1; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (value) => { @@ -56,7 +56,7 @@ Onyx.connect({ }, }); -let preferredSkinTone: number; +let preferredSkinTone: number = CONST.EMOJI_DEFAULT_SKIN_TONE; Onyx.connect({ key: ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE, callback: (value) => { @@ -96,7 +96,7 @@ Onyx.connect({ }, }); -let allPersonalDetails: OnyxEntry> = {}; +let allPersonalDetails: OnyxEntry = {}; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (value) => { diff --git a/src/types/onyx/PersonalDetails.ts b/src/types/onyx/PersonalDetails.ts index af559eafd0a1..8f824272230e 100644 --- a/src/types/onyx/PersonalDetails.ts +++ b/src/types/onyx/PersonalDetails.ts @@ -76,6 +76,8 @@ type PersonalDetails = { payPalMeAddress?: string; }; +type PersonalDetailsList = Record; + export default PersonalDetails; -export type {Timezone, SelectedTimezone}; +export type {Timezone, SelectedTimezone, PersonalDetailsList}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index ff5938e4748e..e940c9d58c67 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -18,7 +18,7 @@ import Modal from './Modal'; import Network from './Network'; import {OnyxUpdateEvent, OnyxUpdatesFromServer} from './OnyxUpdatesFromServer'; import PersonalBankAccount from './PersonalBankAccount'; -import PersonalDetails from './PersonalDetails'; +import PersonalDetails, {PersonalDetailsList} from './PersonalDetails'; import PlaidData from './PlaidData'; import Policy from './Policy'; import PolicyCategory from './PolicyCategory'; @@ -78,6 +78,7 @@ export type { OnyxUpdatesFromServer, PersonalBankAccount, PersonalDetails, + PersonalDetailsList, PlaidData, Policy, PolicyCategory, From 74c9482009e94a774eac11619d2f1e7eabd0eb25 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 5 Dec 2023 11:53:33 +0100 Subject: [PATCH 25/28] Fix type errors around LocalNotification --- src/libs/Notification/LocalNotification/types.ts | 3 ++- src/libs/actions/Report.ts | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/Notification/LocalNotification/types.ts b/src/libs/Notification/LocalNotification/types.ts index 25db8b080e2b..fb18c6931560 100644 --- a/src/libs/Notification/LocalNotification/types.ts +++ b/src/libs/Notification/LocalNotification/types.ts @@ -1,4 +1,5 @@ import {ImageSourcePropType} from 'react-native'; +import {OnyxEntry} from 'react-native-onyx'; import {Report, ReportAction} from '@src/types/onyx'; type PushParams = { @@ -11,7 +12,7 @@ type PushParams = { }; type ReportCommentParams = { - report: Report; + report: OnyxEntry; reportAction: ReportAction; onClick: () => void; }; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index aca7b653782e..993f1f5bb6c9 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -18,6 +18,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import LocalNotification from '@libs/Notification/LocalNotification'; +import {ReportCommentParams} from '@libs/Notification/LocalNotification/types'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as Pusher from '@libs/Pusher/pusher'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; @@ -1810,9 +1811,9 @@ function showReportActionNotification(reportID: string, reportAction: ReportActi } Log.info('[LocalNotification] Creating notification'); - const report = allReports?.[reportID]; + const report = allReports?.[reportID] ?? null; - const notificationParams = { + const notificationParams: ReportCommentParams = { report, reportAction, onClick: () => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(reportID)), From 41471e922924a13756dbc7a7de218e04d8885167 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 7 Dec 2023 10:45:08 +0100 Subject: [PATCH 26/28] Clear the logic in leaveRoom util --- src/libs/actions/Report.ts | 76 ++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 15ba2d8bbb8b..43a991b56c06 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2034,54 +2034,50 @@ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = fal // If a workspace member is leaving a workspace room, they don't actually lose the room from Onyx. // Instead, their notification preference just gets set to "hidden". - const optimisticData: OnyxUpdate[] = []; - const successData: OnyxUpdate[] = []; - const failureData: OnyxUpdate[] = [ + const optimisticData: OnyxUpdate[] = [ + isWorkspaceMemberLeavingWorkspaceRoom + ? { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + }, + } + : { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + reportID, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, + statusNum: CONST.REPORT.STATUS.CLOSED, + chatType: report.chatType, + parentReportID: report.parentReportID, + parentReportActionID: report.parentReportActionID, + policyID: report.policyID, + type: report.type, + }, + }, + ]; + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: report, + value: isWorkspaceMemberLeavingWorkspaceRoom + ? {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN} + : Object.keys(report).reduce>((acc, key) => { + acc[key] = null; + return acc; + }, {}), }, ]; - if (isWorkspaceMemberLeavingWorkspaceRoom) { - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, - }, - }); - - successData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN}, - }); - } else { - optimisticData.push({ - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - reportID, - stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, - statusNum: CONST.REPORT.STATUS.CLOSED, - chatType: report.chatType, - parentReportID: report.parentReportID, - parentReportActionID: report.parentReportActionID, - policyID: report.policyID, - type: report.type, - }, - }); - successData.push({ + const failureData: OnyxUpdate[] = [ + { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: Object.keys(report).reduce>((acc, key) => { - acc[key] = null; - return acc; - }, {}), - }); - } + value: report, + }, + ]; type LeaveRoomParameters = { reportID: string; From 089dca8423282344ad32c49ece22fe2358f5470c Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Fri, 8 Dec 2023 17:45:29 +0100 Subject: [PATCH 27/28] Fix typecheck --- src/types/onyx/Report.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 89e30faaf025..362df191ba6e 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -146,6 +146,9 @@ type Report = { /** Total amount of money owed for IOU report */ iouReportAmount?: number; + /** Is this action pending? */ + pendingAction?: OnyxCommon.PendingAction; + /** Pending fields for the report */ pendingFields?: Record; From 33cb7fcae672409b279bf3e1bb9a1adaf7a62d48 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 11 Dec 2023 09:59:22 +0100 Subject: [PATCH 28/28] Fix typecheck --- src/libs/ReportUtils.ts | 1 - src/libs/SidebarUtils.ts | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 473362d36f8b..49dd69392b29 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -317,7 +317,6 @@ type DisplayNameWithTooltips = Array