From c03786570e7b93cfbee9a0220deecc2f1c6a9ac4 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Tue, 28 Nov 2023 16:07:32 +0100 Subject: [PATCH 01/24] Rename Policy file --- src/libs/actions/{Policy.js => Policy.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/libs/actions/{Policy.js => Policy.ts} (100%) diff --git a/src/libs/actions/Policy.js b/src/libs/actions/Policy.ts similarity index 100% rename from src/libs/actions/Policy.js rename to src/libs/actions/Policy.ts From ddaf7b98934418ac7e9bb1db33aaad9401020b01 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Thu, 30 Nov 2023 14:29:53 +0100 Subject: [PATCH 02/24] TS migration --- src/libs/PersonalDetailsUtils.js | 8 +- src/libs/ReportUtils.ts | 2 +- src/libs/actions/Policy.ts | 626 ++++++++++---------- src/pages/workspace/WorkspaceMembersPage.js | 2 + src/types/onyx/Policy.ts | 56 +- src/types/onyx/Report.ts | 7 +- 6 files changed, 369 insertions(+), 332 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index 560480dcec9d..ec7efa7ffa28 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -79,7 +79,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 + * @param {Array} accountIDs Array of user accountIDs * @returns {Array} - Array of logins according to passed accountIDs */ function getLoginsByAccountIDs(accountIDs) { @@ -101,7 +101,11 @@ function getLoginsByAccountIDs(accountIDs) { * * @param {Array} logins Array of user logins * @param {Array} accountIDs Array of user accountIDs - * @returns {Object} - Object with optimisticData, successData and failureData (object of personal details objects) + * @typedef {Object} OnyxData + * @property {Array} optimisticData + * @property {Array} successData + * @property {Array} failureData + * @returns {OnyxData} - Object with optimisticData, successData and failureData (object of personal details objects) */ function getNewPersonalDetailsOnyxData(logins, accountIDs) { const optimisticData = {}; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index d93661778b83..66207bcd741c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4383,4 +4383,4 @@ export { canEditWriteCapability, }; -export type {OptionData}; +export type {OptionData, OptimisticClosedReportAction}; diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index bcc371b3a609..200eb00f4822 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1,11 +1,10 @@ import {PUBLIC_DOMAINS} from 'expensify-common/lib/CONST'; import Str from 'expensify-common/lib/str'; import {escapeRegExp} from 'lodash'; -import filter from 'lodash/filter'; -import lodashGet from 'lodash/get'; +import lodashClone from 'lodash/clone'; import lodashUnion from 'lodash/union'; -import Onyx from 'react-native-onyx'; -import _ from 'underscore'; +import Onyx, {OnyxCollection, OnyxUpdate} from 'react-native-onyx'; +import {OnyxEntry} from 'react-native-onyx/lib/types'; import * as API from '@libs/API'; import DateUtils from '@libs/DateUtils'; import * as ErrorUtils from '@libs/ErrorUtils'; @@ -15,11 +14,16 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; +import {OptimisticClosedReportAction} from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import {Network, PersonalDetails, Policy, PolicyMember, RecentlyUsedCategories, ReimbursementAccount, Report, ReportAction, Transaction} from '@src/types/onyx'; +import {Errors} from '@src/types/onyx/OnyxCommon'; +import {CustomUnit, NewCustomUnit} from '@src/types/onyx/Policy'; +import {isNotEmptyObject} from '@src/types/utils/EmptyObject'; -const allPolicies = {}; +const allPolicies: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY, callback: (val, key) => { @@ -34,7 +38,11 @@ Onyx.connect({ const policyReports = ReportUtils.getAllPolicyReports(policyID); const cleanUpMergeQueries = {}; const cleanUpSetQueries = {}; - _.each(policyReports, ({reportID}) => { + policyReports.forEach((policyReport) => { + if (!policyReport) { + return; + } + const {reportID} = policyReport; cleanUpMergeQueries[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`] = {hasDraft: false}; cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${reportID}`] = null; cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; @@ -49,14 +57,19 @@ Onyx.connect({ }, }); -let allPolicyMembers; +let allPolicyMembers: OnyxCollection; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY_MEMBERS, waitForCollectionCallback: true, - callback: (val) => (allPolicyMembers = val), + // callback: (val) => (allPolicyMembers = val), + callback: (val) => { + // console.log('POLICYMEMBERS', val); + // console.log('TYPEPOLICYMEMBERS', typeof Object.keys(val)[0]); + allPolicyMembers = val; + }, }); -let lastAccessedWorkspacePolicyID = null; +let lastAccessedWorkspacePolicyID: OnyxEntry = null; Onyx.connect({ key: ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID, callback: (value) => (lastAccessedWorkspacePolicyID = value), @@ -67,67 +80,63 @@ let sessionAccountID = 0; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { - sessionEmail = lodashGet(val, 'email', ''); - sessionAccountID = lodashGet(val, 'accountID', 0); + sessionEmail = val?.email ?? ''; + sessionAccountID = val?.accountID ?? 0; }, }); -let allPersonalDetails; +let allPersonalDetails: OnyxEntry>; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS_LIST, callback: (val) => (allPersonalDetails = val), }); -let reimbursementAccount; +let reimbursementAccount: OnyxEntry; Onyx.connect({ key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, callback: (val) => (reimbursementAccount = val), }); -let allRecentlyUsedCategories = {}; +let allRecentlyUsedCategories: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES, waitForCollectionCallback: true, callback: (val) => (allRecentlyUsedCategories = val), }); -let networkStatus = {}; +let networkStatus: OnyxEntry = {}; Onyx.connect({ key: ONYXKEYS.NETWORK, - waitForCollectionCallback: true, callback: (val) => (networkStatus = val), }); /** * Stores in Onyx the policy ID of the last workspace that was accessed by the user - * @param {String|null} policyID */ -function updateLastAccessedWorkspace(policyID) { +function updateLastAccessedWorkspace(policyID: OnyxEntry) { Onyx.set(ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID, policyID); } /** * Check if the user has any active free policies (aka workspaces) - * - * @param {Array} policies - * @returns {Boolean} + */ -function hasActiveFreePolicy(policies) { - const adminFreePolicies = _.filter(policies, (policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); +function hasActiveFreePolicy(policies: Array> | Record>): boolean { + const adminFreePolicies = Object.values(policies).filter((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); if (adminFreePolicies.length === 0) { return false; } - if (_.some(adminFreePolicies, (policy) => !policy.pendingAction)) { + if (adminFreePolicies.some((policy) => !policy?.pendingAction)) { return true; } - if (_.some(adminFreePolicies, (policy) => policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD)) { + if (adminFreePolicies.some((policy) => policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD)) { return true; } - if (_.some(adminFreePolicies, (policy) => policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)) { + if (adminFreePolicies.some((policy) => policy?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)) { return false; } @@ -138,14 +147,14 @@ function hasActiveFreePolicy(policies) { /** * Delete the workspace - * - * @param {String} policyID - * @param {Array} reports - * @param {String} policyName */ -function deleteWorkspace(policyID, reports, policyName) { - const filteredPolicies = filter(allPolicies, (policy) => policy.id !== policyID); - const optimisticData = [ +function deleteWorkspace(policyID: string, reports: Report[], policyName: string) { + if (!allPolicies) { + return; + } + + const filteredPolicies = Object.values(allPolicies).filter((policy) => policy?.id !== policyID); + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -154,32 +163,32 @@ function deleteWorkspace(policyID, reports, policyName) { errors: null, }, }, - ..._.map(reports, ({reportID}) => ({ + ...reports.map(({reportID}) => ({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS.CLOSED, hasDraft: false, - oldPolicyName: allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`].name, + oldPolicyName: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name, }, })), - ..._.map(reports, ({reportID}) => ({ + ...reports.map(({reportID}) => ({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`, value: null, })), // Add closed actions to all chat reports linked to this policy - ..._.map(reports, ({reportID, ownerAccountID}) => { + ...reports.map(({reportID, ownerAccountID}) => { // Announce & admin chats have FAKE owners, but workspace chats w/ users do have owners. - let emailClosingReport = CONST.POLICY.OWNER_EMAIL_FAKE; - if (ownerAccountID !== CONST.POLICY.OWNER_ACCOUNT_ID_FAKE) { - emailClosingReport = lodashGet(allPersonalDetails, [ownerAccountID, 'login'], ''); + let emailClosingReport: string = CONST.POLICY.OWNER_EMAIL_FAKE; + if (!!ownerAccountID && ownerAccountID !== CONST.POLICY.OWNER_ACCOUNT_ID_FAKE) { + emailClosingReport = allPersonalDetails?.[ownerAccountID]?.login ?? ''; } const optimisticClosedReportAction = ReportUtils.buildOptimisticClosedReportAction(emailClosingReport, policyName, CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED); - const optimisticReportActions = {}; + const optimisticReportActions: Record = {}; optimisticReportActions[optimisticClosedReportAction.reportActionID] = optimisticClosedReportAction; return { onyxMethod: Onyx.METHOD.MERGE, @@ -202,8 +211,8 @@ function deleteWorkspace(policyID, reports, policyName) { ]; // Restore the old report stateNum and statusNum - const failureData = [ - ..._.map(reports, ({reportID, stateNum, statusNum, hasDraft, oldPolicyName}) => ({ + const failureData: OnyxUpdate[] = [ + ...reports.map(({reportID, stateNum, statusNum, hasDraft, oldPolicyName}) => ({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { @@ -217,14 +226,14 @@ function deleteWorkspace(policyID, reports, policyName) { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, value: { - errors: lodashGet(reimbursementAccount, 'errors', null), + errors: reimbursementAccount?.errors ?? null, }, }, ]; // We don't need success data since the push notification will update // the onyxData for all connected clients. - const successData = []; + const successData: OnyxUpdate[] = []; API.write('DeleteWorkspace', {policyID}, {optimisticData, successData, failureData}); // Reset the lastAccessedWorkspacePolicyID @@ -235,84 +244,87 @@ function deleteWorkspace(policyID, reports, policyName) { /** * Is the user an admin of a free policy (aka workspace)? - * - * @param {Record} [policies] - * @returns {Boolean} */ -function isAdminOfFreePolicy(policies) { - return _.some(policies, (policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); +function isAdminOfFreePolicy(policies: Record): boolean { + return Object.values(policies).some((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); } +type AnnounceRoomMembers = { + onyxOptimisticData: OnyxUpdate[]; + onyxFailureData: OnyxUpdate[]; +}; + /** - * Build optimistic data for adding members to the announce room - * @param {String} policyID - * @param {Array} accountIDs - * @returns {Object} + * Build optimistic data for adding members to the announcement room */ -function buildAnnounceRoomMembersOnyxData(policyID, accountIDs) { +function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[]) { const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); - const announceRoomMembers = { + const announceRoomMembers: AnnounceRoomMembers = { onyxOptimisticData: [], onyxFailureData: [], }; - announceRoomMembers.onyxOptimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, - value: { - participantAccountIDs: [...announceReport.participantAccountIDs, ...accountIDs], - }, - }); + if (announceReport?.participantAccountIDs) { + announceRoomMembers.onyxOptimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport?.reportID}`, + value: { + participantAccountIDs: [...announceReport.participantAccountIDs, ...accountIDs], + }, + }); + } announceRoomMembers.onyxFailureData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport?.reportID}`, value: { - participantAccountIDs: announceReport.participantAccountIDs, + participantAccountIDs: announceReport?.participantAccountIDs, }, }); return announceRoomMembers; } +type OptimisticAnnounceRoomMembers = { + onyxOptimisticData: OnyxUpdate[]; + onyxFailureData: OnyxUpdate[]; +}; + /** - * Build optimistic data for removing users from the announce room - * @param {String} policyID - * @param {Array} accountIDs - * @returns {Object} + * Build optimistic data for removing users from the announcement room */ -function removeOptimisticAnnounceRoomMembers(policyID, accountIDs) { +function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: string[]) { const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); - const announceRoomMembers = { + const announceRoomMembers: OptimisticAnnounceRoomMembers = { onyxOptimisticData: [], onyxFailureData: [], }; - const remainUsers = _.difference(announceReport.participantAccountIDs, accountIDs); - announceRoomMembers.onyxOptimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, - value: { - participantAccountIDs: [...remainUsers], - }, - }); + if (announceReport?.participantAccountIDs) { + const remainUsers = announceReport.participantAccountIDs.filter((e) => !accountIDs.includes(e)); + announceRoomMembers.onyxOptimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, + value: { + participantAccountIDs: [...remainUsers], + }, + }); + + announceRoomMembers.onyxFailureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, + value: { + participantAccountIDs: announceReport.participantAccountIDs, + }, + }); + } - announceRoomMembers.onyxFailureData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, - value: { - participantAccountIDs: announceReport.participantAccountIDs, - }, - }); return announceRoomMembers; } /** * Remove the passed members from the policy employeeList - * - * @param {Array} accountIDs - * @param {String} policyID */ -function removeMembers(accountIDs, policyID) { +function removeMembers(accountIDs: string[], policyID: string) { // In case user selects only themselves (admin), their email will be filtered out and the members // array passed will be empty, prevent the function from proceeding in that case as there is no one to remove if (accountIDs.length === 0) { @@ -322,21 +334,28 @@ function removeMembers(accountIDs, policyID) { const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`; const policy = ReportUtils.getPolicy(policyID); const workspaceChats = ReportUtils.getWorkspaceChats(policyID, accountIDs); - const optimisticClosedReportActions = _.map(workspaceChats, () => - ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy.name, CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY), - ); + const optimisticClosedReportActions = workspaceChats.map(() => ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy.name, CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY)); const announceRoomMembers = removeOptimisticAnnounceRoomMembers(policyID, accountIDs); + const optimisticMembersState: Record = {}; + const successMembersState: Record = {}; + const failureMembersState: Record = {}; + accountIDs.forEach((accountID) => { + optimisticMembersState[accountID] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; + successMembersState[accountID] = null; + failureMembersState[accountID] = {errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove')}; + }); + const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, key: membersListKey, - value: _.object(accountIDs, Array(accountIDs.length).fill({pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE})), + value: optimisticMembersState, }, - ..._.map(workspaceChats, (report) => ({ + ...workspaceChats.map((report) => ({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, value: { statusNum: CONST.REPORT.STATUS.CLOSED, stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, @@ -344,9 +363,9 @@ function removeMembers(accountIDs, policyID) { hasDraft: false, }, })), - ..._.map(optimisticClosedReportActions, (reportAction, index) => ({ + ...optimisticClosedReportActions.map((reportAction, index) => ({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats[index].reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats?.[index]?.reportID}`, value: {[reportAction.reportActionID]: reportAction}, })), ...announceRoomMembers.onyxOptimisticData, @@ -354,15 +373,20 @@ function removeMembers(accountIDs, policyID) { // If the policy has primaryLoginsInvited, then it displays informative messages on the members page about which primary logins were added by secondary logins. // If we delete all these logins then we should clear the informative messages since they are no longer relevant. - if (!_.isEmpty(policy.primaryLoginsInvited)) { + if (isNotEmptyObject(policy?.primaryLoginsInvited ?? {})) { // Take the current policy members and remove them optimistically - const policyMemberAccountIDs = _.map(allPolicyMembers[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`], (value, key) => Number(key)); - const remainingMemberAccountIDs = _.difference(policyMemberAccountIDs, accountIDs); - const remainingLogins = PersonalDetailsUtils.getLoginsByAccountIDs(remainingMemberAccountIDs); - const invitedPrimaryToSecondaryLogins = _.invert(policy.primaryLoginsInvited); + // console.log('POLICYMEMBERS', allPolicyMembers); + const policyMemberAccountIDs = Object.keys(allPolicyMembers?.[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`] ?? {}); + const remainingMemberAccountIDs = policyMemberAccountIDs.filter((e) => !accountIDs.includes(e)); + const remainingLogins: string[] = PersonalDetailsUtils.getLoginsByAccountIDs(remainingMemberAccountIDs); + const invitedPrimaryToSecondaryLogins: Record = {}; + + if (policy.primaryLoginsInvited) { + Object.keys(policy.primaryLoginsInvited).forEach((key) => (invitedPrimaryToSecondaryLogins[policy.primaryLoginsInvited?.[key] ?? ''] = key)); + } // Then, if no remaining members exist that were invited by a secondary login, clear the informative messages - if (!_.some(remainingLogins, (remainingLogin) => Boolean(invitedPrimaryToSecondaryLogins[remainingLogin]))) { + if (!remainingLogins.some((remainingLogin) => Boolean(invitedPrimaryToSecondaryLogins[remainingLogin]))) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -377,16 +401,18 @@ function removeMembers(accountIDs, policyID) { { onyxMethod: Onyx.METHOD.MERGE, key: membersListKey, - value: _.object(accountIDs, Array(accountIDs.length).fill(null)), + value: successMembersState, }, ]; + + const filteredWorkspaceChats = workspaceChats.filter((e) => e !== null) as Report[]; const failureData = [ { onyxMethod: Onyx.METHOD.MERGE, key: membersListKey, - value: _.object(accountIDs, Array(accountIDs.length).fill({errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove')})), + value: failureMembersState, }, - ..._.map(workspaceChats, ({reportID, stateNum, statusNum, hasDraft, oldPolicyName = null}) => ({ + ...filteredWorkspaceChats.map(({reportID, stateNum, statusNum, hasDraft, oldPolicyName = null}) => ({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { @@ -396,9 +422,9 @@ function removeMembers(accountIDs, policyID) { oldPolicyName, }, })), - ..._.map(optimisticClosedReportActions, (reportAction, index) => ({ + ...optimisticClosedReportActions.map((reportAction, index) => ({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats[index].reportID}`, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats?.[index]?.reportID}`, value: {[reportAction.reportActionID]: null}, })), ...announceRoomMembers.onyxFailureData, @@ -406,30 +432,41 @@ function removeMembers(accountIDs, policyID) { API.write( 'DeleteMembersFromWorkspace', { - emailList: _.map(accountIDs, (accountID) => allPersonalDetails[accountID].login).join(','), + emailList: accountIDs.map((accountID) => allPersonalDetails?.[accountID].login).join(','), policyID, }, {optimisticData, successData, failureData}, ); } +type WorkspaceMembersChats = { + onyxSuccessData: OnyxUpdate[]; + onyxOptimisticData: OnyxUpdate[]; + onyxFailureData: OnyxUpdate[]; + reportCreationData: Record< + string, + { + reportID: string; + reportActionID?: string; + } + >; +}; + /** * Optimistically create a chat for each member of the workspace, creates both optimistic and success data for onyx. * - * @param {String} policyID - * @param {Object} invitedEmailsToAccountIDs - * @param {Boolean} hasOutstandingChildRequest - * @returns {Object} - object with onyxSuccessData, onyxOptimisticData, and optimisticReportIDs (map login to reportID) + * @returns - object with onyxSuccessData, onyxOptimisticData, and optimisticReportIDs (map login to reportID) */ -function createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs, hasOutstandingChildRequest = false) { - const workspaceMembersChats = { +function createPolicyExpenseChats(policyID: string, invitedEmailsToAccountIDs: Record, hasOutstandingChildRequest = false) { + const workspaceMembersChats: WorkspaceMembersChats = { onyxSuccessData: [], onyxOptimisticData: [], onyxFailureData: [], reportCreationData: {}, }; - _.each(invitedEmailsToAccountIDs, (accountID, email) => { + Object.keys(invitedEmailsToAccountIDs).forEach((email) => { + const accountID = invitedEmailsToAccountIDs[email]; const cleanAccountID = Number(accountID); const login = OptionsListUtils.addSMSDomainIfPhoneNumber(email); @@ -473,7 +510,7 @@ function createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs, hasOutsta workspaceMembersChats.onyxOptimisticData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticReport.reportID}`, - value: {[optimisticCreatedAction.reportActionID]: optimisticCreatedAction}, + value: {[optimisticCreatedAction.reportActionID]: optimisticCreatedAction as ReportAction}, }); workspaceMembersChats.onyxSuccessData.push({ @@ -508,15 +545,10 @@ function createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs, hasOutsta /** * Adds members to the specified workspace/policyID - * - * @param {Object} invitedEmailsToAccountIDs - * @param {String} welcomeNote - * @param {String} policyID */ -function addMembersToWorkspace(invitedEmailsToAccountIDs, welcomeNote, policyID) { - const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`; - const logins = _.map(_.keys(invitedEmailsToAccountIDs), (memberLogin) => OptionsListUtils.addSMSDomainIfPhoneNumber(memberLogin)); - const accountIDs = _.values(invitedEmailsToAccountIDs); +function addMembersToWorkspace(invitedEmailsToAccountIDs: Record, welcomeNote: string, policyID: string) { + const logins = Object.keys(invitedEmailsToAccountIDs).map((memberLogin) => OptionsListUtils.addSMSDomainIfPhoneNumber(memberLogin)); + const accountIDs = Object.values(invitedEmailsToAccountIDs); const newPersonalDetailsOnyxData = PersonalDetailsUtils.getNewPersonalDetailsOnyxData(logins, accountIDs); const announceRoomMembers = buildAnnounceRoomMembersOnyxData(policyID, accountIDs); @@ -524,76 +556,80 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs, welcomeNote, policyID) // create onyx data for policy expense chats for each new member const membersChats = createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs); - const optimisticData = [ + const optimisticMembersState: Record = {}; + const failureMembersState: Record = {}; + accountIDs.forEach((accountID) => { + optimisticMembersState[accountID] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}; + failureMembersState[accountID] = { + errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericAdd'), + }; + }); + + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: membersListKey, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, // Convert to object with each key containing {pendingAction: ‘add’} - value: _.object(accountIDs, Array(accountIDs.length).fill({pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD})), + value: optimisticMembersState, }, - ...newPersonalDetailsOnyxData.optimisticData, + ...(newPersonalDetailsOnyxData.optimisticData as OnyxUpdate[]), ...membersChats.onyxOptimisticData, ...announceRoomMembers.onyxOptimisticData, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: membersListKey, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, // Convert to object with each key clearing pendingAction, when it is an existing account. // Remove the object, when it is a newly created account. - value: _.reduce( - accountIDs, - (accountIDsWithClearedPendingAction, accountID) => { - let value = null; - const accountAlreadyExists = !_.isEmpty(allPersonalDetails[accountID]); + value: accountIDs.reduce((accountIDsWithClearedPendingAction, accountID) => { + let value = null; + const accountAlreadyExists = isNotEmptyObject(allPersonalDetails?.[accountID]); - if (accountAlreadyExists) { - value = {pendingAction: null, errors: null}; - } + if (accountAlreadyExists) { + value = {pendingAction: null, errors: null}; + } - // eslint-disable-next-line no-param-reassign - accountIDsWithClearedPendingAction[accountID] = value; - - return accountIDsWithClearedPendingAction; - }, - {}, - ), + return {...accountIDsWithClearedPendingAction, [accountID]: value}; + }, {}), }, - ...newPersonalDetailsOnyxData.successData, + ...(newPersonalDetailsOnyxData.successData as OnyxUpdate[]), ...membersChats.onyxSuccessData, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: membersListKey, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, // Convert to object with each key containing the error. We don’t // need to remove the members since that is handled by onClose of OfflineWithFeedback. - value: _.object( - accountIDs, - Array(accountIDs.length).fill({ - errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericAdd'), - }), - ), + value: failureMembersState, }, - ...newPersonalDetailsOnyxData.failureData, + ...(newPersonalDetailsOnyxData.failureData as OnyxUpdate[]), ...membersChats.onyxFailureData, ...announceRoomMembers.onyxFailureData, ]; - const params = { - employees: JSON.stringify(_.map(logins, (login) => ({email: login}))), + type AddMembersToWorkspaceParams = { + employees: string; + welcomeNote: string; + policyID: string; + reportCreationData?: string; + }; + + const params: AddMembersToWorkspaceParams = { + employees: JSON.stringify(logins.map((login) => ({email: login}))), // Do not escape HTML special chars for welcomeNote as this will be handled in the backend. // See https://github.com/Expensify/App/issues/20081 for more details. welcomeNote, policyID, }; - if (!_.isEmpty(membersChats.reportCreationData)) { + if (isNotEmptyObject(membersChats.reportCreationData)) { params.reportCreationData = JSON.stringify(membersChats.reportCreationData); } API.write('AddMembersToWorkspace', params, {optimisticData, successData, failureData}); @@ -601,12 +637,9 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs, welcomeNote, policyID) /** * Updates a workspace avatar image - * - * @param {String} policyID - * @param {File|Object} file */ -function updateWorkspaceAvatar(policyID, file) { - const optimisticData = [ +function updateWorkspaceAvatar(policyID: string, file: File) { + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -622,7 +655,7 @@ function updateWorkspaceAvatar(policyID, file) { }, }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -633,12 +666,12 @@ function updateWorkspaceAvatar(policyID, file) { }, }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - avatar: allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`].avatar, + avatar: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.avatar, pendingFields: { avatar: null, }, @@ -651,10 +684,9 @@ function updateWorkspaceAvatar(policyID, file) { /** * Deletes the avatar image for the workspace - * @param {String} policyID */ -function deleteWorkspaceAvatar(policyID) { - const optimisticData = [ +function deleteWorkspaceAvatar(policyID: string) { + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -669,7 +701,7 @@ function deleteWorkspaceAvatar(policyID) { }, }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -680,7 +712,7 @@ function deleteWorkspaceAvatar(policyID) { }, }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -699,9 +731,8 @@ function deleteWorkspaceAvatar(policyID) { /** * Clear error and pending fields for the workspace avatar - * @param {String} policyID */ -function clearAvatarErrors(policyID) { +function clearAvatarErrors(policyID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { errorFields: { avatar: null, @@ -715,22 +746,28 @@ function clearAvatarErrors(policyID) { /** * Optimistically update the general settings. Set the general settings as pending until the response succeeds. * If the response fails set a general error message. Clear the error message when updating. - * - * @param {String} policyID - * @param {String} name - * @param {String} currency */ -function updateGeneralSettings(policyID, name, currency) { - const policy = allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const distanceUnit = _.find(_.values(policy.customUnits), (unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); - const distanceRate = _.find(_.values(distanceUnit ? distanceUnit.rates : {}), (rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); - const optimisticData = [ +function updateGeneralSettings(policyID: string, name: string, currency: string) { + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; + + if (!policy) { + return; + } + + const distanceUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); + const distanceRate = Object.values(distanceUnit ? distanceUnit.rates : {}).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); + + if (!distanceUnit?.customUnitID || !distanceRate?.customUnitRateID) { + return; + } + + const optimisticData: OnyxUpdate[] = [ { // We use SET because it's faster than merge and avoids a race condition when setting the currency and navigating the user to the Bank account page in confirmCurrencyChangeAndHideModal onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - ...allPolicies[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`], + ...((allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {}) as Policy), pendingFields: { generalSettings: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, @@ -742,13 +779,13 @@ function updateGeneralSettings(policyID, name, currency) { }, name, outputCurrency: currency, - ...(distanceUnit + ...((distanceUnit ? { customUnits: { [distanceUnit.customUnitID]: { ...distanceUnit, rates: { - [distanceRate.customUnitRateID]: { + [distanceRate?.customUnitRateID]: { ...distanceRate, currency, }, @@ -756,11 +793,11 @@ function updateGeneralSettings(policyID, name, currency) { }, }, } - : {}), + : {}) as Record), }, }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -771,7 +808,7 @@ function updateGeneralSettings(policyID, name, currency) { }, }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -805,9 +842,9 @@ function updateGeneralSettings(policyID, name, currency) { } /** - * @param {String} policyID The id of the workspace / policy + * @param policyID The id of the workspace / policy */ -function clearWorkspaceGeneralSettingsErrors(policyID) { +function clearWorkspaceGeneralSettingsErrors(policyID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { errorFields: { generalSettings: null, @@ -815,12 +852,8 @@ function clearWorkspaceGeneralSettingsErrors(policyID) { }); } -/** - * @param {String} policyID - * @param {Object} errors - */ -function setWorkspaceErrors(policyID, errors) { - if (!allPolicies[policyID]) { +function setWorkspaceErrors(policyID: string, errors: Errors) { + if (!allPolicies?.[policyID]) { return; } @@ -828,12 +861,7 @@ function setWorkspaceErrors(policyID, errors) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {errors}); } -/** - * @param {String} policyID - * @param {String} customUnitID - * @param {String} customUnitRateID - */ -function clearCustomUnitErrors(policyID, customUnitID, customUnitRateID) { +function clearCustomUnitErrors(policyID: string, customUnitID: string, customUnitRateID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { customUnits: { [customUnitID]: { @@ -850,25 +878,21 @@ function clearCustomUnitErrors(policyID, customUnitID, customUnitRateID) { }); } -/** - * @param {String} policyID - */ -function hideWorkspaceAlertMessage(policyID) { - if (!allPolicies[policyID]) { +function hideWorkspaceAlertMessage(policyID: string) { + if (!allPolicies?.[policyID]) { return; } Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {alertMessage: ''}); } -/** - * @param {String} policyID - * @param {Object} currentCustomUnit - * @param {Object} newCustomUnit - * @param {Number} lastModified - */ -function updateWorkspaceCustomUnitAndRate(policyID, currentCustomUnit, newCustomUnit, lastModified) { - const optimisticData = [ +function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: NewCustomUnit, lastModified: number) { + // console.log('CUSTOMRATES', newCustomUnit, currentCustomUnit); + if (!currentCustomUnit.customUnitID || !newCustomUnit?.customUnitID || !newCustomUnit.rates?.customUnitRateID || !currentCustomUnit.rates?.customUnitRateID) { + return; + } + + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -890,7 +914,7 @@ function updateWorkspaceCustomUnitAndRate(policyID, currentCustomUnit, newCustom }, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -910,7 +934,7 @@ function updateWorkspaceCustomUnitAndRate(policyID, currentCustomUnit, newCustom }, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -930,13 +954,13 @@ function updateWorkspaceCustomUnitAndRate(policyID, currentCustomUnit, newCustom }, ]; - const newCustomUnitParam = _.clone(newCustomUnit); - newCustomUnitParam.rates = _.omit(newCustomUnitParam.rates, ['pendingAction', 'errors']); + const newCustomUnitParam = lodashClone(newCustomUnit); + newCustomUnitParam.rates = {...newCustomUnitParam.rates, pendingAction: undefined, errors: undefined}; API.write( 'UpdateWorkspaceCustomUnitAndRate', { policyID, - ...(!networkStatus.isOffline && {lastModified}), + ...(!networkStatus?.isOffline && {lastModified}), customUnit: JSON.stringify(newCustomUnitParam), customUnitRate: JSON.stringify(newCustomUnitParam.rates), }, @@ -946,11 +970,8 @@ function updateWorkspaceCustomUnitAndRate(policyID, currentCustomUnit, newCustom /** * Removes an error after trying to delete a member - * - * @param {String} policyID - * @param {Number} accountID */ -function clearDeleteMemberError(policyID, accountID) { +function clearDeleteMemberError(policyID: string, accountID: number) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, { [accountID]: { pendingAction: null, @@ -961,11 +982,8 @@ function clearDeleteMemberError(policyID, accountID) { /** * Removes an error after trying to add a member - * - * @param {String} policyID - * @param {Number} accountID */ -function clearAddMemberError(policyID, accountID) { +function clearAddMemberError(policyID: string, accountID: number) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, { [accountID]: null, }); @@ -976,10 +994,8 @@ function clearAddMemberError(policyID, accountID) { /** * Removes an error after trying to delete a workspace - * - * @param {String} policyID */ -function clearDeleteWorkspaceError(policyID) { +function clearDeleteWorkspaceError(policyID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { pendingAction: null, errors: null, @@ -988,19 +1004,16 @@ function clearDeleteWorkspaceError(policyID) { /** * Removes the workspace after failure to create. - * - * @param {String} policyID */ -function removeWorkspace(policyID) { +function removeWorkspace(policyID: string) { Onyx.set(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, null); } /** * Generate a policy name based on an email and policy list. - * @param {String} [email] the email to base the workspace name on. If not passed, will use the logged in user's email instead - * @returns {String} + * @param [email] the email to base the workspace name on. If not passed, will use the logged-in user's email instead */ -function generateDefaultWorkspaceName(email = '') { +function generateDefaultWorkspaceName(email = ''): string { const emailParts = email ? email.split('@') : sessionEmail.split('@'); let defaultWorkspaceName = ''; if (!emailParts || emailParts.length !== 2) { @@ -1009,7 +1022,7 @@ function generateDefaultWorkspaceName(email = '') { const username = emailParts[0]; const domain = emailParts[1]; - if (_.includes(PUBLIC_DOMAINS, domain.toLowerCase())) { + if (PUBLIC_DOMAINS.includes(domain.toLowerCase() as (typeof PUBLIC_DOMAINS)[number])) { defaultWorkspaceName = `${Str.UCFirst(username)}'s Workspace`; } else { defaultWorkspaceName = `${Str.UCFirst(domain.split('.')[0])}'s Workspace`; @@ -1019,43 +1032,37 @@ function generateDefaultWorkspaceName(email = '') { defaultWorkspaceName = 'My Group Workspace'; } - if (allPolicies.length === 0) { + if (!isNotEmptyObject(allPolicies ?? {})) { return defaultWorkspaceName; } // find default named workspaces and increment the last number const numberRegEx = new RegExp(`${escapeRegExp(defaultWorkspaceName)} ?(\\d*)`, 'i'); - const lastWorkspaceNumber = _.chain(allPolicies) - .filter((policy) => policy.name && numberRegEx.test(policy.name)) - .map((policy) => parseInt(numberRegEx.exec(policy.name)[1] || 1, 10)) // parse the number at the end - .max() - .value(); + const parsedWorkspaceNumbers = Object.values(allPolicies ?? {}) + .filter((policy) => policy?.name && numberRegEx.test(policy.name)) + .map((policy) => parseInt(numberRegEx.exec(policy?.name ?? '')?.[1] ?? '1', 10)); // parse the number at the end + const lastWorkspaceNumber = Math.max(...parsedWorkspaceNumbers); return lastWorkspaceNumber !== -Infinity ? `${defaultWorkspaceName} ${lastWorkspaceNumber + 1}` : defaultWorkspaceName; } /** * Returns a client generated 16 character hexadecimal value for the policyID - * @returns {String} */ -function generatePolicyID() { +function generatePolicyID(): string { return NumberUtils.generateHexadecimalValue(16); } /** * Returns a client generated 13 character hexadecimal value for a custom unit ID - * @returns {String} */ -function generateCustomUnitID() { +function generateCustomUnitID(): string { return NumberUtils.generateHexadecimalValue(13); } -/** - * @returns {Object} - */ function buildOptimisticCustomUnits() { const customUnitID = generateCustomUnitID(); const customUnitRateID = generateCustomUnitID(); - const customUnits = { + const customUnits: Record = { [customUnitID]: { customUnitID, name: CONST.CUSTOM_UNITS.NAME_DISTANCE, @@ -1082,16 +1089,16 @@ function buildOptimisticCustomUnits() { /** * Optimistically creates a Policy Draft for a new workspace * - * @param {String} [policyOwnerEmail] Optional, the email of the account to make the owner of the policy - * @param {String} [policyName] Optional, custom policy name we will use for created workspace - * @param {String} [policyID] Optional, custom policy id we will use for created workspace - * @param {Boolean} [makeMeAdmin] Optional, leave the calling account as an admin on the policy + * @param [policyOwnerEmail] Optional, the email of the account to make the owner of the policy + * @param [policyName] Optional, custom policy name we will use for created workspace + * @param [policyID] Optional, custom policy id we will use for created workspace + * @param [makeMeAdmin] Optional, leave the calling account as an admin on the policy */ function createDraftInitialWorkspace(policyOwnerEmail = '', policyName = '', policyID = generatePolicyID(), makeMeAdmin = false) { const workspaceName = policyName || generateDefaultWorkspaceName(policyOwnerEmail); const {customUnits} = buildOptimisticCustomUnits(); - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, @@ -1102,7 +1109,7 @@ function createDraftInitialWorkspace(policyOwnerEmail = '', policyName = '', pol role: CONST.POLICY.ROLE.ADMIN, owner: sessionEmail, isPolicyExpenseChatEnabled: true, - outputCurrency: lodashGet(allPersonalDetails, [sessionAccountID, 'localCurrencyCode'], CONST.CURRENCY.USD), + outputCurrency: allPersonalDetails?.[sessionAccountID]?.localCurrencyCode ?? CONST.CURRENCY.USD, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, customUnits, makeMeAdmin, @@ -1126,13 +1133,12 @@ function createDraftInitialWorkspace(policyOwnerEmail = '', policyName = '', pol /** * Optimistically creates a new workspace and default workspace chats * - * @param {String} [policyOwnerEmail] Optional, the email of the account to make the owner of the policy - * @param {Boolean} [makeMeAdmin] Optional, leave the calling account as an admin on the policy - * @param {String} [policyName] Optional, custom policy name we will use for created workspace - * @param {String} [policyID] Optional, custom policy id we will use for created workspace - * @returns {String} + * @param [policyOwnerEmail] Optional, the email of the account to make the owner of the policy + * @param [makeMeAdmin] Optional, leave the calling account as an admin on the policy + * @param [policyName] Optional, custom policy name we will use for created workspace + * @param [policyID] Optional, custom policy id we will use for created workspace */ -function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName = '', policyID = generatePolicyID()) { +function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName = '', policyID = generatePolicyID()): string { const workspaceName = policyName || generateDefaultWorkspaceName(policyOwnerEmail); const {customUnits, customUnitID, customUnitRateID} = buildOptimisticCustomUnits(); @@ -1181,7 +1187,7 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName role: CONST.POLICY.ROLE.ADMIN, owner: sessionEmail, isPolicyExpenseChatEnabled: true, - outputCurrency: lodashGet(allPersonalDetails, [sessionAccountID, 'localCurrencyCode'], CONST.CURRENCY.USD), + outputCurrency: allPersonalDetails?.[sessionAccountID]?.localCurrencyCode ?? CONST.CURRENCY.USD, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, customUnits, }, @@ -1272,7 +1278,7 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, value: { - [_.keys(announceChatData)[0]]: { + [Object.keys(announceChatData)[0]]: { pendingAction: null, }, }, @@ -1291,7 +1297,7 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, value: { - [_.keys(adminsChatData)[0]]: { + [Object.keys(adminsChatData)[0]]: { pendingAction: null, }, }, @@ -1310,7 +1316,7 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, value: { - [_.keys(expenseChatData)[0]]: { + [Object.keys(expenseChatData)[0]]: { pendingAction: null, }, }, @@ -1359,11 +1365,7 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName return adminsChatReportID; } -/** - * - * @param {string} policyID - */ -function openWorkspaceReimburseView(policyID) { +function openWorkspaceReimburseView(policyID: string) { if (!policyID) { Log.warn('openWorkspaceReimburseView invalid params', {policyID}); return; @@ -1392,7 +1394,7 @@ function openWorkspaceReimburseView(policyID) { API.read('OpenWorkspaceReimburseView', {policyID}, onyxData); } -function openWorkspaceMembersPage(policyID, clientMemberEmails) { +function openWorkspaceMembersPage(policyID: string, clientMemberEmails: string[]) { if (!policyID || !clientMemberEmails) { Log.warn('openWorkspaceMembersPage invalid params', {policyID, clientMemberEmails}); return; @@ -1404,7 +1406,7 @@ function openWorkspaceMembersPage(policyID, clientMemberEmails) { }); } -function openWorkspaceInvitePage(policyID, clientMemberEmails) { +function openWorkspaceInvitePage(policyID: string, clientMemberEmails: string[]) { if (!policyID || !clientMemberEmails) { Log.warn('openWorkspaceInvitePage invalid params', {policyID, clientMemberEmails}); return; @@ -1416,57 +1418,36 @@ function openWorkspaceInvitePage(policyID, clientMemberEmails) { }); } -/** - * @param {String} policyID - */ -function openDraftWorkspaceRequest(policyID) { +function openDraftWorkspaceRequest(policyID: string) { API.read('OpenDraftWorkspaceRequest', {policyID}); } -/** - * @param {String} policyID - * @param {Object} invitedEmailsToAccountIDs - */ -function setWorkspaceInviteMembersDraft(policyID, invitedEmailsToAccountIDs) { +function setWorkspaceInviteMembersDraft(policyID: string, invitedEmailsToAccountIDs: Record) { Onyx.set(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${policyID}`, invitedEmailsToAccountIDs); } -/** - * @param {String} policyID - * @param {String} message - */ -function setWorkspaceInviteMessageDraft(policyID, message) { +function setWorkspaceInviteMessageDraft(policyID: string, message: string) { Onyx.set(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MESSAGE_DRAFT}${policyID}`, message); } -/** - * @param {String} policyID - */ -function clearErrors(policyID) { +function clearErrors(policyID: string) { setWorkspaceErrors(policyID, {}); hideWorkspaceAlertMessage(policyID); } /** * Dismiss the informative messages about which policy members were added with primary logins when invited with their secondary login. - * - * @param {String} policyID */ -function dismissAddedWithPrimaryLoginMessages(policyID) { +function dismissAddedWithPrimaryLoginMessages(policyID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {primaryLoginsInvited: null}); } -/** - * @param {String} policyID - * @param {String} category - * @returns {Object} - */ -function buildOptimisticPolicyRecentlyUsedCategories(policyID, category) { +function buildOptimisticPolicyRecentlyUsedCategories(policyID: string, category: string) { if (!policyID || !category) { return []; } - const policyRecentlyUsedCategories = lodashGet(allRecentlyUsedCategories, `${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`, []); + const policyRecentlyUsedCategories = allRecentlyUsedCategories?.[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES}${policyID}`] ?? []; return lodashUnion([category], policyRecentlyUsedCategories); } @@ -1476,10 +1457,9 @@ function buildOptimisticPolicyRecentlyUsedCategories(policyID, category) { * we create a Collect type workspace when the person taking the action becomes an owner and an admin, while we * add a new member to the workspace as an employee and convert the IOU report passed as a param into an expense report. * - * @param {Object} iouReport - * @returns {String} policyID of the workspace we have created + * @returns policyID of the workspace we have created */ -function createWorkspaceFromIOUPayment(iouReport) { +function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { // This flow only works for IOU reports if (!ReportUtils.isIOUReport(iouReport)) { return; @@ -1509,6 +1489,10 @@ function createWorkspaceFromIOUPayment(iouReport) { expenseCreatedReportActionID: workspaceChatCreatedReportActionID, } = ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName); + if (!employeeAccountID || !employeeEmail) { + return; + } + // Create the workspace chat for the employee whose IOU is being paid const employeeWorkspaceChat = createPolicyExpenseChats(policyID, {[employeeEmail]: employeeAccountID}, true); const newWorkspace = { @@ -1527,7 +1511,7 @@ function createWorkspaceFromIOUPayment(iouReport) { customUnits, }; - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -1560,7 +1544,7 @@ function createWorkspaceFromIOUPayment(iouReport) { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: announceReportActionData, + value: announceReportActionData as Record, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -1575,7 +1559,7 @@ function createWorkspaceFromIOUPayment(iouReport) { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: adminsReportActionData, + value: adminsReportActionData as Record, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -1590,7 +1574,7 @@ function createWorkspaceFromIOUPayment(iouReport) { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, - value: workspaceChatReportActionData, + value: workspaceChatReportActionData as Record, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -1605,7 +1589,7 @@ function createWorkspaceFromIOUPayment(iouReport) { ...employeeWorkspaceChat.onyxOptimisticData, ]; - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -1625,7 +1609,7 @@ function createWorkspaceFromIOUPayment(iouReport) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, value: { - [_.keys(announceChatData)[0]]: { + [Object.keys(announceChatData)[0]]: { pendingAction: null, }, }, @@ -1644,7 +1628,7 @@ function createWorkspaceFromIOUPayment(iouReport) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, value: { - [_.keys(adminsChatData)[0]]: { + [Object.keys(adminsChatData)[0]]: { pendingAction: null, }, }, @@ -1663,7 +1647,7 @@ function createWorkspaceFromIOUPayment(iouReport) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, value: { - [_.keys(workspaceChatData)[0]]: { + [Object.keys(workspaceChatData)[0]]: { pendingAction: null, }, }, @@ -1671,7 +1655,7 @@ function createWorkspaceFromIOUPayment(iouReport) { ...employeeWorkspaceChat.onyxSuccessData, ]; - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, @@ -1732,7 +1716,7 @@ function createWorkspaceFromIOUPayment(iouReport) { policyID, policyName: workspaceName, type: CONST.REPORT.TYPE.EXPENSE, - total: -iouReport.total, + total: -(iouReport?.total ?? 0), }; optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, @@ -1749,9 +1733,9 @@ function createWorkspaceFromIOUPayment(iouReport) { const reportTransactions = TransactionUtils.getAllReportTransactions(iouReportID); // For performance reasons, we are going to compose a merge collection data for transactions - const transactionsOptimisticData = {}; - const transactionFailureData = {}; - _.each(reportTransactions, (transaction) => { + const transactionsOptimisticData: Record = {}; + const transactionFailureData: Record = {}; + reportTransactions.forEach((transaction) => { transactionsOptimisticData[`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`] = { ...transaction, amount: -transaction.amount, diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js index 09b613350705..4c9bc5ca3a9b 100644 --- a/src/pages/workspace/WorkspaceMembersPage.js +++ b/src/pages/workspace/WorkspaceMembersPage.js @@ -85,6 +85,8 @@ function WorkspaceMembersPage(props) { const isOfflineAndNoMemberDataAvailable = _.isEmpty(props.policyMembers) && props.network.isOffline; const prevPersonalDetails = usePrevious(props.personalDetails); + console.log('POLICY', props.policy); + /** * Get filtered personalDetails list with current policyMembers * @param {Object} policyMembers diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index e6e3240d1b23..8c24e5d98dca 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -2,6 +2,39 @@ import {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; import * as OnyxCommon from './OnyxCommon'; +type Unit = 'mi' | 'km'; + +type Rate = { + name: string; + rate: number; + currency?: string; + customUnitRateID?: string; + errors?: OnyxCommon.Errors; + pendingAction?: string; +}; + +type NewCustomUnit = { + name: string; + customUnitID?: string; + attributes: { + unit: Unit; + }; + rates: Rate; + pendingAction?: string; + errors?: OnyxCommon.Errors; +}; + +type CustomUnit = { + name: string; + customUnitID?: string; + attributes: { + unit: Unit; + }; + rates: Record; + pendingAction?: string; + errors?: OnyxCommon.Errors; +}; + type Policy = { /** The ID of the policy */ id: string; @@ -19,7 +52,7 @@ type Policy = { owner: string; /** The accountID of the policy owner */ - ownerAccountID: number; + ownerAccountID?: number; /** The output currency for the policy */ outputCurrency: string; @@ -34,7 +67,7 @@ type Policy = { pendingAction?: OnyxCommon.PendingAction; /** A list of errors keyed by microtime */ - errors: OnyxCommon.Errors; + errors?: OnyxCommon.Errors; /** Whether this policy was loaded from a policy summary, or loaded completely with all of its values */ isFromFullPolicy?: boolean; @@ -43,22 +76,33 @@ type Policy = { lastModified?: string; /** The custom units data for this policy */ - customUnits?: Record; + customUnits?: Record; /** Whether chat rooms can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ - areChatRoomsEnabled: boolean; + areChatRoomsEnabled?: boolean; /** Whether policy expense chats can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ isPolicyExpenseChatEnabled: boolean; /** Whether the scheduled submit is enabled */ - autoReporting: boolean; + autoReporting?: boolean; /** The scheduled submit frequency set up on the this policy */ - autoReportingFrequency: ValueOf; + autoReportingFrequency?: ValueOf; /** The employee list of the policy */ employeeList?: []; + + makeMeAdmin?: boolean; + + pendingFields?: Record; + + originalFileName?: string; + + alertMessage?: string; + + primaryLoginsInvited?: Record; }; export default Policy; +export type {CustomUnit, NewCustomUnit}; diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 81a92c4bf603..0dea4c635a28 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -108,8 +108,9 @@ type Report = { lastMessageHtml?: string; welcomeMessage?: string; lastActorAccountID?: number; - ownerAccountID?: number; - participantAccountIDs?: number[]; + ownerAccountID?: string; + ownerEmail?: string; + participantAccountIDs?: string[]; total?: number; currency?: string; parentReportActionIDs?: number[]; @@ -130,6 +131,8 @@ type Report = { /** Pending fields for the report */ pendingFields?: Record; + pendingAction?: string; + /** The ID of the preexisting report (it is possible that we optimistically created a Report for which a report already exists) */ preexistingReportID?: string; From a5ea5d1d9a65def57aa44da5c9481bfbe758676d Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Wed, 6 Dec 2023 13:42:39 +0100 Subject: [PATCH 03/24] TS migration of Policy.js --- src/libs/actions/Policy.ts | 12 ++++-------- src/types/onyx/Report.ts | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 200eb00f4822..c235cae98bee 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -61,10 +61,7 @@ let allPolicyMembers: OnyxCollection; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY_MEMBERS, waitForCollectionCallback: true, - // callback: (val) => (allPolicyMembers = val), callback: (val) => { - // console.log('POLICYMEMBERS', val); - // console.log('TYPEPOLICYMEMBERS', typeof Object.keys(val)[0]); allPolicyMembers = val; }, }); @@ -119,7 +116,6 @@ function updateLastAccessedWorkspace(policyID: OnyxEntry) { /** * Check if the user has any active free policies (aka workspaces) - */ function hasActiveFreePolicy(policies: Array> | Record>): boolean { const adminFreePolicies = Object.values(policies).filter((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); @@ -292,7 +288,7 @@ type OptimisticAnnounceRoomMembers = { /** * Build optimistic data for removing users from the announcement room */ -function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: string[]) { +function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: number[]) { const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); const announceRoomMembers: OptimisticAnnounceRoomMembers = { onyxOptimisticData: [], @@ -324,7 +320,7 @@ function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: strin /** * Remove the passed members from the policy employeeList */ -function removeMembers(accountIDs: string[], policyID: string) { +function removeMembers(accountIDs: number[], policyID: string) { // In case user selects only themselves (admin), their email will be filtered out and the members // array passed will be empty, prevent the function from proceeding in that case as there is no one to remove if (accountIDs.length === 0) { @@ -377,7 +373,7 @@ function removeMembers(accountIDs: string[], policyID: string) { // Take the current policy members and remove them optimistically // console.log('POLICYMEMBERS', allPolicyMembers); const policyMemberAccountIDs = Object.keys(allPolicyMembers?.[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`] ?? {}); - const remainingMemberAccountIDs = policyMemberAccountIDs.filter((e) => !accountIDs.includes(e)); + const remainingMemberAccountIDs = policyMemberAccountIDs.filter((e) => !accountIDs.includes(Number(e))); const remainingLogins: string[] = PersonalDetailsUtils.getLoginsByAccountIDs(remainingMemberAccountIDs); const invitedPrimaryToSecondaryLogins: Record = {}; @@ -1804,7 +1800,7 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { }); // Create the MOVED report action and add it to the DM chat which indicates to the user where the report has been moved - const movedReportAction = ReportUtils.buildOptimisticMovedReportAction(oldPersonalPolicyID, policyID, memberData.workspaceChatReportID, iouReportID, workspaceName); + const movedReportAction = ReportUtils.buildOptimisticMovedReportAction(oldPersonalPolicyID ?? '', policyID, memberData.workspaceChatReportID, iouReportID, workspaceName); optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${oldChatReportID}`, diff --git a/src/types/onyx/Report.ts b/src/types/onyx/Report.ts index 28f772bd1ef0..d49bf2169022 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -108,9 +108,9 @@ type Report = { lastMessageHtml?: string; welcomeMessage?: string; lastActorAccountID?: number; - ownerAccountID?: string; + ownerAccountID?: number; ownerEmail?: string; - participantAccountIDs?: string[]; + participantAccountIDs?: number[]; total?: number; currency?: string; parentReportActionIDs?: number[]; From ecb7dfe2bf90b07f454f9990cfe8250b36cb41c4 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Wed, 13 Dec 2023 13:36:07 +0100 Subject: [PATCH 04/24] Add changes to ReportUtils & merge main --- assets/emojis/common.ts | 2 +- src/libs/ReportUtils.ts | 124 +++++++++++++----------------- src/libs/actions/Policy.ts | 66 ++++++++-------- src/types/onyx/OriginalMessage.ts | 11 ++- src/types/onyx/PersonalDetails.ts | 4 +- src/types/onyx/Report.ts | 22 ++++-- src/types/onyx/ReportAction.ts | 52 ++++++++++++- src/types/onyx/index.ts | 3 +- 8 files changed, 166 insertions(+), 118 deletions(-) diff --git a/assets/emojis/common.ts b/assets/emojis/common.ts index cbefb21cf2d6..8d2fded0a4a1 100644 --- a/assets/emojis/common.ts +++ b/assets/emojis/common.ts @@ -91,7 +91,7 @@ const emojis: PickerEmojis = [ code: '😊', }, { - name: 'innocent', + name: ':innocent:', code: '😇', }, { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 3e55a97cd294..c53b50122faf 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -6,7 +6,6 @@ import lodashEscape from 'lodash/escape'; import lodashFindLastIndex from 'lodash/findLastIndex'; import lodashIntersection from 'lodash/intersection'; import lodashIsEqual from 'lodash/isEqual'; -import React from 'react'; import Onyx, {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import {SvgProps} from 'react-native-svg'; import {ValueOf} from 'type-fest'; @@ -16,10 +15,11 @@ import CONST from '@src/CONST'; import {ParentNavigationSummaryParams, 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} from '@src/types/onyx/OriginalMessage'; -import {Message, ReportActions} from '@src/types/onyx/ReportAction'; +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'; import {EmptyObject, isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; @@ -187,10 +187,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, @@ -277,20 +275,20 @@ type OptimisticTaskReport = Pick< type TransactionDetails = | { - created: string; - amount: number; - currency: string; - merchant: string; - waypoints?: WaypointCollection; - comment: string; - category: string; - billable: boolean; - tag: string; - mccGroup?: ValueOf; - cardID: number; - originalAmount: number; - originalCurrency: string; - } + created: string; + amount: number; + currency: string; + merchant: string; + waypoints?: WaypointCollection; + comment: string; + category: string; + billable: boolean; + tag: string; + mccGroup?: ValueOf; + cardID: number; + originalAmount: number; + originalCurrency: string; +} | undefined; type OptimisticIOUReport = Pick< @@ -1409,7 +1407,7 @@ function getDisplayNameForParticipant(accountID?: number, shouldUseShortForm = f } function getDisplayNamesWithTooltips( - personalDetailsList: PersonalDetails[] | Record, + personalDetailsList: PersonalDetails[] | PersonalDetailsList, isMultipleParticipantReport: boolean, shouldFallbackToHidden = true, ): DisplayNameWithTooltips { @@ -1423,7 +1421,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); } @@ -1547,9 +1545,9 @@ function isUnreadWithMention(report: OnyxEntry | OptionData): boolean { /** * Determines if the option requires action from the current user. This can happen when it: - - is unread and the user was mentioned in one of the unread comments - - is for an outstanding task waiting on the user - - has an outstanding child money request that is waiting for an action from the current user (e.g. pay, approve, add bank account) + - is unread and the user was mentioned in one of the unread comments + - is for an outstanding task waiting on the user + - has an outstanding child money request that is waiting for an action from the current user (e.g. pay, approve, add bank account) * * @param option (report or optionItem) * @param parentReportAction (the report action the current report is a thread of) @@ -1828,12 +1826,12 @@ function canEditReportAction(reportAction: OnyxEntry): boolean { return Boolean( reportAction?.actorAccountID === currentUserAccountID && - isCommentOrIOU && - canEditMoneyRequest(reportAction) && // Returns true for non-IOU actions - !isReportMessageAttachment(reportAction?.message?.[0] ?? {type: '', text: ''}) && - !ReportActionsUtils.isDeletedAction(reportAction) && - !ReportActionsUtils.isCreatedTaskReportAction(reportAction) && - reportAction?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + isCommentOrIOU && + canEditMoneyRequest(reportAction) && // Returns true for non-IOU actions + !isReportMessageAttachment(reportAction?.message?.[0] ?? {type: '', text: ''}) && + !ReportActionsUtils.isDeletedAction(reportAction) && + !ReportActionsUtils.isCreatedTaskReportAction(reportAction) && + reportAction?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); } @@ -2376,7 +2374,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; @@ -2994,9 +2992,9 @@ function updateReportPreview( childMoneyRequestCount: (reportPreviewAction?.childMoneyRequestCount ?? 0) + (isPayRequest ? 0 : 1), childRecentReceiptTransactionIDs: hasReceipt ? { - ...(transaction && {[transaction.transactionID]: transaction?.created}), - ...previousTransactions, - } + ...(transaction && {[transaction.transactionID]: transaction?.created}), + ...previousTransactions, + } : recentReceiptTransactions, // As soon as we add a transaction without a receipt to the report, it will have ready money requests, // so we remove the whisper @@ -3052,7 +3050,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 = '', @@ -3578,12 +3576,12 @@ function canFlagReportAction(reportAction: OnyxEntry, reportID: st return Boolean( !isCurrentUserAction && - reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT && - !ReportActionsUtils.isDeletedAction(reportAction) && - !ReportActionsUtils.isCreatedTaskReportAction(reportAction) && - isNotEmptyObject(report) && - report && - isAllowedToComment(report), + reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT && + !ReportActionsUtils.isDeletedAction(reportAction) && + !ReportActionsUtils.isCreatedTaskReportAction(reportAction) && + isNotEmptyObject(report) && + report && + isAllowedToComment(report), ); } @@ -4227,12 +4225,12 @@ function getChannelLogMemberMessage(reportAction: OnyxEntry): stri function isGroupChat(report: OnyxEntry): boolean { return Boolean( report && - !isChatThread(report) && - !isTaskReport(report) && - !isMoneyRequestReport(report) && - !isArchivedRoom(report) && - !Object.values(CONST.REPORT.CHAT_TYPE).some((chatType) => chatType === getChatType(report)) && - (report.participantAccountIDs?.length ?? 0) > 2, + !isChatThread(report) && + !isTaskReport(report) && + !isMoneyRequestReport(report) && + !isArchivedRoom(report) && + !Object.values(CONST.REPORT.CHAT_TYPE).some((chatType) => chatType === getChatType(report)) && + (report.participantAccountIDs?.length ?? 0) > 2, ); } @@ -4252,33 +4250,16 @@ function shouldDisableWelcomeMessage(report: OnyxEntry, policy: OnyxEntr return isMoneyRequestReport(report) || isArchivedRoom(report) || !isChatRoom(report) || isChatThread(report) || !PolicyUtils.isPolicyAdmin(policy); } -function shouldAutoFocusOnKeyPress(event: KeyboardEvent): boolean { - if (event.key.length > 1) { - return false; - } - - // If a key is pressed in combination with Meta, Control or Alt do not focus - if (event.ctrlKey || event.metaKey) { - return false; - } - - if (event.code === 'Space') { - return false; - } - - return true; -} - /** * Navigates to the appropriate screen based on the presence of a private note for the current user. */ function navigateToPrivateNotes(report: Report, session: Session) { - if (isEmpty(report) || isEmpty(session)) { + if (isEmpty(report) || isEmpty(session) || !session.accountID) { return; } - const currentUserPrivateNote = report.privateNotes?.[String(session.accountID)]?.note ?? ''; + const currentUserPrivateNote = report.privateNotes?.[session.accountID]?.note ?? ''; if (isEmpty(currentUserPrivateNote)) { - Navigation.navigate(ROUTES.PRIVATE_NOTES_EDIT.getRoute(report.reportID, String(session.accountID))); + Navigation.navigate(ROUTES.PRIVATE_NOTES_EDIT.getRoute(report.reportID, session.accountID)); return; } Navigation.navigate(ROUTES.PRIVATE_NOTES_LIST.getRoute(report.reportID)); @@ -4451,7 +4432,6 @@ export { shouldDisableWelcomeMessage, navigateToPrivateNotes, canEditWriteCapability, - shouldAutoFocusOnKeyPress, }; -export type {OptionData, OptimisticClosedReportAction}; +export type {OptionData, OptimisticChatReport, OptimisticClosedReportAction}; diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 14ca1b169e78..5cf0c57b3882 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -14,7 +14,6 @@ import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; -import {OptimisticClosedReportAction} from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -181,7 +180,7 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string return; } - const filteredPolicies = Object.values(allPolicies).filter((policy) => policy?.id !== policyID); + const filteredPolicies = Object.values(allPolicies).filter((policy): policy is Policy => policy?.id !== policyID); const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -191,22 +190,22 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string errors: null, }, }, - ...reports.map(({reportID}) => ({ + ...reports.map(({reportID}) => ({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { + value: ({ stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS.CLOSED, hasDraft: false, - oldPolicyName: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name, - }, + oldPolicyName: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? '', + }), })), - ...reports.map(({reportID}) => ({ + ...(reports.map(({reportID}) => ({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`, value: null, - })), + }))), // Add closed actions to all chat reports linked to this policy ...reports.map(({reportID, ownerAccountID}) => { @@ -215,8 +214,8 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string if (!!ownerAccountID && ownerAccountID !== CONST.POLICY.OWNER_ACCOUNT_ID_FAKE) { emailClosingReport = allPersonalDetails?.[ownerAccountID]?.login ?? ''; } - const optimisticClosedReportAction = ReportUtils.buildOptimisticClosedReportAction(emailClosingReport, policyName, CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED); - const optimisticReportActions: Record = {}; + const optimisticClosedReportAction: ReportAction = ReportUtils.buildOptimisticClosedReportAction(emailClosingReport, policyName, CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED); + const optimisticReportActions: Record = {}; optimisticReportActions[optimisticClosedReportAction.reportActionID] = optimisticClosedReportAction; return { onyxMethod: Onyx.METHOD.MERGE, @@ -226,7 +225,7 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string }), ...(!hasActiveFreePolicy(filteredPolicies) - ? [ + ? ([ { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, @@ -234,8 +233,8 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string errors: null, }, }, - ] - : []), + ]) + : ([])), ]; // Restore the old report stateNum and statusNum @@ -433,7 +432,7 @@ function removeMembers(accountIDs: number[], policyID: string) { }, ]; - const filteredWorkspaceChats = workspaceChats.filter((e) => e !== null) as Report[]; + const filteredWorkspaceChats = workspaceChats.filter((e): e is Report => e !== null); const failureData = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -538,7 +537,7 @@ function createPolicyExpenseChats(policyID: string, invitedEmailsToAccountIDs: R workspaceMembersChats.onyxOptimisticData.push({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${optimisticReport.reportID}`, - value: {[optimisticCreatedAction.reportActionID]: optimisticCreatedAction as ReportAction}, + value: {[optimisticCreatedAction.reportActionID]: optimisticCreatedAction}, }); workspaceMembersChats.onyxSuccessData.push({ @@ -795,7 +794,7 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - ...((allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {}) as Policy), + ...((allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {} as Policy)), pendingFields: { generalSettings: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, @@ -821,7 +820,7 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) }, }, } - : {}) as Record), + : {})), }, }, ]; @@ -971,7 +970,7 @@ function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: C [currentCustomUnit.customUnitID]: { customUnitID: currentCustomUnit.customUnitID, rates: { - [currentCustomUnit.rates.customUnitRateID]: { + [newCustomUnit.rates.customUnitRateID]: { ...currentCustomUnit.rates, errors: ErrorUtils.getMicroSecondOnyxError('workspace.reimburse.updateCustomUnitError'), }, @@ -1592,7 +1591,7 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: announceReportActionData as Record, + value: announceReportActionData, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -1607,7 +1606,7 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: adminsReportActionData as Record, + value: adminsReportActionData, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -1622,7 +1621,7 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, - value: workspaceChatReportActionData as Record, + value: workspaceChatReportActionData, }, { onyxMethod: Onyx.METHOD.MERGE, @@ -1833,18 +1832,21 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { }, }); - // Update the created timestamp of the report preview action to be after the workspace chat created timestamp. - optimisticData.push({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${memberData.workspaceChatReportID}`, - value: { - [reportPreview.reportActionID]: { - ...reportPreview, - message: ReportUtils.getReportPreviewMessage(expenseReport, {}, false, false, newWorkspace), - created: DateUtils.getDBTime(), + if (reportPreview?.reportActionID) { + // Update the created timestamp of the report preview action to be after the workspace chat created timestamp. + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${memberData.workspaceChatReportID}`, + value: { + [reportPreview.reportActionID]: ({ + ...reportPreview, + message: ReportUtils.getReportPreviewMessage(expenseReport, {}, false, false, newWorkspace), + created: DateUtils.getDBTime(), + }), }, - }, - }); + }); + } + failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${memberData.workspaceChatReportID}`, diff --git a/src/types/onyx/OriginalMessage.ts b/src/types/onyx/OriginalMessage.ts index f76fbd5ffd7d..fc7f8eb8ba31 100644 --- a/src/types/onyx/OriginalMessage.ts +++ b/src/types/onyx/OriginalMessage.ts @@ -45,6 +45,7 @@ type IOUMessage = { /** Only exists when we are sending money */ IOUDetails?: IOUDetails; }; + type OriginalMessageIOU = { actionName: typeof CONST.REPORT.ACTIONS.TYPE.IOU; originalMessage: IOUMessage; @@ -69,7 +70,7 @@ type DecisionName = ValueOf< >; type Decision = { decision: DecisionName; - timestamp: string; + timestamp?: string; }; type User = { @@ -105,6 +106,7 @@ type OriginalMessageAddComment = { reactions?: Reaction[]; }; }; + type OriginalMessageSubmitted = { actionName: typeof CONST.REPORT.ACTIONS.TYPE.SUBMITTED; originalMessage: unknown; @@ -117,7 +119,7 @@ type OriginalMessageClosed = { type OriginalMessageCreated = { actionName: typeof CONST.REPORT.ACTIONS.TYPE.CREATED; - originalMessage: unknown; + originalMessage?: unknown; }; type OriginalMessageRenamed = { @@ -183,7 +185,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; }; @@ -231,4 +234,4 @@ type OriginalMessage = | OriginalMessageMoved; export default OriginalMessage; -export type {ChronosOOOEvent, Decision, Reaction, ActionName, IOUMessage, Closed, OriginalMessageActionName, ChangeLog}; +export type {ChronosOOOEvent, Decision, Reaction, ActionName, IOUMessage, Closed, OriginalMessageActionName, ChangeLog, OriginalMessageIOU, OriginalMessageCreated}; 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/Report.ts b/src/types/onyx/Report.ts index 41a2a4d4effd..95086aeda0a1 100644 --- a/src/types/onyx/Report.ts +++ b/src/types/onyx/Report.ts @@ -3,6 +3,16 @@ import CONST from '@src/CONST'; import * as OnyxCommon from './OnyxCommon'; import PersonalDetails from './PersonalDetails'; +type NotificationPreference = ValueOf; + +type WriteCapability = ValueOf; + +type Note = { + note: string; + errors?: OnyxCommon.Errors; + pendingAction?: OnyxCommon.PendingAction; +}; + type Report = { /** The specific type of chat */ chatType?: ValueOf; @@ -47,7 +57,7 @@ type Report = { lastMentionedTime?: string | null; /** The current user's notification preference for this report */ - notificationPreference?: string | number; + notificationPreference?: NotificationPreference; /** The policy name to use */ policyName?: string | null; @@ -89,7 +99,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; @@ -136,8 +146,7 @@ type Report = { /** Pending fields for the report */ pendingFields?: Record; - - pendingAction?: string; + pendingAction?: OnyxCommon.PendingAction; /** The ID of the preexisting report (it is possible that we optimistically created a Report for which a report already exists) */ preexistingReportID?: string; @@ -148,7 +157,10 @@ type Report = { isChatRoom?: boolean; participantsList?: Array>; text?: string; - privateNotes?: Record; + 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 64e1eb0b7c88..b193d5e720ec 100644 --- a/src/types/onyx/ReportAction.ts +++ b/src/types/onyx/ReportAction.ts @@ -1,8 +1,10 @@ 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'; import {Receipt} from './Transaction'; type Message = { @@ -52,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; @@ -79,6 +112,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; @@ -121,7 +157,7 @@ type ReportActionBase = { isFirstItem?: boolean; /** Informations about attachments of report action */ - attachmentInfo?: (File & {source: string; uri: string}) | Record; + attachmentInfo?: File | EmptyObject; /** Receipt tied to report action */ receipt?: Receipt; @@ -129,15 +165,27 @@ type ReportActionBase = { /** ISO-formatted datetime */ lastModified?: string; + /** Is this action pending? */ pendingAction?: OnyxCommon.PendingAction; delegateAccountID?: string; /** 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?: LinkMetadata[]; + + /** The current user's notification preference for this report's child */ + childReportNotificationPreference?: NotificationPreference; }; type ReportAction = ReportActionBase & OriginalMessage; @@ -145,4 +193,4 @@ type ReportAction = ReportActionBase & OriginalMessage; type ReportActions = Record; export default ReportAction; -export type {Message, ReportActions}; +export type {ReportActions, ReportActionBase, Message}; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 8329b56dc4b8..42bd4500d8ad 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -19,7 +19,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, {PolicyCategories} from './PolicyCategory'; @@ -80,6 +80,7 @@ export type { OnyxUpdatesFromServer, PersonalBankAccount, PersonalDetails, + PersonalDetailsList, PlaidData, Policy, PolicyCategory, From a7934868a1bcda4489de5296cfbdd0c522d3453d Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 28 Dec 2023 07:53:35 +0100 Subject: [PATCH 05/24] add remaining typescript typing to the policy lib --- src/libs/ReportUtils.ts | 70 +++++------ src/libs/actions/Policy.ts | 129 ++++++++++++-------- src/pages/workspace/WorkspaceMembersPage.js | 2 - 3 files changed, 111 insertions(+), 90 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c53b50122faf..129947c09065 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -275,20 +275,20 @@ type OptimisticTaskReport = Pick< type TransactionDetails = | { - created: string; - amount: number; - currency: string; - merchant: string; - waypoints?: WaypointCollection; - comment: string; - category: string; - billable: boolean; - tag: string; - mccGroup?: ValueOf; - cardID: number; - originalAmount: number; - originalCurrency: string; -} + created: string; + amount: number; + currency: string; + merchant: string; + waypoints?: WaypointCollection; + comment: string; + category: string; + billable: boolean; + tag: string; + mccGroup?: ValueOf; + cardID: number; + originalAmount: number; + originalCurrency: string; + } | undefined; type OptimisticIOUReport = Pick< @@ -1826,12 +1826,12 @@ function canEditReportAction(reportAction: OnyxEntry): boolean { return Boolean( reportAction?.actorAccountID === currentUserAccountID && - isCommentOrIOU && - canEditMoneyRequest(reportAction) && // Returns true for non-IOU actions - !isReportMessageAttachment(reportAction?.message?.[0] ?? {type: '', text: ''}) && - !ReportActionsUtils.isDeletedAction(reportAction) && - !ReportActionsUtils.isCreatedTaskReportAction(reportAction) && - reportAction?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + isCommentOrIOU && + canEditMoneyRequest(reportAction) && // Returns true for non-IOU actions + !isReportMessageAttachment(reportAction?.message?.[0] ?? {type: '', text: ''}) && + !ReportActionsUtils.isDeletedAction(reportAction) && + !ReportActionsUtils.isCreatedTaskReportAction(reportAction) && + reportAction?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); } @@ -2992,9 +2992,9 @@ function updateReportPreview( childMoneyRequestCount: (reportPreviewAction?.childMoneyRequestCount ?? 0) + (isPayRequest ? 0 : 1), childRecentReceiptTransactionIDs: hasReceipt ? { - ...(transaction && {[transaction.transactionID]: transaction?.created}), - ...previousTransactions, - } + ...(transaction && {[transaction.transactionID]: transaction?.created}), + ...previousTransactions, + } : recentReceiptTransactions, // As soon as we add a transaction without a receipt to the report, it will have ready money requests, // so we remove the whisper @@ -3576,12 +3576,12 @@ function canFlagReportAction(reportAction: OnyxEntry, reportID: st return Boolean( !isCurrentUserAction && - reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT && - !ReportActionsUtils.isDeletedAction(reportAction) && - !ReportActionsUtils.isCreatedTaskReportAction(reportAction) && - isNotEmptyObject(report) && - report && - isAllowedToComment(report), + reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT && + !ReportActionsUtils.isDeletedAction(reportAction) && + !ReportActionsUtils.isCreatedTaskReportAction(reportAction) && + isNotEmptyObject(report) && + report && + isAllowedToComment(report), ); } @@ -4225,12 +4225,12 @@ function getChannelLogMemberMessage(reportAction: OnyxEntry): stri function isGroupChat(report: OnyxEntry): boolean { return Boolean( report && - !isChatThread(report) && - !isTaskReport(report) && - !isMoneyRequestReport(report) && - !isArchivedRoom(report) && - !Object.values(CONST.REPORT.CHAT_TYPE).some((chatType) => chatType === getChatType(report)) && - (report.participantAccountIDs?.length ?? 0) > 2, + !isChatThread(report) && + !isTaskReport(report) && + !isMoneyRequestReport(report) && + !isArchivedRoom(report) && + !Object.values(CONST.REPORT.CHAT_TYPE).some((chatType) => chatType === getChatType(report)) && + (report.participantAccountIDs?.length ?? 0) > 2, ); } diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 5cf0c57b3882..ee0340f6e9fa 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -17,18 +17,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import { - Network, - PersonalDetails, - Policy, - PolicyMember, PolicyTags, - RecentlyUsedCategories, - RecentlyUsedTags, - ReimbursementAccount, - Report, - ReportAction, - Transaction -} from '@src/types/onyx'; +import {Network, PersonalDetails, Policy, PolicyMember, PolicyTags, RecentlyUsedCategories, RecentlyUsedTags, ReimbursementAccount, Report, ReportAction, Transaction} from '@src/types/onyx'; import {Errors} from '@src/types/onyx/OnyxCommon'; import {CustomUnit, NewCustomUnit} from '@src/types/onyx/Policy'; import {isNotEmptyObject} from '@src/types/utils/EmptyObject'; @@ -46,8 +35,8 @@ Onyx.connect({ // More info: https://github.com/Expensify/App/issues/14260 const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); const policyReports = ReportUtils.getAllPolicyReports(policyID); - const cleanUpMergeQueries = {}; - const cleanUpSetQueries = {}; + const cleanUpMergeQueries: OnyxCollection> = {}; + const cleanUpSetQueries: OnyxCollection> = {}; policyReports.forEach((policyReport) => { if (!policyReport) { return; @@ -58,7 +47,7 @@ Onyx.connect({ cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; }); Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); - Onyx.multiSet(cleanUpSetQueries); + Onyx.multiSet(cleanUpSetQueries as Partial<{string: null}>); delete allPolicies[key]; return; } @@ -190,33 +179,33 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string errors: null, }, }, - ...reports.map(({reportID}) => ({ + ...reports.map(({reportID}) => ({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: ({ + value: { stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS.CLOSED, hasDraft: false, oldPolicyName: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? '', - }), + }, })), - ...(reports.map(({reportID}) => ({ + ...reports.map(({reportID}) => ({ onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`, value: null, - }))), + })), // Add closed actions to all chat reports linked to this policy - ...reports.map(({reportID, ownerAccountID}) => { + ...reports.map(({reportID, ownerAccountID}) => { // Announce & admin chats have FAKE owners, but workspace chats w/ users do have owners. let emailClosingReport: string = CONST.POLICY.OWNER_EMAIL_FAKE; if (!!ownerAccountID && ownerAccountID !== CONST.POLICY.OWNER_ACCOUNT_ID_FAKE) { emailClosingReport = allPersonalDetails?.[ownerAccountID]?.login ?? ''; } - const optimisticClosedReportAction: ReportAction = ReportUtils.buildOptimisticClosedReportAction(emailClosingReport, policyName, CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED); + const optimisticClosedReportAction = ReportUtils.buildOptimisticClosedReportAction(emailClosingReport, policyName, CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED); const optimisticReportActions: Record = {}; - optimisticReportActions[optimisticClosedReportAction.reportActionID] = optimisticClosedReportAction; + optimisticReportActions[optimisticClosedReportAction.reportActionID] = optimisticClosedReportAction as ReportAction; return { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, @@ -225,7 +214,7 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string }), ...(!hasActiveFreePolicy(filteredPolicies) - ? ([ + ? [ { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, @@ -233,8 +222,8 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string errors: null, }, }, - ]) - : ([])), + ] + : []), ]; // Restore the old report stateNum and statusNum @@ -358,7 +347,6 @@ function removeMembers(accountIDs: number[], policyID: string) { return; } - const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`; const policy = ReportUtils.getPolicy(policyID); const workspaceChats = ReportUtils.getWorkspaceChats(policyID, accountIDs); const optimisticClosedReportActions = workspaceChats.map(() => ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy.name, CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY)); @@ -374,13 +362,13 @@ function removeMembers(accountIDs: number[], policyID: string) { failureMembersState[accountID] = {errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove')}; }); - const optimisticData = [ + const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: membersListKey, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, value: optimisticMembersState, }, - ...workspaceChats.map((report) => ({ + ...workspaceChats.map((report) => ({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, value: { @@ -390,10 +378,10 @@ function removeMembers(accountIDs: number[], policyID: string) { hasDraft: false, }, })), - ...optimisticClosedReportActions.map((reportAction, index) => ({ + ...optimisticClosedReportActions.map((reportAction, index) => ({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats?.[index]?.reportID}`, - value: {[reportAction.reportActionID]: reportAction}, + value: {[reportAction.reportActionID]: reportAction as ReportAction}, })), ...announceRoomMembers.onyxOptimisticData, ]; @@ -424,22 +412,22 @@ function removeMembers(accountIDs: number[], policyID: string) { } } - const successData = [ + const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: membersListKey, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, value: successMembersState, }, ]; const filteredWorkspaceChats = workspaceChats.filter((e): e is Report => e !== null); - const failureData = [ + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: membersListKey, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, value: failureMembersState, }, - ...filteredWorkspaceChats.map(({reportID, stateNum, statusNum, hasDraft, oldPolicyName = null}) => ({ + ...filteredWorkspaceChats.map(({reportID, stateNum, statusNum, hasDraft, oldPolicyName = null}) => ({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { @@ -449,7 +437,7 @@ function removeMembers(accountIDs: number[], policyID: string) { oldPolicyName, }, })), - ...optimisticClosedReportActions.map((reportAction, index) => ({ + ...optimisticClosedReportActions.map((reportAction, index) => ({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats?.[index]?.reportID}`, value: {[reportAction.reportActionID]: null}, @@ -794,7 +782,7 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - ...((allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? {} as Policy)), + ...(allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? ({} as Policy)), pendingFields: { generalSettings: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, @@ -806,7 +794,7 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) }, name, outputCurrency: currency, - ...((distanceUnit + ...(distanceUnit ? { customUnits: { [distanceUnit.customUnitID]: { @@ -820,7 +808,7 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) }, }, } - : {})), + : {}), }, }, ]; @@ -1626,12 +1614,19 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, - value: null, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS}${policyID}`, - value: null, + value: { + pendingAction: null, + }, }, ...employeeWorkspaceChat.onyxOptimisticData, ]; @@ -1706,37 +1701,60 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, - value: null, + value: { + pendingAction: null, + }, }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: null, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: null, + value: { + pendingAction: null, + }, }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: null, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: null, + value: { + pendingAction: null, + }, }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${workspaceChatReportID}`, - value: null, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, }, { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`, - value: null, + value: { + pendingAction: null, + }, }, ]; @@ -1838,11 +1856,16 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${memberData.workspaceChatReportID}`, value: { - [reportPreview.reportActionID]: ({ + [reportPreview.reportActionID]: { ...reportPreview, - message: ReportUtils.getReportPreviewMessage(expenseReport, {}, false, false, newWorkspace), + message: [ + { + type: CONST.REPORT.MESSAGE.TYPE.TEXT, + text: ReportUtils.getReportPreviewMessage(expenseReport, {}, false, false, newWorkspace), + }, + ], created: DateUtils.getDBTime(), - }), + }, }, }); } diff --git a/src/pages/workspace/WorkspaceMembersPage.js b/src/pages/workspace/WorkspaceMembersPage.js index 4c9bc5ca3a9b..09b613350705 100644 --- a/src/pages/workspace/WorkspaceMembersPage.js +++ b/src/pages/workspace/WorkspaceMembersPage.js @@ -85,8 +85,6 @@ function WorkspaceMembersPage(props) { const isOfflineAndNoMemberDataAvailable = _.isEmpty(props.policyMembers) && props.network.isOffline; const prevPersonalDetails = usePrevious(props.personalDetails); - console.log('POLICY', props.policy); - /** * Get filtered personalDetails list with current policyMembers * @param {Object} policyMembers From 1097bc162b74897bf2418319df6df306b6dbeee3 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Tue, 2 Jan 2024 11:59:22 +0100 Subject: [PATCH 06/24] add review suggested changes --- assets/emojis/common.ts | 2 +- src/libs/actions/Policy.ts | 595 +++++++++++++++++++++---------------- 2 files changed, 348 insertions(+), 249 deletions(-) diff --git a/assets/emojis/common.ts b/assets/emojis/common.ts index 8d2fded0a4a1..cbefb21cf2d6 100644 --- a/assets/emojis/common.ts +++ b/assets/emojis/common.ts @@ -91,7 +91,7 @@ const emojis: PickerEmojis = [ code: '😊', }, { - name: ':innocent:', + name: 'innocent', code: '😇', }, { diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index d8e1806d801d..8da49639ad9f 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -257,7 +257,14 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string // We don't need success data since the push notification will update // the onyxData for all connected clients. const successData: OnyxUpdate[] = []; - API.write('DeleteWorkspace', {policyID}, {optimisticData, successData, failureData}); + + type DeleteWorkspaceParams = { + policyID: string; + }; + + const params: DeleteWorkspaceParams = {policyID}; + + API.write('DeleteWorkspace', params, {optimisticData, successData, failureData}); // Reset the lastAccessedWorkspacePolicyID if (policyID === lastAccessedWorkspacePolicyID) { @@ -354,6 +361,7 @@ function removeMembers(accountIDs: number[], policyID: string) { return; } + const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}` as const; const policy = ReportUtils.getPolicy(policyID); const workspaceChats = ReportUtils.getWorkspaceChats(policyID, accountIDs); const optimisticClosedReportActions = workspaceChats.map(() => ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy.name, CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY)); @@ -372,7 +380,7 @@ function removeMembers(accountIDs: number[], policyID: string) { const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + key: membersListKey, value: optimisticMembersState, }, ...workspaceChats.map((report) => ({ @@ -397,7 +405,6 @@ function removeMembers(accountIDs: number[], policyID: string) { // If we delete all these logins then we should clear the informative messages since they are no longer relevant. if (isNotEmptyObject(policy?.primaryLoginsInvited ?? {})) { // Take the current policy members and remove them optimistically - // console.log('POLICYMEMBERS', allPolicyMembers); const policyMemberAccountIDs = Object.keys(allPolicyMembers?.[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`] ?? {}).map((accountID) => parseInt(accountID)); const remainingMemberAccountIDs = policyMemberAccountIDs.filter((e) => !accountIDs.includes(Number(e))); const remainingLogins: string[] = PersonalDetailsUtils.getLoginsByAccountIDs(remainingMemberAccountIDs); @@ -411,7 +418,7 @@ function removeMembers(accountIDs: number[], policyID: string) { if (!remainingLogins.some((remainingLogin) => Boolean(invitedPrimaryToSecondaryLogins[remainingLogin]))) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + key: membersListKey, value: { primaryLoginsInvited: null, }, @@ -422,7 +429,7 @@ function removeMembers(accountIDs: number[], policyID: string) { const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + key: membersListKey, value: successMembersState, }, ]; @@ -431,7 +438,7 @@ function removeMembers(accountIDs: number[], policyID: string) { const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + key: membersListKey, value: failureMembersState, }, ...filteredWorkspaceChats.map(({reportID, stateNum, statusNum, hasDraft, oldPolicyName = null}) => ({ @@ -451,14 +458,18 @@ function removeMembers(accountIDs: number[], policyID: string) { })), ...announceRoomMembers.onyxFailureData, ]; - API.write( - 'DeleteMembersFromWorkspace', - { - emailList: accountIDs.map((accountID) => allPersonalDetails?.[accountID]?.login).join(','), - policyID, - }, - {optimisticData, successData, failureData}, - ); + + type DeleteMembersFromWorkspaceParams = { + emailList: string; + policyID: string; + }; + + const params: DeleteMembersFromWorkspaceParams = { + emailList: accountIDs.map((accountID) => allPersonalDetails?.[accountID]?.login).join(','), + policyID, + }; + + API.write('DeleteMembersFromWorkspace', params, {optimisticData, successData, failureData}); } type WorkspaceMembersChats = { @@ -569,6 +580,7 @@ function createPolicyExpenseChats(policyID: string, invitedEmailsToAccountIDs: R * Adds members to the specified workspace/policyID */ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record, welcomeNote: string, policyID: string) { + const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}` as const; const logins = Object.keys(invitedEmailsToAccountIDs).map((memberLogin) => OptionsListUtils.addSMSDomainIfPhoneNumber(memberLogin)); const accountIDs = Object.values(invitedEmailsToAccountIDs); const newPersonalDetailsOnyxData = PersonalDetailsUtils.getNewPersonalDetailsOnyxData(logins, accountIDs); @@ -590,7 +602,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record const optimisticData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + key: membersListKey, // Convert to object with each key containing {pendingAction: ‘add’} value: optimisticMembersState, @@ -603,7 +615,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record const successData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + key: membersListKey, // Convert to object with each key clearing pendingAction, when it is an existing account. // Remove the object, when it is a newly created account. @@ -625,7 +637,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + key: membersListKey, // Convert to object with each key containing the error. We don’t // need to remove the members since that is handled by onClose of OfflineWithFeedback. @@ -701,7 +713,17 @@ function updateWorkspaceAvatar(policyID: string, file: File) { }, ]; - API.write('UpdateWorkspaceAvatar', {policyID, file}, {optimisticData, successData, failureData}); + type UpdateWorkspaceAvatarParams = { + policyID: string; + file: File; + }; + + const params: UpdateWorkspaceAvatarParams = { + policyID, + file, + }; + + API.write('UpdateWorkspaceAvatar', params, {optimisticData, successData, failureData}); } /** @@ -748,7 +770,14 @@ function deleteWorkspaceAvatar(policyID: string) { }, }, ]; - API.write('DeleteWorkspaceAvatar', {policyID}, {optimisticData, successData, failureData}); + + type DeleteWorkspaceAvatarParams = { + policyID: string; + }; + + const params: DeleteWorkspaceAvatarParams = {policyID}; + + API.write('DeleteWorkspaceAvatar', params, {optimisticData, successData, failureData}); } /** @@ -789,7 +818,7 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) onyxMethod: Onyx.METHOD.SET, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - ...(allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] ?? ({} as Policy)), + ...policy, pendingFields: { generalSettings: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, @@ -852,15 +881,23 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) }, ]; - API.write( - 'UpdateWorkspaceGeneralSettings', - {policyID, workspaceName: name, currency}, - { - optimisticData, - successData, - failureData, - }, - ); + type UpdateWorkspaceGeneralSettingsParams = { + policyID: string; + workspaceName: string; + currency: string; + }; + + const params: UpdateWorkspaceGeneralSettingsParams = { + policyID, + workspaceName: name, + currency, + }; + + API.write('UpdateWorkspaceGeneralSettings', params, { + optimisticData, + successData, + failureData, + }); } /** @@ -909,7 +946,6 @@ function hideWorkspaceAlertMessage(policyID: string) { } function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: NewCustomUnit, lastModified: number) { - // console.log('CUSTOMRATES', newCustomUnit, currentCustomUnit); if (!currentCustomUnit.customUnitID || !newCustomUnit?.customUnitID || !newCustomUnit.rates?.customUnitRateID || !currentCustomUnit.rates?.customUnitRateID) { return; } @@ -978,16 +1014,22 @@ function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: C const newCustomUnitParam = lodashClone(newCustomUnit); newCustomUnitParam.rates = {...newCustomUnitParam.rates, pendingAction: undefined, errors: undefined}; - API.write( - 'UpdateWorkspaceCustomUnitAndRate', - { - policyID, - lastModified, - customUnit: JSON.stringify(newCustomUnitParam), - customUnitRate: JSON.stringify(newCustomUnitParam.rates), - }, - {optimisticData, successData, failureData}, - ); + + type UpdateWorkspaceCustomUnitAndRate = { + policyID: string; + lastModified: number; + customUnit: string; + customUnitRate: string; + }; + + const params: UpdateWorkspaceCustomUnitAndRate = { + policyID, + lastModified, + customUnit: JSON.stringify(newCustomUnitParam), + customUnitRate: JSON.stringify(newCustomUnitParam.rates), + }; + + API.write('UpdateWorkspaceCustomUnitAndRate', params, {optimisticData, successData, failureData}); } /** @@ -1044,7 +1086,7 @@ function generateDefaultWorkspaceName(email = ''): string { const username = emailParts[0]; const domain = emailParts[1]; - if (PUBLIC_DOMAINS.includes(domain.toLowerCase() as (typeof PUBLIC_DOMAINS)[number])) { + if ((PUBLIC_DOMAINS as readonly string[]).includes(domain.toLowerCase())) { defaultWorkspaceName = `${Str.UCFirst(username)}'s Workspace`; } else { defaultWorkspaceName = `${Str.UCFirst(domain.split('.')[0])}'s Workspace`; @@ -1184,209 +1226,223 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName expenseCreatedReportActionID, } = ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName); - API.write( - 'CreateWorkspace', - { - policyID, - announceChatReportID, - adminsChatReportID, - expenseChatReportID, - ownerEmail: policyOwnerEmail, - makeMeAdmin, - policyName: workspaceName, - type: CONST.POLICY.TYPE.FREE, - announceCreatedReportActionID, - adminsCreatedReportActionID, - expenseCreatedReportActionID, - customUnitID, - customUnitRateID, - }, - { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - id: policyID, - type: CONST.POLICY.TYPE.FREE, - name: workspaceName, + type CreateWorkspaceParams = { + policyID: string; + announceChatReportID: string; + adminsChatReportID: string; + expenseChatReportID: string; + ownerEmail: string; + makeMeAdmin: boolean; + policyName: string; + type: string; + announceCreatedReportActionID: string; + adminsCreatedReportActionID: string; + expenseCreatedReportActionID: string; + customUnitID: string; + customUnitRateID: string; + }; + + const params: CreateWorkspaceParams = { + policyID, + announceChatReportID, + adminsChatReportID, + expenseChatReportID, + ownerEmail: policyOwnerEmail, + makeMeAdmin, + policyName: workspaceName, + type: CONST.POLICY.TYPE.FREE, + announceCreatedReportActionID, + adminsCreatedReportActionID, + expenseCreatedReportActionID, + customUnitID, + customUnitRateID, + }; + + API.write('CreateWorkspace', params, { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + id: policyID, + type: CONST.POLICY.TYPE.FREE, + name: workspaceName, + role: CONST.POLICY.ROLE.ADMIN, + owner: sessionEmail, + isPolicyExpenseChatEnabled: true, + outputCurrency, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + customUnits, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + value: { + [sessionAccountID]: { role: CONST.POLICY.ROLE.ADMIN, - owner: sessionEmail, - isPolicyExpenseChatEnabled: true, - outputCurrency, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - customUnits, + errors: {}, }, }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, - value: { - [sessionAccountID]: { - role: CONST.POLICY.ROLE.ADMIN, - errors: {}, - }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, + ...announceChatData, }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...announceChatData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: announceReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, + ...adminsChatData, }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: announceReportActionData, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...adminsChatData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: adminsReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, }, + ...expenseChatData, }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: adminsReportActionData, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...expenseChatData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, + value: expenseReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS}${policyID}`, + value: null, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: {pendingAction: null}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, }, + pendingAction: null, }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, - value: expenseReportActionData, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS}${policyID}`, - value: null, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: {pendingAction: null}, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: { + [Object.keys(announceChatData)[0]]: { pendingAction: null, }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: { - [Object.keys(announceChatData)[0]]: { - pendingAction: null, - }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, }, + pendingAction: null, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: { + [Object.keys(adminsChatData)[0]]: { pendingAction: null, }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: { - [Object.keys(adminsChatData)[0]]: { - pendingAction: null, - }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, }, + pendingAction: null, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, + value: { + [Object.keys(expenseChatData)[0]]: { pendingAction: null, }, }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, - value: { - [Object.keys(expenseChatData)[0]]: { - pendingAction: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, - value: null, - }, - ], - }, - ); + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, + value: null, + }, + ], + }); return adminsChatReportID; } @@ -1417,7 +1473,13 @@ function openWorkspaceReimburseView(policyID: string) { ], }; - API.read('OpenWorkspaceReimburseView', {policyID}, onyxData); + type OpenWorkspaceReimburseViewParams = { + policyID: string; + }; + + const params: OpenWorkspaceReimburseViewParams = {policyID}; + + API.read('OpenWorkspaceReimburseView', params, onyxData); } function openWorkspaceMembersPage(policyID: string, clientMemberEmails: string[]) { @@ -1426,10 +1488,17 @@ function openWorkspaceMembersPage(policyID: string, clientMemberEmails: string[] return; } - API.read('OpenWorkspaceMembersPage', { + type OpenWorkspaceMembersPageParams = { + policyID: string; + clientMemberEmails: string; + }; + + const params: OpenWorkspaceMembersPageParams = { policyID, clientMemberEmails: JSON.stringify(clientMemberEmails), - }); + }; + + API.read('OpenWorkspaceMembersPage', params); } function openWorkspaceInvitePage(policyID: string, clientMemberEmails: string[]) { @@ -1438,14 +1507,27 @@ function openWorkspaceInvitePage(policyID: string, clientMemberEmails: string[]) return; } - API.read('OpenWorkspaceInvitePage', { + type OpenWorkspaceInvitePageParams = { + policyID: string; + clientMemberEmails: string; + }; + + const params: OpenWorkspaceInvitePageParams = { policyID, clientMemberEmails: JSON.stringify(clientMemberEmails), - }); + }; + + API.read('OpenWorkspaceInvitePage', params); } function openDraftWorkspaceRequest(policyID: string) { - API.read('OpenDraftWorkspaceRequest', {policyID}); + type OpenDraftWorkspaceRequestParams = { + policyID: string; + }; + + const params: OpenDraftWorkspaceRequestParams = {policyID}; + + API.read('OpenDraftWorkspaceRequest', params); } function setWorkspaceInviteMembersDraft(policyID: string, invitedEmailsToAccountIDs: Record) { @@ -1906,28 +1988,45 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { value: {[movedReportAction.reportActionID]: null}, }); - API.write( - 'CreateWorkspaceFromIOUPayment', - { - policyID, - announceChatReportID, - adminsChatReportID, - expenseChatReportID: workspaceChatReportID, - ownerEmail: '', - makeMeAdmin: false, - policyName: workspaceName, - type: CONST.POLICY.TYPE.TEAM, - announceCreatedReportActionID, - adminsCreatedReportActionID, - expenseCreatedReportActionID: workspaceChatCreatedReportActionID, - customUnitID, - customUnitRateID, - iouReportID, - memberData: JSON.stringify(memberData), - reportActionID: movedReportAction.reportActionID, - }, - {optimisticData, successData, failureData}, - ); + type CreateWorkspaceFromIOUPayment = { + policyID: string; + announceChatReportID: string; + adminsChatReportID: string; + expenseChatReportID: string; + ownerEmail: string; + makeMeAdmin: boolean; + policyName: string; + type: string; + announceCreatedReportActionID: string; + adminsCreatedReportActionID: string; + expenseCreatedReportActionID: string; + customUnitID: string; + customUnitRateID: string; + iouReportID: string; + memberData: string; + reportActionID: string; + }; + + const params: CreateWorkspaceFromIOUPayment = { + policyID, + announceChatReportID, + adminsChatReportID, + expenseChatReportID: workspaceChatReportID, + ownerEmail: '', + makeMeAdmin: false, + policyName: workspaceName, + type: CONST.POLICY.TYPE.TEAM, + announceCreatedReportActionID, + adminsCreatedReportActionID, + expenseCreatedReportActionID: workspaceChatCreatedReportActionID, + customUnitID, + customUnitRateID, + iouReportID, + memberData: JSON.stringify(memberData), + reportActionID: movedReportAction.reportActionID, + }; + + API.write('CreateWorkspaceFromIOUPayment', params, {optimisticData, successData, failureData}); return policyID; } From fa05286e2dd8549a960aa1759386482e5c23cece Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 2 Jan 2024 17:41:53 +0100 Subject: [PATCH 07/24] Fix typecheck and lint --- src/libs/DistanceRequestUtils.ts | 4 ++-- src/libs/PolicyUtils.ts | 6 +++--- src/libs/actions/Policy.ts | 23 +++++------------------ 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 477bfb4b61f9..95fee9b4d442 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -6,8 +6,8 @@ import * as CurrencyUtils from './CurrencyUtils'; import * as PolicyUtils from './PolicyUtils'; type DefaultMileageRate = { - rate: number; - currency: string; + rate?: number; + currency?: string; unit: Unit; }; diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index bc7ddb14b53e..d3d6e9cc4cf6 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -12,10 +12,10 @@ type UnitRate = {rate: number}; * Filter out the active policies, which will exclude policies with pending deletion * These are policies that we can use to create reports with in NewDot. */ -function getActivePolicies(policies: OnyxCollection): Policy[] | undefined { - return Object.values(policies ?? {}).filter( +function getActivePolicies(policies: OnyxCollection): Policy[] { + return Object.values(policies ?? {}).filter( (policy): policy is Policy => - policy !== null && policy && (policy.isPolicyExpenseChatEnabled || policy.areChatRoomsEnabled) && policy.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + policy !== null && !!policy && (!!policy.isPolicyExpenseChatEnabled || !!policy.areChatRoomsEnabled) && policy.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); } diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 8da49639ad9f..ee40ebadb4e1 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -17,19 +17,7 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import { - PersonalDetails, - PersonalDetailsList, - Policy, - PolicyMember, - PolicyTags, - RecentlyUsedCategories, - RecentlyUsedTags, - ReimbursementAccount, - Report, - ReportAction, - Transaction, -} from '@src/types/onyx'; +import {PersonalDetailsList, Policy, PolicyMember, PolicyTags, RecentlyUsedCategories, RecentlyUsedTags, ReimbursementAccount, Report, ReportAction, Transaction} from '@src/types/onyx'; import {Errors} from '@src/types/onyx/OnyxCommon'; import {CustomUnit, NewCustomUnit} from '@src/types/onyx/Policy'; import {isNotEmptyObject} from '@src/types/utils/EmptyObject'; @@ -47,8 +35,7 @@ Onyx.connect({ // More info: https://github.com/Expensify/App/issues/14260 const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); const policyReports = ReportUtils.getAllPolicyReports(policyID); - type cleanUpMergeKey = `${typeof ONYXKEYS.COLLECTION.REPORT}${string}`; - const cleanUpMergeQueries: Record>> = {}; + const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep>> = {}; const cleanUpSetQueries: OnyxCollection> = {}; policyReports.forEach((policyReport) => { if (!policyReport) { @@ -275,8 +262,8 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string /** * Is the user an admin of a free policy (aka workspace)? */ -function isAdminOfFreePolicy(policies: Record): boolean { - return Object.values(policies).some((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); +function isAdminOfFreePolicy(policies?: Record>): boolean { + return Object.values(policies ?? {}).some((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); } type AnnounceRoomMembers = { @@ -405,7 +392,7 @@ function removeMembers(accountIDs: number[], policyID: string) { // If we delete all these logins then we should clear the informative messages since they are no longer relevant. if (isNotEmptyObject(policy?.primaryLoginsInvited ?? {})) { // Take the current policy members and remove them optimistically - const policyMemberAccountIDs = Object.keys(allPolicyMembers?.[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`] ?? {}).map((accountID) => parseInt(accountID)); + const policyMemberAccountIDs = Object.keys(allPolicyMembers?.[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`] ?? {}).map((accountID) => parseInt(accountID, 10)); const remainingMemberAccountIDs = policyMemberAccountIDs.filter((e) => !accountIDs.includes(Number(e))); const remainingLogins: string[] = PersonalDetailsUtils.getLoginsByAccountIDs(remainingMemberAccountIDs); const invitedPrimaryToSecondaryLogins: Record = {}; From 18a8891b937cbea0e1dc4bf13e6afaf8f6cda301 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 2 Jan 2024 18:15:32 +0100 Subject: [PATCH 08/24] Final Policy adjustements --- src/libs/PersonalDetailsUtils.ts | 54 ++- src/libs/actions/Policy.ts | 543 ++++++++++++++++--------------- 2 files changed, 305 insertions(+), 292 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index a5a033797c7b..b0616b1a723a 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -1,7 +1,8 @@ -import Onyx, {OnyxEntry} from 'react-native-onyx'; +import Onyx, {OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import ONYXKEYS from '@src/ONYXKEYS'; import * as OnyxTypes from '@src/types/onyx'; import {PersonalDetails, PersonalDetailsList} from '@src/types/onyx'; +import {OnyxData} from '@src/types/onyx/Request'; import * as LocalePhoneNumber from './LocalePhoneNumber'; import * as Localize from './Localize'; import * as UserUtils from './UserUtils'; @@ -95,16 +96,15 @@ function getLoginsByAccountIDs(accountIDs: number[]): string[] { * @param accountIDs Array of user accountIDs * @returns Object with optimisticData, successData and failureData (object of personal details objects) */ -function getNewPersonalDetailsOnyxData(logins: string[], accountIDs: number[]) { - const optimisticData: PersonalDetailsList = {}; - const successData: PersonalDetailsList = {}; - const failureData: PersonalDetailsList = {}; +function getNewPersonalDetailsOnyxData(logins: string[], accountIDs: number[]): Required { + const personalDetailsNew: PersonalDetailsList = {}; + const personalDetailsCleanup: PersonalDetailsList = {}; logins.forEach((login, index) => { const accountID = accountIDs[index]; if (allPersonalDetails && Object.keys(allPersonalDetails?.[accountID] ?? {}).length === 0) { - optimisticData[accountID] = { + personalDetailsNew[accountID] = { login, accountID, avatar: UserUtils.getDefaultAvatarURL(accountID), @@ -115,32 +115,30 @@ function getNewPersonalDetailsOnyxData(logins: string[], accountIDs: number[]) { * Cleanup the optimistic user to ensure it does not permanently persist. * This is done to prevent duplicate entries (upon success) since the BE will return other personal details with the correct account IDs. */ - successData[accountID] = null; + personalDetailsCleanup[accountID] = null; } }); + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: personalDetailsNew, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + value: personalDetailsCleanup, + }, + ]; + return { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: optimisticData, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: successData, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - value: failureData, - }, - ], + optimisticData, + successData, + failureData: [], }; } diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index ee40ebadb4e1..e74d4a5f7119 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -35,8 +35,8 @@ Onyx.connect({ // More info: https://github.com/Expensify/App/issues/14260 const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); const policyReports = ReportUtils.getAllPolicyReports(policyID); - const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep>> = {}; - const cleanUpSetQueries: OnyxCollection> = {}; + const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; + const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; policyReports.forEach((policyReport) => { if (!policyReport) { return; @@ -47,7 +47,7 @@ Onyx.connect({ cleanUpSetQueries[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`] = null; }); Onyx.mergeCollection(ONYXKEYS.COLLECTION.REPORT, cleanUpMergeQueries); - Onyx.multiSet(cleanUpSetQueries as Partial<{string: null}>); + Onyx.multiSet(cleanUpSetQueries); delete allPolicies[key]; return; } @@ -173,40 +173,6 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string errors: null, }, }, - ...reports.map(({reportID}) => ({ - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, - statusNum: CONST.REPORT.STATUS.CLOSED, - hasDraft: false, - oldPolicyName: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? '', - }, - })), - - ...reports.map(({reportID}) => ({ - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`, - value: null, - })), - - // Add closed actions to all chat reports linked to this policy - ...reports.map(({reportID, ownerAccountID}) => { - // Announce & admin chats have FAKE owners, but workspace chats w/ users do have owners. - let emailClosingReport: string = CONST.POLICY.OWNER_EMAIL_FAKE; - if (!!ownerAccountID && ownerAccountID !== CONST.POLICY.OWNER_ACCOUNT_ID_FAKE) { - emailClosingReport = allPersonalDetails?.[ownerAccountID]?.login ?? ''; - } - const optimisticClosedReportAction = ReportUtils.buildOptimisticClosedReportAction(emailClosingReport, policyName, CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED); - const optimisticReportActions: Record = {}; - optimisticReportActions[optimisticClosedReportAction.reportActionID] = optimisticClosedReportAction as ReportAction; - return { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, - value: optimisticReportActions, - }; - }), - ...(!hasActiveFreePolicy(filteredPolicies) ? [ { @@ -220,18 +186,42 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string : []), ]; - // Restore the old report stateNum and statusNum - const failureData: OnyxUpdate[] = [ - ...reports.map(({reportID, stateNum, statusNum, hasDraft, oldPolicyName}) => ({ + reports.forEach(({reportID, ownerAccountID}) => { + optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { - stateNum, - statusNum, - hasDraft, - oldPolicyName, + stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, + statusNum: CONST.REPORT.STATUS.CLOSED, + hasDraft: false, + oldPolicyName: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.name ?? '', }, - })), + }); + + optimisticData.push({ + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${reportID}`, + value: null, + }); + + // Add closed actions to all chat reports linked to this policy + // Announce & admin chats have FAKE owners, but workspace chats w/ users do have owners. + let emailClosingReport: string = CONST.POLICY.OWNER_EMAIL_FAKE; + if (!!ownerAccountID && ownerAccountID !== CONST.POLICY.OWNER_ACCOUNT_ID_FAKE) { + emailClosingReport = allPersonalDetails?.[ownerAccountID]?.login ?? ''; + } + const optimisticClosedReportAction = ReportUtils.buildOptimisticClosedReportAction(emailClosingReport, policyName, CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED); + const optimisticReportActions: Record = {}; + optimisticReportActions[optimisticClosedReportAction.reportActionID] = optimisticClosedReportAction as ReportAction; + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`, + value: optimisticReportActions, + }); + }); + + // Restore the old report stateNum and statusNum + const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, @@ -241,6 +231,19 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string }, ]; + reports.forEach(({reportID, stateNum, statusNum, hasDraft, oldPolicyName}) => { + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + stateNum, + statusNum, + hasDraft, + oldPolicyName, + }, + }); + }); + // We don't need success data since the push notification will update // the onyxData for all connected clients. const successData: OnyxUpdate[] = []; @@ -370,7 +373,11 @@ function removeMembers(accountIDs: number[], policyID: string) { key: membersListKey, value: optimisticMembersState, }, - ...workspaceChats.map((report) => ({ + ...announceRoomMembers.onyxOptimisticData, + ]; + + workspaceChats.forEach((report) => { + optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, value: { @@ -379,14 +386,15 @@ function removeMembers(accountIDs: number[], policyID: string) { oldPolicyName: policy.name, hasDraft: false, }, - })), - ...optimisticClosedReportActions.map((reportAction, index) => ({ + }); + }); + optimisticClosedReportActions.forEach((reportAction, index) => { + optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats?.[index]?.reportID}`, value: {[reportAction.reportActionID]: reportAction as ReportAction}, - })), - ...announceRoomMembers.onyxOptimisticData, - ]; + }); + }); // If the policy has primaryLoginsInvited, then it displays informative messages on the members page about which primary logins were added by secondary logins. // If we delete all these logins then we should clear the informative messages since they are no longer relevant. @@ -428,7 +436,11 @@ function removeMembers(accountIDs: number[], policyID: string) { key: membersListKey, value: failureMembersState, }, - ...filteredWorkspaceChats.map(({reportID, stateNum, statusNum, hasDraft, oldPolicyName = null}) => ({ + ...announceRoomMembers.onyxFailureData, + ]; + + filteredWorkspaceChats.forEach(({reportID, stateNum, statusNum, hasDraft, oldPolicyName = null}) => { + failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, value: { @@ -437,14 +449,15 @@ function removeMembers(accountIDs: number[], policyID: string) { hasDraft, oldPolicyName, }, - })), - ...optimisticClosedReportActions.map((reportAction, index) => ({ + }); + }); + optimisticClosedReportActions.forEach((reportAction, index) => { + failureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChats?.[index]?.reportID}`, value: {[reportAction.reportActionID]: null}, - })), - ...announceRoomMembers.onyxFailureData, - ]; + }); + }); type DeleteMembersFromWorkspaceParams = { emailList: string; @@ -594,7 +607,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record // Convert to object with each key containing {pendingAction: ‘add’} value: optimisticMembersState, }, - ...(newPersonalDetailsOnyxData.optimisticData as OnyxUpdate[]), + ...newPersonalDetailsOnyxData.optimisticData, ...membersChats.onyxOptimisticData, ...announceRoomMembers.onyxOptimisticData, ]; @@ -617,7 +630,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record return {...accountIDsWithClearedPendingAction, [accountID]: value}; }, {}), }, - ...(newPersonalDetailsOnyxData.successData as OnyxUpdate[]), + ...newPersonalDetailsOnyxData.successData, ...membersChats.onyxSuccessData, ]; @@ -630,7 +643,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record // need to remove the members since that is handled by onClose of OfflineWithFeedback. value: failureMembersState, }, - ...(newPersonalDetailsOnyxData.failureData as OnyxUpdate[]), + ...newPersonalDetailsOnyxData.failureData, ...membersChats.onyxFailureData, ...announceRoomMembers.onyxFailureData, ]; @@ -1213,6 +1226,192 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName expenseCreatedReportActionID, } = ReportUtils.buildOptimisticWorkspaceChats(policyID, workspaceName); + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + id: policyID, + type: CONST.POLICY.TYPE.FREE, + name: workspaceName, + role: CONST.POLICY.ROLE.ADMIN, + owner: sessionEmail, + isPolicyExpenseChatEnabled: true, + outputCurrency, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + customUnits, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + value: { + [sessionAccountID]: { + role: CONST.POLICY.ROLE.ADMIN, + errors: {}, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...announceChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: announceReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...adminsChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: adminsReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + }, + ...expenseChatData, + }, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, + value: expenseReportActionData, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS}${policyID}`, + value: null, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: {pendingAction: null}, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: { + [Object.keys(announceChatData)[0]]: { + pendingAction: null, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: { + [Object.keys(adminsChatData)[0]]: { + pendingAction: null, + }, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, + value: { + pendingFields: { + addWorkspaceRoom: null, + }, + pendingAction: null, + }, + }, + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, + value: { + [Object.keys(expenseChatData)[0]]: { + pendingAction: null, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, + value: null, + }, + ]; + type CreateWorkspaceParams = { policyID: string; announceChatReportID: string; @@ -1245,191 +1444,7 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName customUnitRateID, }; - API.write('CreateWorkspace', params, { - optimisticData: [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - id: policyID, - type: CONST.POLICY.TYPE.FREE, - name: workspaceName, - role: CONST.POLICY.ROLE.ADMIN, - owner: sessionEmail, - isPolicyExpenseChatEnabled: true, - outputCurrency, - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - customUnits, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, - value: { - [sessionAccountID]: { - role: CONST.POLICY.ROLE.ADMIN, - errors: {}, - }, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...announceChatData, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: announceReportActionData, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...adminsChatData, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: adminsReportActionData, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, - }, - ...expenseChatData, - }, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, - value: expenseReportActionData, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_DRAFTS}${policyID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS}${policyID}`, - value: null, - }, - ], - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: {pendingAction: null}, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: { - [Object.keys(announceChatData)[0]]: { - pendingAction: null, - }, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: { - [Object.keys(adminsChatData)[0]]: { - pendingAction: null, - }, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, - value: { - pendingFields: { - addWorkspaceRoom: null, - }, - pendingAction: null, - }, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, - value: { - [Object.keys(expenseChatData)[0]]: { - pendingAction: null, - }, - }, - }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT}${expenseChatReportID}`, - value: null, - }, - { - onyxMethod: Onyx.METHOD.SET, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${expenseChatReportID}`, - value: null, - }, - ], - }); + API.write('CreateWorkspace', params, {optimisticData, successData, failureData}); return adminsChatReportID; } @@ -1439,26 +1454,26 @@ function openWorkspaceReimburseView(policyID: string) { Log.warn('openWorkspaceReimburseView invalid params', {policyID}); return; } - const onyxData = { - successData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - value: { - isLoading: false, - }, + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + value: { + isLoading: false, }, - ], - failureData: [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - value: { - isLoading: false, - }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + value: { + isLoading: false, }, - ], - }; + }, + ]; type OpenWorkspaceReimburseViewParams = { policyID: string; @@ -1466,7 +1481,7 @@ function openWorkspaceReimburseView(policyID: string) { const params: OpenWorkspaceReimburseViewParams = {policyID}; - API.read('OpenWorkspaceReimburseView', params, onyxData); + API.read('OpenWorkspaceReimburseView', params, {successData, failureData}); } function openWorkspaceMembersPage(policyID: string, clientMemberEmails: string[]) { From 55648a3170097b485ac570d58bae0317f44836bc Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 2 Jan 2024 18:34:21 +0100 Subject: [PATCH 09/24] Add missing return types and change Record to stricter types --- src/libs/actions/Policy.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index e74d4a5f7119..7e88c419abe7 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -277,7 +277,7 @@ type AnnounceRoomMembers = { /** * Build optimistic data for adding members to the announcement room */ -function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[]) { +function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[]): AnnounceRoomMembers { const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); const announceRoomMembers: AnnounceRoomMembers = { onyxOptimisticData: [], @@ -312,7 +312,7 @@ type OptimisticAnnounceRoomMembers = { /** * Build optimistic data for removing users from the announcement room */ -function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: number[]) { +function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: number[]): OptimisticAnnounceRoomMembers { const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); const announceRoomMembers: OptimisticAnnounceRoomMembers = { onyxOptimisticData: [], @@ -358,9 +358,9 @@ function removeMembers(accountIDs: number[], policyID: string) { const announceRoomMembers = removeOptimisticAnnounceRoomMembers(policyID, accountIDs); - const optimisticMembersState: Record> = {}; - const successMembersState: Record | null> = {}; - const failureMembersState: Record> = {}; + const optimisticMembersState: OnyxCollection = {}; + const successMembersState: OnyxCollection = {}; + const failureMembersState: OnyxCollection = {}; accountIDs.forEach((accountID) => { optimisticMembersState[accountID] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}; successMembersState[accountID] = null; @@ -490,7 +490,7 @@ type WorkspaceMembersChats = { * * @returns - object with onyxSuccessData, onyxOptimisticData, and optimisticReportIDs (map login to reportID) */ -function createPolicyExpenseChats(policyID: string, invitedEmailsToAccountIDs: Record, hasOutstandingChildRequest = false) { +function createPolicyExpenseChats(policyID: string, invitedEmailsToAccountIDs: Record, hasOutstandingChildRequest = false): WorkspaceMembersChats { const workspaceMembersChats: WorkspaceMembersChats = { onyxSuccessData: [], onyxOptimisticData: [], @@ -590,8 +590,8 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record // create onyx data for policy expense chats for each new member const membersChats = createPolicyExpenseChats(policyID, invitedEmailsToAccountIDs); - const optimisticMembersState: Record> = {}; - const failureMembersState: Record> = {}; + const optimisticMembersState: OnyxCollection = {}; + const failureMembersState: OnyxCollection = {}; accountIDs.forEach((accountID) => { optimisticMembersState[accountID] = {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD}; failureMembersState[accountID] = { From fa42c080c80c86ce040653aff55b5b86409717b9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 2 Jan 2024 18:37:38 +0100 Subject: [PATCH 10/24] Make isHarvestingEnabled optional --- src/types/onyx/Policy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index ac9386775eb2..80660fec4517 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -91,7 +91,7 @@ type Policy = { autoReportingFrequency?: ValueOf; /** Whether the scheduled submit is enabled */ - isHarvestingEnabled: boolean; + isHarvestingEnabled?: boolean; /** The accountID of manager who the employee submits their expenses to on paid policies */ submitsTo?: number; From 4f1adec52bfc9a78a56186e2990719b7ada96db5 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:59:41 +0100 Subject: [PATCH 11/24] add suggested review changes and refactor --- src/libs/actions/Policy.ts | 102 ++++++++++++++++++++----------------- src/types/onyx/Policy.ts | 5 ++ 2 files changed, 61 insertions(+), 46 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index b336a84be5ac..5ff6363b17aa 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -22,6 +22,35 @@ import {Errors} from '@src/types/onyx/OnyxCommon'; import {CustomUnit, NewCustomUnit} from '@src/types/onyx/Policy'; import {isNotEmptyObject} from '@src/types/utils/EmptyObject'; +type AnnounceRoomMembers = { + onyxOptimisticData: OnyxUpdate[]; + onyxFailureData: OnyxUpdate[]; +}; + +type ReportCreationData = Record< + string, + { + reportID: string; + reportActionID?: string; + } +>; + +type WorkspaceMembersChats = { + onyxSuccessData: OnyxUpdate[]; + onyxOptimisticData: OnyxUpdate[]; + onyxFailureData: OnyxUpdate[]; + reportCreationData: ReportCreationData; +}; + +type OptimisticCustomUnits = { + customUnits: Record; + customUnitID: string; + customUnitRateID: string; + outputCurrency: string; +}; + +type PoliciesRecord = Record>; + const allPolicies: OnyxCollection = {}; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY, @@ -35,8 +64,12 @@ Onyx.connect({ // More info: https://github.com/Expensify/App/issues/14260 const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); const policyReports = ReportUtils.getAllPolicyReports(policyID); - const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; - const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; + + type MergeQueries = Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep>; + type SetQueries = Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null>; + + const cleanUpMergeQueries: MergeQueries = {}; + const cleanUpSetQueries: SetQueries = {}; policyReports.forEach((policyReport) => { if (!policyReport) { return; @@ -77,7 +110,7 @@ Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { sessionEmail = val?.email ?? ''; - sessionAccountID = val?.accountID ?? 0; + sessionAccountID = val?.accountID ?? -1; }, }); @@ -131,7 +164,7 @@ function updateLastAccessedWorkspace(policyID: OnyxEntry) { /** * Check if the user has any active free policies (aka workspaces) */ -function hasActiveFreePolicy(policies: Array> | Record>): boolean { +function hasActiveFreePolicy(policies: Array> | PoliciesRecord): boolean { const adminFreePolicies = Object.values(policies).filter((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); if (adminFreePolicies.length === 0) { @@ -265,15 +298,10 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string /** * Is the user an admin of a free policy (aka workspace)? */ -function isAdminOfFreePolicy(policies?: Record>): boolean { +function isAdminOfFreePolicy(policies?: PoliciesRecord): boolean { return Object.values(policies ?? {}).some((policy) => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN); } -type AnnounceRoomMembers = { - onyxOptimisticData: OnyxUpdate[]; - onyxFailureData: OnyxUpdate[]; -}; - /** * Build optimistic data for adding members to the announcement room */ @@ -308,17 +336,12 @@ function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[] return announceRoomMembers; } -type OptimisticAnnounceRoomMembers = { - onyxOptimisticData: OnyxUpdate[]; - onyxFailureData: OnyxUpdate[]; -}; - /** * Build optimistic data for removing users from the announcement room */ -function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: number[]): OptimisticAnnounceRoomMembers { +function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: number[]): AnnounceRoomMembers { const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); - const announceRoomMembers: OptimisticAnnounceRoomMembers = { + const announceRoomMembers: AnnounceRoomMembers = { onyxOptimisticData: [], onyxFailureData: [], }; @@ -408,8 +431,8 @@ function removeMembers(accountIDs: number[], policyID: string) { // If we delete all these logins then we should clear the informative messages since they are no longer relevant. if (isNotEmptyObject(policy?.primaryLoginsInvited ?? {})) { // Take the current policy members and remove them optimistically - const policyMemberAccountIDs = Object.keys(allPolicyMembers?.[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`] ?? {}).map((accountID) => parseInt(accountID, 10)); - const remainingMemberAccountIDs = policyMemberAccountIDs.filter((e) => !accountIDs.includes(Number(e))); + const policyMemberAccountIDs = Object.keys(allPolicyMembers?.[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`] ?? {}).map((accountID) => Number(accountID)); + const remainingMemberAccountIDs = policyMemberAccountIDs.filter((accountID) => !accountIDs.includes(accountID)); const remainingLogins: string[] = PersonalDetailsUtils.getLoginsByAccountIDs(remainingMemberAccountIDs); const invitedPrimaryToSecondaryLogins: Record = {}; @@ -418,7 +441,7 @@ function removeMembers(accountIDs: number[], policyID: string) { } // Then, if no remaining members exist that were invited by a secondary login, clear the informative messages - if (!remainingLogins.some((remainingLogin) => Boolean(invitedPrimaryToSecondaryLogins[remainingLogin]))) { + if (!remainingLogins.some((remainingLogin) => !!invitedPrimaryToSecondaryLogins[remainingLogin])) { optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: membersListKey, @@ -437,7 +460,7 @@ function removeMembers(accountIDs: number[], policyID: string) { }, ]; - const filteredWorkspaceChats = workspaceChats.filter((e): e is Report => e !== null); + const filteredWorkspaceChats = workspaceChats.filter((report): report is Report => report !== null); const failureData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -480,19 +503,6 @@ function removeMembers(accountIDs: number[], policyID: string) { API.write('DeleteMembersFromWorkspace', params, {optimisticData, successData, failureData}); } -type WorkspaceMembersChats = { - onyxSuccessData: OnyxUpdate[]; - onyxOptimisticData: OnyxUpdate[]; - onyxFailureData: OnyxUpdate[]; - reportCreationData: Record< - string, - { - reportID: string; - reportActionID?: string; - } - >; -}; - /** * Optimistically create a chat for each member of the workspace, creates both optimistic and success data for onyx. * @@ -1094,7 +1104,7 @@ function generateDefaultWorkspaceName(email = ''): string { const username = emailParts[0]; const domain = emailParts[1]; - if ((PUBLIC_DOMAINS as readonly string[]).includes(domain.toLowerCase())) { + if (PUBLIC_DOMAINS.some((publicDomain) => publicDomain === domain.toLowerCase())) { defaultWorkspaceName = `${Str.UCFirst(username)}'s Workspace`; } else { defaultWorkspaceName = `${Str.UCFirst(domain.split('.')[0])}'s Workspace`; @@ -1112,7 +1122,7 @@ function generateDefaultWorkspaceName(email = ''): string { const numberRegEx = new RegExp(`${escapeRegExp(defaultWorkspaceName)} ?(\\d*)`, 'i'); const parsedWorkspaceNumbers = Object.values(allPolicies ?? {}) .filter((policy) => policy?.name && numberRegEx.test(policy.name)) - .map((policy) => parseInt(numberRegEx.exec(policy?.name ?? '')?.[1] ?? '1', 10)); // parse the number at the end + .map((policy) => Number(numberRegEx.exec(policy?.name ?? '')?.[1] ?? '1')); // parse the number at the end const lastWorkspaceNumber = Math.max(...parsedWorkspaceNumbers); return lastWorkspaceNumber !== -Infinity ? `${defaultWorkspaceName} ${lastWorkspaceNumber + 1}` : defaultWorkspaceName; } @@ -1131,7 +1141,7 @@ function generateCustomUnitID(): string { return NumberUtils.generateHexadecimalValue(13); } -function buildOptimisticCustomUnits() { +function buildOptimisticCustomUnits(): OptimisticCustomUnits { const currency = allPersonalDetails?.[sessionAccountID]?.localCurrencyCode ?? CONST.CURRENCY.USD; const customUnitID = generateCustomUnitID(); const customUnitRateID = generateCustomUnitID(); @@ -1165,10 +1175,10 @@ function buildOptimisticCustomUnits() { /** * Optimistically creates a Policy Draft for a new workspace * - * @param [policyOwnerEmail] Optional, the email of the account to make the owner of the policy - * @param [policyName] Optional, custom policy name we will use for created workspace - * @param [policyID] Optional, custom policy id we will use for created workspace - * @param [makeMeAdmin] Optional, leave the calling account as an admin on the policy + * @param [policyOwnerEmail] the email of the account to make the owner of the policy + * @param [policyName] custom policy name we will use for created workspace + * @param [policyID] custom policy id we will use for created workspace + * @param [makeMeAdmin] leave the calling account as an admin on the policy */ function createDraftInitialWorkspace(policyOwnerEmail = '', policyName = '', policyID = generatePolicyID(), makeMeAdmin = false) { const workspaceName = policyName || generateDefaultWorkspaceName(policyOwnerEmail); @@ -1209,10 +1219,10 @@ function createDraftInitialWorkspace(policyOwnerEmail = '', policyName = '', pol /** * Optimistically creates a new workspace and default workspace chats * - * @param [policyOwnerEmail] Optional, the email of the account to make the owner of the policy - * @param [makeMeAdmin] Optional, leave the calling account as an admin on the policy - * @param [policyName] Optional, custom policy name we will use for created workspace - * @param [policyID] Optional, custom policy id we will use for created workspace + * @param [policyOwnerEmail] the email of the account to make the owner of the policy + * @param [makeMeAdmin] leave the calling account as an admin on the policy + * @param [policyName] custom policy name we will use for created workspace + * @param [policyID] custom policy id we will use for created workspace */ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName = '', policyID = generatePolicyID()): string { const workspaceName = policyName || generateDefaultWorkspaceName(policyOwnerEmail); @@ -1570,7 +1580,7 @@ function buildOptimisticPolicyRecentlyUsedCategories(policyID: string, category: return lodashUnion([category], policyRecentlyUsedCategories); } -function buildOptimisticPolicyRecentlyUsedTags(policyID: string, tag: string) { +function buildOptimisticPolicyRecentlyUsedTags(policyID: string, tag: string): RecentlyUsedTags { if (!policyID || !tag) { return {}; } diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 80660fec4517..8586340bca9b 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -99,14 +99,19 @@ type Policy = { /** The employee list of the policy */ employeeList?: []; + /** Whether to leave the calling account as an admin on the policy */ makeMeAdmin?: boolean; + /** Pending fields for the policy */ pendingFields?: Record; + /** Original file name which is used for the policy avatar */ originalFileName?: string; + /** Alert message for the policy */ alertMessage?: string; + /** Informative messages about which policy members were added with primary logins when invited with their secondary login */ primaryLoginsInvited?: Record; }; From bd95ad85570066c1b05458c38e3e95c1ca3e7f69 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:09:18 +0100 Subject: [PATCH 12/24] fix correct type imports --- src/libs/actions/Policy.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 5ff6363b17aa..ab97962b1bfe 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -3,8 +3,9 @@ import Str from 'expensify-common/lib/str'; import {escapeRegExp} from 'lodash'; import lodashClone from 'lodash/clone'; import lodashUnion from 'lodash/union'; -import Onyx, {OnyxCollection, OnyxUpdate} from 'react-native-onyx'; -import {NullishDeep, OnyxEntry} from 'react-native-onyx/lib/types'; +import type {OnyxCollection, OnyxUpdate} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; +import type {NullishDeep, OnyxEntry} from 'react-native-onyx/lib/types'; import * as API from '@libs/API'; import DateUtils from '@libs/DateUtils'; import * as ErrorUtils from '@libs/ErrorUtils'; @@ -17,9 +18,9 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import {PersonalDetailsList, Policy, PolicyMember, PolicyTags, RecentlyUsedCategories, RecentlyUsedTags, ReimbursementAccount, Report, ReportAction, Transaction} from '@src/types/onyx'; -import {Errors} from '@src/types/onyx/OnyxCommon'; -import {CustomUnit, NewCustomUnit} from '@src/types/onyx/Policy'; +import type {PersonalDetailsList, Policy, PolicyMember, PolicyTags, RecentlyUsedCategories, RecentlyUsedTags, ReimbursementAccount, Report, ReportAction, Transaction} from '@src/types/onyx'; +import type {Errors} from '@src/types/onyx/OnyxCommon'; +import type {CustomUnit, NewCustomUnit} from '@src/types/onyx/Policy'; import {isNotEmptyObject} from '@src/types/utils/EmptyObject'; type AnnounceRoomMembers = { From b0a9ed9b0e8fe29410c0babbeb407da69e6e9c4e Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Tue, 9 Jan 2024 08:10:22 +0100 Subject: [PATCH 13/24] implement fix and review suggestions --- src/libs/PolicyUtils.ts | 3 +-- src/libs/actions/Policy.ts | 4 ++-- src/types/onyx/Policy.ts | 12 ++++++------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index a5d723c7e211..d0ef0c8831ab 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -15,8 +15,7 @@ type UnitRate = {rate: number}; */ function getActivePolicies(policies: OnyxCollection): Policy[] { return Object.values(policies ?? {}).filter( - (policy): policy is Policy => - policy !== null && !!policy && (!!policy.isPolicyExpenseChatEnabled || !!policy.areChatRoomsEnabled) && policy.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + (policy): policy is Policy => (!!policy?.isPolicyExpenseChatEnabled || !!policy?.areChatRoomsEnabled) && policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); } diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 82ece226ae68..559b61f4c01a 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -21,7 +21,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Policy, PolicyMember, PolicyTags, RecentlyUsedCategories, RecentlyUsedTags, ReimbursementAccount, Report, ReportAction, Transaction} from '@src/types/onyx'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import type {CustomUnit, NewCustomUnit} from '@src/types/onyx/Policy'; -import {isNotEmptyObject} from '@src/types/utils/EmptyObject'; +import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; type AnnounceRoomMembers = { onyxOptimisticData: OnyxUpdate[]; @@ -1122,7 +1122,7 @@ function generateDefaultWorkspaceName(email = ''): string { defaultWorkspaceName = 'My Group Workspace'; } - if (!isNotEmptyObject(allPolicies ?? {})) { + if (isEmptyObject(allPolicies)) { return defaultWorkspaceName; } diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 758f1ea8d72b..ab70218bb985 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -13,12 +13,14 @@ type Rate = { pendingAction?: string; }; +type Attributes = { + unit: Unit; +}; + type NewCustomUnit = { name?: string; customUnitID?: string; - attributes: { - unit: Unit; - }; + attributes: Attributes; rates?: Rate; pendingAction?: string; errors?: OnyxCommon.Errors; @@ -27,9 +29,7 @@ type NewCustomUnit = { type CustomUnit = { name?: string; customUnitID?: string; - attributes: { - unit: Unit; - }; + attributes: Attributes; rates?: Record; pendingAction?: string; errors?: OnyxCommon.Errors; From 97e9b1130c4f561b5c616f4cdba498a35d3a0252 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Tue, 9 Jan 2024 11:14:16 +0100 Subject: [PATCH 14/24] add minor code refactors --- src/libs/actions/Policy.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index bd099afaf1a1..675e1545e546 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -832,10 +832,6 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) const distanceUnit = Object.values(policy?.customUnits ?? {}).find((unit) => unit.name === CONST.CUSTOM_UNITS.NAME_DISTANCE); const distanceRate = Object.values(distanceUnit?.rates ?? {}).find((rate) => rate.name === CONST.CUSTOM_UNITS.DEFAULT_RATE); - if (!distanceUnit?.customUnitID || !distanceRate?.customUnitRateID) { - return; - } - const optimisticData: OnyxUpdate[] = [ { // We use SET because it's faster than merge and avoids a race condition when setting the currency and navigating the user to the Bank account page in confirmCurrencyChangeAndHideModal @@ -854,10 +850,10 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) }, name, outputCurrency: currency, - ...(distanceUnit + ...(distanceUnit?.customUnitID && distanceRate?.customUnitRateID ? { customUnits: { - [distanceUnit.customUnitID]: { + [distanceUnit?.customUnitID]: { ...distanceUnit, rates: { [distanceRate?.customUnitRateID]: { @@ -894,7 +890,7 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) errorFields: { generalSettings: ErrorUtils.getMicroSecondOnyxError('workspace.editor.genericFailureMessage'), }, - ...(distanceUnit + ...(distanceUnit?.customUnitID ? { customUnits: { [distanceUnit.customUnitID]: distanceUnit, @@ -1037,7 +1033,8 @@ function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: C ]; const newCustomUnitParam = lodashClone(newCustomUnit); - newCustomUnitParam.rates = {...newCustomUnitParam.rates, pendingAction: undefined, errors: undefined}; + const {pendingAction, errors, ...newRates} = newCustomUnitParam.rates ?? {}; + newCustomUnitParam.rates = newRates; type UpdateWorkspaceCustomUnitAndRate = { policyID: string; From d9ef0b20e0bd314b4824592a84434331d21cf963 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Wed, 10 Jan 2024 09:50:21 +0100 Subject: [PATCH 15/24] fix if statement in unit and rate update --- src/libs/actions/Policy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 675e1545e546..2f752053c76f 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -966,7 +966,7 @@ function hideWorkspaceAlertMessage(policyID: string) { } function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: NewCustomUnit, lastModified: number) { - if (!currentCustomUnit.customUnitID || !newCustomUnit?.customUnitID || !newCustomUnit.rates?.customUnitRateID || !currentCustomUnit.rates?.customUnitRateID) { + if (!currentCustomUnit.customUnitID || !newCustomUnit?.customUnitID || !newCustomUnit.rates?.customUnitRateID) { return; } From ef4c05594d67073fc755d058776bff7653ac6604 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 11 Jan 2024 12:16:48 +0100 Subject: [PATCH 16/24] implement finallyData and fix merge errors --- src/libs/PersonalDetailsUtils.ts | 7 +++-- src/libs/actions/Policy.ts | 46 +++++++++++++------------------- src/libs/actions/Report.ts | 7 +++-- src/types/onyx/Policy.ts | 2 +- 4 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index 9ba1253d899c..2fb9ff55968f 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -92,7 +92,7 @@ function getLoginsByAccountIDs(accountIDs: number[]): string[] { * @param accountIDs Array of user accountIDs * @returns Object with optimisticData, successData and failureData (object of personal details objects) */ -function getNewPersonalDetailsOnyxData(logins: string[], accountIDs: number[]): Required { +function getNewPersonalDetailsOnyxData(logins: string[], accountIDs: number[]): OnyxData { const personalDetailsNew: PersonalDetailsList = {}; const personalDetailsCleanup: PersonalDetailsList = {}; @@ -123,7 +123,7 @@ function getNewPersonalDetailsOnyxData(logins: string[], accountIDs: number[]): }, ]; - const successData: OnyxUpdate[] = [ + const finallyData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS_LIST, @@ -133,8 +133,7 @@ function getNewPersonalDetailsOnyxData(logins: string[], accountIDs: number[]): return { optimisticData, - successData, - failureData: [], + finallyData, }; } diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 2f752053c76f..d37df12b47b2 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -24,7 +24,7 @@ import type {Errors} from '@src/types/onyx/OnyxCommon'; import type {CustomUnit, NewCustomUnit} from '@src/types/onyx/Policy'; import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; -type AnnounceRoomMembers = { +type AnnounceRoomMembersOnyxData = { onyxOptimisticData: OnyxUpdate[]; onyxFailureData: OnyxUpdate[]; }; @@ -66,12 +66,8 @@ Onyx.connect({ // More info: https://github.com/Expensify/App/issues/14260 const policyID = key.replace(ONYXKEYS.COLLECTION.POLICY, ''); const policyReports = ReportUtils.getAllPolicyReports(policyID); - - type MergeQueries = Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep>; - type SetQueries = Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null>; - - const cleanUpMergeQueries: MergeQueries = {}; - const cleanUpSetQueries: SetQueries = {}; + const cleanUpMergeQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT}${string}`, NullishDeep> = {}; + const cleanUpSetQueries: Record<`${typeof ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${string}` | `${typeof ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${string}`, null> = {}; policyReports.forEach((policyReport) => { if (!policyReport) { return; @@ -279,17 +275,13 @@ function deleteWorkspace(policyID: string, reports: Report[], policyName: string }); }); - // We don't need success data since the push notification will update - // the onyxData for all connected clients. - const successData: OnyxUpdate[] = []; - type DeleteWorkspaceParams = { policyID: string; }; const params: DeleteWorkspaceParams = {policyID}; - API.write('DeleteWorkspace', params, {optimisticData, successData, failureData}); + API.write('DeleteWorkspace', params, {optimisticData, failureData}); // Reset the lastAccessedWorkspacePolicyID if (policyID === lastAccessedWorkspacePolicyID) { @@ -307,9 +299,9 @@ function isAdminOfFreePolicy(policies?: PoliciesRecord): boolean { /** * Build optimistic data for adding members to the announcement room */ -function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[]): AnnounceRoomMembers { +function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[]): AnnounceRoomMembersOnyxData { const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); - const announceRoomMembers: AnnounceRoomMembers = { + const announceRoomMembers: AnnounceRoomMembersOnyxData = { onyxOptimisticData: [], onyxFailureData: [], }; @@ -346,9 +338,9 @@ function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[] /** * Build optimistic data for removing users from the announcement room */ -function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: number[]): AnnounceRoomMembers { +function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: number[]): AnnounceRoomMembersOnyxData { const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); - const announceRoomMembers: AnnounceRoomMembers = { + const announceRoomMembers: AnnounceRoomMembersOnyxData = { onyxOptimisticData: [], onyxFailureData: [], }; @@ -610,7 +602,13 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}` as const; const logins = Object.keys(invitedEmailsToAccountIDs).map((memberLogin) => OptionsListUtils.addSMSDomainIfPhoneNumber(memberLogin)); const accountIDs = Object.values(invitedEmailsToAccountIDs); - const newPersonalDetailsOnyxData = PersonalDetailsUtils.getNewPersonalDetailsOnyxData(logins, accountIDs); + + type PersonalDetailsOnyxData = { + optimisticData: OnyxUpdate[]; + finallyData: OnyxUpdate[]; + }; + + const newPersonalDetailsOnyxData = PersonalDetailsUtils.getNewPersonalDetailsOnyxData(logins, accountIDs) as PersonalDetailsOnyxData; const announceRoomMembers = buildAnnounceRoomMembersOnyxData(policyID, accountIDs); @@ -657,7 +655,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record return {...accountIDsWithClearedPendingAction, [accountID]: value}; }, {}), }, - ...newPersonalDetailsOnyxData.successData, + ...newPersonalDetailsOnyxData.finallyData, ...membersChats.onyxSuccessData, ]; @@ -670,7 +668,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record // need to remove the members since that is handled by onClose of OfflineWithFeedback. value: failureMembersState, }, - ...newPersonalDetailsOnyxData.failureData, + ...newPersonalDetailsOnyxData.finallyData, ...membersChats.onyxFailureData, ...announceRoomMembers.onyxFailureData, ]; @@ -713,7 +711,7 @@ function updateWorkspaceAvatar(policyID: string, file: File) { }, }, ]; - const successData: OnyxUpdate[] = [ + const finallyData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -730,9 +728,6 @@ function updateWorkspaceAvatar(policyID: string, file: File) { key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { avatar: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]?.avatar, - pendingFields: { - avatar: null, - }, }, }, ]; @@ -747,7 +742,7 @@ function updateWorkspaceAvatar(policyID: string, file: File) { file, }; - API.write('UpdateWorkspaceAvatar', params, {optimisticData, successData, failureData}); + API.write('UpdateWorkspaceAvatar', params, {optimisticData, finallyData, failureData}); } /** @@ -920,9 +915,6 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) }); } -/** - * @param policyID The id of the workspace / policy - */ function clearWorkspaceGeneralSettingsErrors(policyID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, { errorFields: { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 19f6fa3f36b4..0b305b81a7b6 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2179,8 +2179,7 @@ function inviteToRoom(reportID: string, inviteeEmailsToAccountIDs: Record OptionsListUtils.addSMSDomainIfPhoneNumber(memberLogin)); @@ -2198,7 +2197,7 @@ function inviteToRoom(reportID: string, inviteeEmailsToAccountIDs: Record; /** Whether chat rooms can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ - areChatRoomsEnabled: boolean; + areChatRoomsEnabled?: boolean; /** Whether policy expense chats can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ isPolicyExpenseChatEnabled: boolean; From 5ba57d1bc5e178ddb9c0d5fddbb6e7de32cd88b1 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 11 Jan 2024 12:45:12 +0100 Subject: [PATCH 17/24] include required areChatRoomsEnabled in policy creation --- src/libs/actions/Policy.ts | 3 +++ src/types/onyx/Policy.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index d37df12b47b2..96d42a2900ca 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1192,6 +1192,7 @@ function createDraftInitialWorkspace(policyOwnerEmail = '', policyName = '', pol isPolicyExpenseChatEnabled: true, outputCurrency, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + areChatRoomsEnabled: true, customUnits, makeMeAdmin, }, @@ -1252,6 +1253,7 @@ function createWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policyName isPolicyExpenseChatEnabled: true, outputCurrency, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + areChatRoomsEnabled: true, customUnits, }, }, @@ -1647,6 +1649,7 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { // Setting the currency to USD as we can only add the VBBA for this policy currency right now outputCurrency: CONST.CURRENCY.USD, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD, + areChatRoomsEnabled: true, customUnits, }; diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index ab70218bb985..086b47abbe45 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -79,7 +79,7 @@ type Policy = { customUnits?: Record; /** Whether chat rooms can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ - areChatRoomsEnabled?: boolean; + areChatRoomsEnabled: boolean; /** Whether policy expense chats can be created and used on this policy. Enabled manually by CQ/JS snippet. Always true for free policies. */ isPolicyExpenseChatEnabled: boolean; From 58ac053b4784a87cf2427da1d0a070189aa3f6ec Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:20:41 +0100 Subject: [PATCH 18/24] change properties from optional to required for CustomUnit --- src/types/onyx/Policy.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 086b47abbe45..6fd82bf1feff 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -18,19 +18,19 @@ type Attributes = { }; type NewCustomUnit = { - name?: string; - customUnitID?: string; + name: string; + customUnitID: string; attributes: Attributes; - rates?: Rate; + rates: Rate; pendingAction?: string; errors?: OnyxCommon.Errors; }; type CustomUnit = { - name?: string; - customUnitID?: string; + name: string; + customUnitID: string; attributes: Attributes; - rates?: Record; + rates: Record; pendingAction?: string; errors?: OnyxCommon.Errors; }; From ce0205df498fe30fea0810a877cedcbb33dd7556 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:32:13 +0100 Subject: [PATCH 19/24] remove NewCustomUnit type and refactor rest accordingly --- src/libs/actions/Policy.ts | 10 +++++----- src/types/onyx/Policy.ts | 13 ++----------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 96d42a2900ca..80f1d87d0b6c 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -21,7 +21,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Policy, PolicyMember, PolicyTags, RecentlyUsedCategories, RecentlyUsedTags, ReimbursementAccount, Report, ReportAction, Transaction} from '@src/types/onyx'; import type {Errors} from '@src/types/onyx/OnyxCommon'; -import type {CustomUnit, NewCustomUnit} from '@src/types/onyx/Policy'; +import type {CustomUnit} from '@src/types/onyx/Policy'; import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; type AnnounceRoomMembersOnyxData = { @@ -957,7 +957,7 @@ function hideWorkspaceAlertMessage(policyID: string) { Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {alertMessage: ''}); } -function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: NewCustomUnit, lastModified: number) { +function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: CustomUnit, lastModified: number) { if (!currentCustomUnit.customUnitID || !newCustomUnit?.customUnitID || !newCustomUnit.rates?.customUnitRateID) { return; } @@ -971,7 +971,7 @@ function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: C [newCustomUnit.customUnitID]: { ...newCustomUnit, rates: { - [newCustomUnit.rates.customUnitRateID]: { + [newCustomUnit.rates.customUnitRateID as string]: { ...newCustomUnit.rates, errors: null, pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, @@ -994,7 +994,7 @@ function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: C pendingAction: null, errors: null, rates: { - [newCustomUnit.rates.customUnitRateID]: { + [newCustomUnit.rates.customUnitRateID as string]: { pendingAction: null, }, }, @@ -1013,7 +1013,7 @@ function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: C [currentCustomUnit.customUnitID]: { customUnitID: currentCustomUnit.customUnitID, rates: { - [newCustomUnit.rates.customUnitRateID]: { + [newCustomUnit.rates.customUnitRateID as string]: { ...currentCustomUnit.rates, errors: ErrorUtils.getMicroSecondOnyxError('workspace.reimburse.updateCustomUnitError'), }, diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index 6fd82bf1feff..fd9d3359ac74 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -17,20 +17,11 @@ type Attributes = { unit: Unit; }; -type NewCustomUnit = { - name: string; - customUnitID: string; - attributes: Attributes; - rates: Rate; - pendingAction?: string; - errors?: OnyxCommon.Errors; -}; - type CustomUnit = { name: string; customUnitID: string; attributes: Attributes; - rates: Record; + rates: Record | Rate; pendingAction?: string; errors?: OnyxCommon.Errors; }; @@ -117,4 +108,4 @@ type Policy = { export default Policy; -export type {Unit, CustomUnit, NewCustomUnit}; +export type {Unit, CustomUnit}; From 2d67506ca9e9eedd21898600a4444754418d712a Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:47:31 +0100 Subject: [PATCH 20/24] refactor two api writes for finallyData --- src/libs/actions/Policy.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 80f1d87d0b6c..c54a3d65d4e4 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -764,7 +764,7 @@ function deleteWorkspaceAvatar(policyID: string) { }, }, ]; - const successData: OnyxUpdate[] = [ + const finallyData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -780,9 +780,6 @@ function deleteWorkspaceAvatar(policyID: string) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - pendingFields: { - avatar: null, - }, errorFields: { avatar: ErrorUtils.getMicroSecondOnyxError('avatarWithImagePicker.deleteWorkspaceError'), }, @@ -796,7 +793,7 @@ function deleteWorkspaceAvatar(policyID: string) { const params: DeleteWorkspaceAvatarParams = {policyID}; - API.write('DeleteWorkspaceAvatar', params, {optimisticData, successData, failureData}); + API.write('DeleteWorkspaceAvatar', params, {optimisticData, finallyData, failureData}); } /** @@ -863,7 +860,7 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) }, }, ]; - const successData: OnyxUpdate[] = [ + const finallyData: OnyxUpdate[] = [ { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, @@ -879,9 +876,6 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - pendingFields: { - generalSettings: null, - }, errorFields: { generalSettings: ErrorUtils.getMicroSecondOnyxError('workspace.editor.genericFailureMessage'), }, @@ -910,7 +904,7 @@ function updateGeneralSettings(policyID: string, name: string, currency: string) API.write('UpdateWorkspaceGeneralSettings', params, { optimisticData, - successData, + finallyData, failureData, }); } From cdc8486f587c5c695ae3abd5199476dd7d5a0d0d Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Wed, 17 Jan 2024 02:17:54 +0100 Subject: [PATCH 21/24] refactor return type of getNewPersonalDetailsOnyxData --- src/libs/PersonalDetailsUtils.ts | 2 +- src/libs/actions/Policy.ts | 7 +------ src/libs/actions/Report.ts | 7 +------ 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/libs/PersonalDetailsUtils.ts b/src/libs/PersonalDetailsUtils.ts index 2fb9ff55968f..78cde95fb0e4 100644 --- a/src/libs/PersonalDetailsUtils.ts +++ b/src/libs/PersonalDetailsUtils.ts @@ -92,7 +92,7 @@ function getLoginsByAccountIDs(accountIDs: number[]): string[] { * @param accountIDs Array of user accountIDs * @returns Object with optimisticData, successData and failureData (object of personal details objects) */ -function getNewPersonalDetailsOnyxData(logins: string[], accountIDs: number[]): OnyxData { +function getNewPersonalDetailsOnyxData(logins: string[], accountIDs: number[]): Required> { const personalDetailsNew: PersonalDetailsList = {}; const personalDetailsCleanup: PersonalDetailsList = {}; diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index c54a3d65d4e4..35ce0bb81fd5 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -603,12 +603,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record const logins = Object.keys(invitedEmailsToAccountIDs).map((memberLogin) => OptionsListUtils.addSMSDomainIfPhoneNumber(memberLogin)); const accountIDs = Object.values(invitedEmailsToAccountIDs); - type PersonalDetailsOnyxData = { - optimisticData: OnyxUpdate[]; - finallyData: OnyxUpdate[]; - }; - - const newPersonalDetailsOnyxData = PersonalDetailsUtils.getNewPersonalDetailsOnyxData(logins, accountIDs) as PersonalDetailsOnyxData; + const newPersonalDetailsOnyxData = PersonalDetailsUtils.getNewPersonalDetailsOnyxData(logins, accountIDs); const announceRoomMembers = buildAnnounceRoomMembersOnyxData(policyID, accountIDs); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 0b305b81a7b6..5828dae00322 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2177,13 +2177,8 @@ function inviteToRoom(reportID: string, inviteeEmailsToAccountIDs: Record typeof accountID === 'number', ); - type PersonalDetailsOnyxData = { - optimisticData: OnyxUpdate[]; - finallyData: OnyxUpdate[]; - }; - const logins = inviteeEmails.map((memberLogin) => OptionsListUtils.addSMSDomainIfPhoneNumber(memberLogin)); - const newPersonalDetailsOnyxData = PersonalDetailsUtils.getNewPersonalDetailsOnyxData(logins, inviteeAccountIDs) as PersonalDetailsOnyxData; + const newPersonalDetailsOnyxData = PersonalDetailsUtils.getNewPersonalDetailsOnyxData(logins, inviteeAccountIDs); const optimisticData: OnyxUpdate[] = [ { From 59556b283d8e3dab7ebefdf3aabfee885e415377 Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Wed, 17 Jan 2024 02:19:19 +0100 Subject: [PATCH 22/24] fix CustomUnit rates typing --- src/types/onyx/Policy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/onyx/Policy.ts b/src/types/onyx/Policy.ts index fd9d3359ac74..81e5b8392ee2 100644 --- a/src/types/onyx/Policy.ts +++ b/src/types/onyx/Policy.ts @@ -21,7 +21,7 @@ type CustomUnit = { name: string; customUnitID: string; attributes: Attributes; - rates: Record | Rate; + rates: Record; pendingAction?: string; errors?: OnyxCommon.Errors; }; From 6c72ef8f9c3aaf4c312e7fc7b6dd5415e96b52dc Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Wed, 17 Jan 2024 02:23:32 +0100 Subject: [PATCH 23/24] remove usage of isNotEmptyObject for in PR related files --- src/libs/ReportUtils.ts | 48 +++++++++++++++++++------------------- src/libs/actions/Policy.ts | 8 +++---- src/libs/actions/Report.ts | 14 +++++------ 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 01e41465f99a..9f833ea5b661 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -23,7 +23,7 @@ import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/Rep import type {Receipt, WaypointCollection} from '@src/types/onyx/Transaction'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; -import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; import * as CurrencyUtils from './CurrencyUtils'; import DateUtils from './DateUtils'; @@ -479,7 +479,7 @@ function getRootParentReport(report: OnyxEntry | undefined | EmptyObject const parentReport = getReport(report?.parentReportID); // Runs recursion to iterate a parent report - return getRootParentReport(isNotEmptyObject(parentReport) ? parentReport : null); + return getRootParentReport(!isEmptyObject(parentReport) ? parentReport : null); } function getPolicy(policyID: string): Policy | EmptyObject { @@ -566,11 +566,11 @@ function isTaskReport(report: OnyxEntry): boolean { * In this case, we have added the key to the report itself */ function isCanceledTaskReport(report: OnyxEntry | EmptyObject = {}, parentReportAction: OnyxEntry | EmptyObject = {}): boolean { - if (isNotEmptyObject(parentReportAction) && (parentReportAction?.message?.[0]?.isDeletedParentAction ?? false)) { + if (!isEmptyObject(parentReportAction) && (parentReportAction?.message?.[0]?.isDeletedParentAction ?? false)) { return true; } - if (isNotEmptyObject(report) && report?.isDeletedParentAction) { + if (!isEmptyObject(report) && report?.isDeletedParentAction) { return true; } @@ -1034,7 +1034,7 @@ function isExpenseRequest(report: OnyxEntry): boolean { if (isThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; - return isExpenseReport(parentReport) && isNotEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction); + return isExpenseReport(parentReport) && !isEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction); } return false; } @@ -1047,7 +1047,7 @@ function isIOURequest(report: OnyxEntry): boolean { if (isThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`] ?? null; - return isIOUReport(parentReport) && isNotEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction); + return isIOUReport(parentReport) && !isEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction); } return false; } @@ -1115,7 +1115,7 @@ function canDeleteReportAction(reportAction: OnyxEntry, reportID: // For now, users cannot delete split actions const isSplitAction = reportAction?.originalMessage?.type === CONST.IOU.REPORT_ACTION_TYPE.SPLIT; - if (isSplitAction || isSettled(String(reportAction?.originalMessage?.IOUReportID)) || (isNotEmptyObject(report) && isReportApproved(report))) { + if (isSplitAction || isSettled(String(reportAction?.originalMessage?.IOUReportID)) || (!isEmptyObject(report) && isReportApproved(report))) { return false; } @@ -1134,7 +1134,7 @@ function canDeleteReportAction(reportAction: OnyxEntry, reportID: } const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`]; - const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN && isNotEmptyObject(report) && !isDM(report); + const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN && !isEmptyObject(report) && !isDM(report); return isActionOwner || isAdmin; } @@ -1611,7 +1611,7 @@ function getLastVisibleMessage(reportID: string | undefined, actionsToMerge: Rep const lastVisibleAction = ReportActionsUtils.getLastVisibleAction(reportID ?? '', actionsToMerge); // For Chat Report with deleted parent actions, let us fetch the correct message - if (ReportActionsUtils.isDeletedParentAction(lastVisibleAction) && isNotEmptyObject(report) && isChatReport(report)) { + if (ReportActionsUtils.isDeletedParentAction(lastVisibleAction) && !isEmptyObject(report) && isChatReport(report)) { const lastMessageText = getDeletedParentActionMessageForChatReport(lastVisibleAction); return { lastMessageText, @@ -1832,7 +1832,7 @@ function getTransactionDetails(transaction: OnyxEntry, createdDateF const report = getReport(transaction?.reportID); return { created: TransactionUtils.getCreated(transaction, createdDateFormat), - amount: TransactionUtils.getAmount(transaction, isNotEmptyObject(report) && isExpenseReport(report)), + amount: TransactionUtils.getAmount(transaction, !isEmptyObject(report) && isExpenseReport(report)), currency: TransactionUtils.getCurrency(transaction), comment: TransactionUtils.getDescription(transaction), merchant: TransactionUtils.getMerchant(transaction), @@ -2023,7 +2023,7 @@ function getTransactionReportName(reportAction: OnyxEntry): string } const transaction = TransactionUtils.getLinkedTransaction(reportAction); - if (!isNotEmptyObject(transaction)) { + if (!!isEmptyObject(transaction)) { // Transaction data might be empty on app's first load, if so we fallback to Request return Localize.translateLocal('iou.request'); } @@ -2064,14 +2064,14 @@ function getReportPreviewMessage( return reportActionMessage; } - if (isNotEmptyObject(reportAction) && !isIOUReport(report) && reportAction && ReportActionsUtils.isSplitBillAction(reportAction)) { + if (!isEmptyObject(reportAction) && !isIOUReport(report) && reportAction && ReportActionsUtils.isSplitBillAction(reportAction)) { // This covers group chats where the last action is a split bill action const linkedTransaction = TransactionUtils.getLinkedTransaction(reportAction); if (isEmptyObject(linkedTransaction)) { return reportActionMessage; } - if (isNotEmptyObject(linkedTransaction)) { + if (!isEmptyObject(linkedTransaction)) { if (TransactionUtils.isReceiptBeingScanned(linkedTransaction)) { return Localize.translateLocal('iou.receiptScanning'); } @@ -2099,10 +2099,10 @@ function getReportPreviewMessage( }); } - if (isNotEmptyObject(reportAction) && shouldConsiderReceiptBeingScanned && reportAction && ReportActionsUtils.isMoneyRequestAction(reportAction)) { + if (!isEmptyObject(reportAction) && shouldConsiderReceiptBeingScanned && reportAction && ReportActionsUtils.isMoneyRequestAction(reportAction)) { const linkedTransaction = TransactionUtils.getLinkedTransaction(reportAction); - if (isNotEmptyObject(linkedTransaction) && TransactionUtils.hasReceipt(linkedTransaction) && TransactionUtils.isReceiptBeingScanned(linkedTransaction)) { + if (!isEmptyObject(linkedTransaction) && TransactionUtils.hasReceipt(linkedTransaction) && TransactionUtils.isReceiptBeingScanned(linkedTransaction)) { return Localize.translateLocal('iou.receiptScanning'); } } @@ -2208,11 +2208,11 @@ function getReportName(report: OnyxEntry, policy: OnyxEntry = nu let formattedName: string | undefined; const parentReportAction = ReportActionsUtils.getParentReportAction(report); if (isChatThread(report)) { - if (isNotEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction)) { + if (!isEmptyObject(parentReportAction) && ReportActionsUtils.isTransactionThread(parentReportAction)) { return getTransactionReportName(parentReportAction); } - const isAttachment = ReportActionsUtils.isReportActionAttachment(isNotEmptyObject(parentReportAction) ? parentReportAction : null); + const isAttachment = ReportActionsUtils.isReportActionAttachment(!isEmptyObject(parentReportAction) ? parentReportAction : null); const parentReportActionMessage = (parentReportAction?.message?.[0]?.text ?? '').replace(/(\r\n|\n|\r)/gm, ' '); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; @@ -2477,11 +2477,11 @@ function updateOptimisticParentReportAction(parentReportAction: OnyxEntry, policies: OnyxCollection, currentReportId: string): boolean { const currentReport = getReport(currentReportId); - const parentReport = getParentReport(isNotEmptyObject(currentReport) ? currentReport : null); + const parentReport = getParentReport(!isEmptyObject(currentReport) ? currentReport : null); const reportActions = ReportActionsUtils.getAllReportActions(report?.reportID ?? ''); const isChildReportHasComment = Object.values(reportActions ?? {})?.some((reportAction) => (reportAction?.childVisibleActionCount ?? 0) > 0); return parentReport?.reportID !== report?.reportID && !isChildReportHasComment; @@ -3592,7 +3592,7 @@ function canFlagReportAction(reportAction: OnyxEntry, reportID: st reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT && !ReportActionsUtils.isDeletedAction(reportAction) && !ReportActionsUtils.isCreatedTaskReportAction(reportAction) && - isNotEmptyObject(report) && + !isEmptyObject(report) && report && isAllowedToComment(report), ); @@ -4215,7 +4215,7 @@ function getIOUReportActionDisplayMessage(reportAction: OnyxEntry) } const transaction = TransactionUtils.getTransaction(originalMessage.IOUTransactionID ?? ''); - const transactionDetails = getTransactionDetails(isNotEmptyObject(transaction) ? transaction : null); + const transactionDetails = getTransactionDetails(!isEmptyObject(transaction) ? transaction : null); const formattedAmount = CurrencyUtils.convertToDisplayString(transactionDetails?.amount ?? 0, transactionDetails?.currency); const isRequestSettled = isSettled(originalMessage.IOUReportID); const isApproved = isReportApproved(iouReport); diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 35ce0bb81fd5..743804410ba6 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -22,7 +22,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {PersonalDetailsList, Policy, PolicyMember, PolicyTags, RecentlyUsedCategories, RecentlyUsedTags, ReimbursementAccount, Report, ReportAction, Transaction} from '@src/types/onyx'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import type {CustomUnit} from '@src/types/onyx/Policy'; -import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type AnnounceRoomMembersOnyxData = { onyxOptimisticData: OnyxUpdate[]; @@ -430,7 +430,7 @@ function removeMembers(accountIDs: number[], policyID: string) { // If the policy has primaryLoginsInvited, then it displays informative messages on the members page about which primary logins were added by secondary logins. // If we delete all these logins then we should clear the informative messages since they are no longer relevant. - if (isNotEmptyObject(policy?.primaryLoginsInvited ?? {})) { + if (!isEmptyObject(policy?.primaryLoginsInvited ?? {})) { // Take the current policy members and remove them optimistically const policyMemberAccountIDs = Object.keys(allPolicyMembers?.[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`] ?? {}).map((accountID) => Number(accountID)); const remainingMemberAccountIDs = policyMemberAccountIDs.filter((accountID) => !accountIDs.includes(accountID)); @@ -641,7 +641,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record // Remove the object, when it is a newly created account. value: accountIDs.reduce((accountIDsWithClearedPendingAction, accountID) => { let value = null; - const accountAlreadyExists = isNotEmptyObject(allPersonalDetails?.[accountID]); + const accountAlreadyExists = !isEmptyObject(allPersonalDetails?.[accountID]); if (accountAlreadyExists) { value = {pendingAction: null, errors: null}; @@ -680,7 +680,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: Record welcomeNote: new ExpensiMark().replace(welcomeNote), policyID, }; - if (isNotEmptyObject(membersChats.reportCreationData)) { + if (!isEmptyObject(membersChats.reportCreationData)) { params.reportCreationData = JSON.stringify(membersChats.reportCreationData); } API.write('AddMembersToWorkspace', params, {optimisticData, successData, failureData}); diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 5828dae00322..3c433cf342b2 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -38,7 +38,7 @@ import type Report from '@src/types/onyx/Report'; import type {Message, ReportActionBase, ReportActions} from '@src/types/onyx/ReportAction'; import type ReportAction from '@src/types/onyx/ReportAction'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; -import {isEmptyObject, isNotEmptyObject} from '@src/types/utils/EmptyObject'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import * as Session from './Session'; import * as Welcome from './Welcome'; @@ -321,7 +321,7 @@ function addActions(reportID: string, text = '', file?: File) { const report = ReportUtils.getReport(reportID); - if (isNotEmptyObject(report) && ReportUtils.getReportNotificationPreference(report) === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) { + if (!isEmptyObject(report) && ReportUtils.getReportNotificationPreference(report) === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN) { optimisticReport.notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS; } @@ -422,7 +422,7 @@ function addActions(reportID: string, text = '', file?: File) { // 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 (isNotEmptyObject(optimisticParentReportData)) { + if (!isEmptyObject(optimisticParentReportData)) { optimisticData.push(optimisticParentReportData); } @@ -572,7 +572,7 @@ function openReport( } // If we are creating a new report, we need to add the optimistic report data and a report action - if (isNotEmptyObject(newReportObject)) { + if (!isEmptyObject(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 optimisticData[0].onyxMethod = Onyx.METHOD.SET; @@ -1190,7 +1190,7 @@ function deleteReportComment(reportID: string, reportAction: ReportAction) { optimisticReport?.lastVisibleActionCreated ?? '', CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, ); - if (isNotEmptyObject(optimisticParentReportData)) { + if (!isEmptyObject(optimisticParentReportData)) { optimisticData.push(optimisticParentReportData); } } @@ -1390,7 +1390,7 @@ function updateNotificationPreference( report: OnyxEntry | EmptyObject = {}, ) { if (previousValue === newValue) { - if (navigate && isNotEmptyObject(report) && report.reportID) { + if (navigate && !isEmptyObject(report) && report.reportID) { ReportUtils.goBackToDetailsPage(report); } return; @@ -1433,7 +1433,7 @@ function updateNotificationPreference( const parameters: UpdateReportNotificationPreferenceParameters = {reportID, notificationPreference: newValue}; API.write('UpdateReportNotificationPreference', parameters, {optimisticData, failureData}); - if (navigate && isNotEmptyObject(report)) { + if (navigate && !isEmptyObject(report)) { ReportUtils.goBackToDetailsPage(report); } } From b9f4551e77ec4ca1d3a7757845d20bdc2603f78d Mon Sep 17 00:00:00 2001 From: cdOut <88325488+cdOut@users.noreply.github.com> Date: Wed, 17 Jan 2024 03:07:04 +0100 Subject: [PATCH 24/24] fix double negation lint error --- src/libs/ReportUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index d3b61a28363b..a93a36898126 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2044,7 +2044,7 @@ function getTransactionReportName(reportAction: OnyxEntry): string } const transaction = TransactionUtils.getLinkedTransaction(reportAction); - if (!!isEmptyObject(transaction)) { + if (isEmptyObject(transaction)) { // Transaction data might be empty on app's first load, if so we fallback to Request return Localize.translateLocal('iou.request'); }