From 107f84cde8e3b3b8be70a51f79ecf8fb8201eab7 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 2 Feb 2024 15:32:38 +0100 Subject: [PATCH 01/32] implement isPolicyOwner --- src/libs/PolicyUtils.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libs/PolicyUtils.ts b/src/libs/PolicyUtils.ts index b6ee4ab3a353..80f23fd3c9ab 100644 --- a/src/libs/PolicyUtils.ts +++ b/src/libs/PolicyUtils.ts @@ -111,6 +111,11 @@ const isPolicyAdmin = (policy: OnyxEntry): boolean => policy?.role === C const isPolicyMember = (policyID: string, policies: Record): boolean => Object.values(policies).some((policy) => policy?.id === policyID); +/** + * Checks if the current user is an owner (creator) of the policy. + */ +const isPolicyOwner = (policy: OnyxEntry, currentUserAccountID: number): boolean => policy?.ownerAccountID === currentUserAccountID; + /** * Create an object mapping member emails to their accountIDs. Filter for members without errors, and get the login email from the personalDetail object using the accountID. * @@ -244,6 +249,7 @@ export { getCleanedTagName, isPendingDeletePolicy, isPolicyMember, + isPolicyOwner, isPaidGroupPolicy, extractPolicyIDFromPath, getPathWithoutPolicyID, From 41110392095b4762c020f66bd4baebeeb57ad171 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 2 Feb 2024 15:32:46 +0100 Subject: [PATCH 02/32] implement isReportOwner --- src/libs/ReportUtils.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5f3efcbcdbb0..33ef7982af3c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4679,6 +4679,10 @@ function canBeAutoReimbursed(report: OnyxEntry, policy: OnyxEntry): boolean { + return report?.ownerAccountID === currentUserPersonalDetails?.accountID; +} + export { getReportParticipantsTitle, isReportMessageAttachment, @@ -4865,6 +4869,7 @@ export { isReportFieldOfTypeTitle, isReportFieldDisabled, getAvailableReportFields, + isReportOwner, }; export type { From 52b16ceb740c3f21c4b9c3629643db1ef4b8e52b Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 2 Feb 2024 15:45:47 +0100 Subject: [PATCH 03/32] implement draft types for leave API --- src/libs/API/parameters/LeavePolicyExpenseChatParams.ts | 6 ++++++ src/libs/API/parameters/LeaveWorkspaceParams.ts | 6 ++++++ src/libs/API/parameters/index.ts | 2 ++ src/libs/API/types.ts | 5 +++++ 4 files changed, 19 insertions(+) create mode 100644 src/libs/API/parameters/LeavePolicyExpenseChatParams.ts create mode 100644 src/libs/API/parameters/LeaveWorkspaceParams.ts diff --git a/src/libs/API/parameters/LeavePolicyExpenseChatParams.ts b/src/libs/API/parameters/LeavePolicyExpenseChatParams.ts new file mode 100644 index 000000000000..665b28b9e0a2 --- /dev/null +++ b/src/libs/API/parameters/LeavePolicyExpenseChatParams.ts @@ -0,0 +1,6 @@ +type LeavePolicyExpenseChatParams = { + // TODO: Clarify + reportID: string; +}; + +export default LeavePolicyExpenseChatParams; diff --git a/src/libs/API/parameters/LeaveWorkspaceParams.ts b/src/libs/API/parameters/LeaveWorkspaceParams.ts new file mode 100644 index 000000000000..b28fa3e5aed9 --- /dev/null +++ b/src/libs/API/parameters/LeaveWorkspaceParams.ts @@ -0,0 +1,6 @@ +type LeaveWorkspaceParams = { + // TODO: Clarify + policyID: string; +}; + +export default LeaveWorkspaceParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 8c0c2fde17cf..9e877fffbd2e 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -89,7 +89,9 @@ export type {default as AddWorkspaceRoomParams} from './AddWorkspaceRoomParams'; export type {default as UpdatePolicyRoomNameParams} from './UpdatePolicyRoomNameParams'; export type {default as AddEmojiReactionParams} from './AddEmojiReactionParams'; export type {default as RemoveEmojiReactionParams} from './RemoveEmojiReactionParams'; +export type {default as LeavePolicyExpenseChatParams} from './LeavePolicyExpenseChatParams'; export type {default as LeaveRoomParams} from './LeaveRoomParams'; +export type {default as LeaveWorkspaceParams} from './LeaveWorkspaceParams'; export type {default as InviteToRoomParams} from './InviteToRoomParams'; export type {default as RemoveFromRoomParams} from './RemoveFromRoomParams'; export type {default as FlagCommentParams} from './FlagCommentParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 05b658ee0702..37ed746ea5b6 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -115,6 +115,9 @@ const WRITE_COMMANDS = { SET_NAME_VALUE_PAIR: 'SetNameValuePair', SET_REPORT_FIELD: 'Report_SetFields', SET_REPORT_NAME: 'RenameReport', + // TODO: Clarify + LEAVE_WORKSPACE: 'LeaveWorkspace', + LEAVE_POLICY_EXPENSE_CHAT: 'LeaveWorkspaceExpenseChat', } as const; type WriteCommand = ValueOf; @@ -227,6 +230,8 @@ type WriteCommandParameters = { [WRITE_COMMANDS.SET_NAME_VALUE_PAIR]: Parameters.SetNameValuePairParams; [WRITE_COMMANDS.SET_REPORT_FIELD]: Parameters.SetReportFieldParams; [WRITE_COMMANDS.SET_REPORT_NAME]: Parameters.SetReportNameParams; + [WRITE_COMMANDS.LEAVE_WORKSPACE]: Parameters.LeaveWorkspaceParams; + [WRITE_COMMANDS.LEAVE_POLICY_EXPENSE_CHAT]: Parameters.LeavePolicyExpenseChatParams; }; const READ_COMMANDS = { From 5788e867e4ebd40cde8099d19f72332206842a92 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 2 Feb 2024 15:46:04 +0100 Subject: [PATCH 04/32] implement draft for leave API --- src/libs/actions/Policy.ts | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 0c3a8afc1576..c68bd947faf9 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import {PUBLIC_DOMAINS} from 'expensify-common/lib/CONST'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; @@ -14,6 +15,8 @@ import type { DeleteMembersFromWorkspaceParams, DeleteWorkspaceAvatarParams, DeleteWorkspaceParams, + LeavePolicyExpenseChatParams, + LeaveWorkspaceParams, OpenDraftWorkspaceRequestParams, OpenWorkspaceInvitePageParams, OpenWorkspaceMembersPageParams, @@ -1984,6 +1987,48 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { return policyID; } +/** + * TODO: Comment + */ +function leaveWorkspace(policyID: string) { + const optimisticData: OnyxUpdate[] = []; + const successData: OnyxUpdate[] = []; + const failureData: OnyxUpdate[] = []; + + const parameters: LeaveWorkspaceParams = { + policyID, + }; + + console.group('leaveWorkspace'); + console.log('policyID', policyID); + console.log('parameters', parameters); + console.log('{optimisticData, successData, failureData}', {optimisticData, successData, failureData}); + console.groupEnd(); + return; + API.write(WRITE_COMMANDS.LEAVE_WORKSPACE, parameters, {optimisticData, successData, failureData}); +} + +/** + * TODO: Comment + */ +function leavePolicyExpenseChat(reportID: string) { + const optimisticData: OnyxUpdate[] = []; + const successData: OnyxUpdate[] = []; + const failureData: OnyxUpdate[] = []; + + const parameters: LeavePolicyExpenseChatParams = { + reportID, + }; + + console.group('leaveWorkspace'); + console.log('reportID', reportID); + console.log('parameters', parameters); + console.log('{optimisticData, successData, failureData}', {optimisticData, successData, failureData}); + console.groupEnd(); + return; + API.write(WRITE_COMMANDS.LEAVE_POLICY_EXPENSE_CHAT, parameters, {optimisticData, successData, failureData}); +} + export { removeMembers, addMembersToWorkspace, @@ -2020,4 +2065,6 @@ export { buildOptimisticPolicyRecentlyUsedTags, createDraftInitialWorkspace, setWorkspaceInviteMessageDraft, + leaveWorkspace, + leavePolicyExpenseChat, }; From 2114adb61d998fb729101975b9a7d765355f9d57 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 2 Feb 2024 15:46:23 +0100 Subject: [PATCH 05/32] implement draft for leave a workspace chat --- src/pages/home/HeaderView.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index ca4c90b2df55..0ea2f92d3cce 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -29,10 +29,12 @@ import {getGroupChatName} from '@libs/GroupChatUtils'; import * as HeaderUtils from '@libs/HeaderUtils'; import reportWithoutHasDraftSelector from '@libs/OnyxSelectors/reportWithoutHasDraftSelector'; import * as OptionsListUtils from '@libs/OptionsListUtils'; +import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import reportPropTypes from '@pages/reportPropTypes'; import * as Link from '@userActions/Link'; +import * as Policy from '@userActions/Policy'; import * as Report from '@userActions/Report'; import * as Session from '@userActions/Session'; import * as Task from '@userActions/Task'; @@ -118,6 +120,8 @@ function HeaderView(props) { const isUserCreatedPolicyRoom = ReportUtils.isUserCreatedPolicyRoom(props.report); const isPolicyMember = useMemo(() => !_.isEmpty(props.policy), [props.policy]); const canLeaveRoom = ReportUtils.canLeaveRoom(props.report, isPolicyMember); + const canLeavePolicyExpenseChat = + isPolicyExpenseChat && !(PolicyUtils.isPolicyAdmin(props.policy) || PolicyUtils.isPolicyOwner(props.policy, props.session.accountID) || ReportUtils.isReportOwner(props.report)); const isArchivedRoom = ReportUtils.isArchivedRoom(props.report); // We hide the button when we are chatting with an automated Expensify account since it's not possible to contact @@ -156,9 +160,9 @@ function HeaderView(props) { ), ); - const canJoinOrLeave = isChatThread || isUserCreatedPolicyRoom || canLeaveRoom; + const canJoinOrLeave = isChatThread || isUserCreatedPolicyRoom || canLeaveRoom || canLeavePolicyExpenseChat; const canJoin = canJoinOrLeave && !isWhisperAction && props.report.notificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN; - const canLeave = canJoinOrLeave && ((isChatThread && props.report.notificationPreference.length) || isUserCreatedPolicyRoom || canLeaveRoom); + const canLeave = canJoinOrLeave && ((isChatThread && props.report.notificationPreference.length) || isUserCreatedPolicyRoom || canLeaveRoom || canLeavePolicyExpenseChat); if (canJoin) { threeDotMenuItems.push({ icon: Expensicons.ChatBubbles, @@ -167,10 +171,11 @@ function HeaderView(props) { }); } else if (canLeave) { const isWorkspaceMemberLeavingWorkspaceRoom = !isChatThread && lodashGet(props.report, 'visibility', '') === CONST.REPORT.VISIBILITY.RESTRICTED && isPolicyMember; + const action = isPolicyExpenseChat ? () => Policy.leavePolicyExpenseChat(props.reportID) : () => Report.leaveRoom(props.reportID, isWorkspaceMemberLeavingWorkspaceRoom); threeDotMenuItems.push({ icon: Expensicons.ChatBubbles, text: translate('common.leave'), - onSelected: Session.checkIfActionIsAllowed(() => Report.leaveRoom(props.reportID, isWorkspaceMemberLeavingWorkspaceRoom)), + onSelected: Session.checkIfActionIsAllowed(action), }); } @@ -360,7 +365,7 @@ export default memo( }, policy: { key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`, - selector: (policy) => _.pick(policy, ['name', 'avatar', 'pendingAction']), + selector: (policy) => _.pick(policy, ['name', 'avatar', 'pendingAction', 'role', 'ownerAccountID']), }, rootParentReportPolicy: { key: ({report}) => { From eb6299247215dff7c64c877273acb0f3949552aa Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 2 Feb 2024 15:46:31 +0100 Subject: [PATCH 06/32] implement draft for leave a workspace --- src/pages/workspace/WorkspacesListPage.tsx | 23 +++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 9b763120b30d..c8e518429557 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -28,11 +28,12 @@ import * as ReportUtils from '@libs/ReportUtils'; import type {AvatarSource} from '@libs/UserUtils'; import * as App from '@userActions/App'; import * as Policy from '@userActions/Policy'; +import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; -import type {PolicyMembers, Policy as PolicyType, ReimbursementAccount, Report} from '@src/types/onyx'; +import type {PolicyMembers, Policy as PolicyType, ReimbursementAccount, Report, Session as SessionType} from '@src/types/onyx'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; @@ -74,6 +75,9 @@ type WorkspaceListPageOnyxProps = { /** All reports shared with the user (coming from Onyx) */ reports: OnyxCollection; + + /** Session info for the currently logged in user. */ + session: OnyxEntry; }; type WorkspaceListPageProps = WithPolicyAndFullscreenLoadingProps & WorkspaceListPageOnyxProps; @@ -109,7 +113,7 @@ function dismissWorkspaceError(policyID: string, pendingAction: OnyxCommon.Pendi throw new Error('Not implemented'); } -function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, reports}: WorkspaceListPageProps) { +function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, reports, session}: WorkspaceListPageProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -135,6 +139,7 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r const getMenuItem = useCallback( ({item, index}: GetMenuItem) => { const isAdmin = item.role === CONST.POLICY.ROLE.ADMIN; + const isOwner = item.ownerAccountID === session?.accountID; // Menu options to navigate to the chat report of #admins and #announce room. // For navigation, the chat report ids may be unavailable due to the missing chat reports in Onyx. // In such cases, let us use the available chat report ids from the policy. @@ -168,6 +173,15 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r }); } + if (!(isAdmin || isOwner)) { + threeDotsMenuItems.push({ + icon: Expensicons.ChatBubbles, + text: translate('common.leave'), + // TODO: Integrate a handler + onSelected: Session.checkIfActionIsAllowed(() => Policy.leaveWorkspace(item.policyID ?? '')), + }); + } + return ( ); }, - [isSmallScreenWidth, styles.mb3, styles.mh5, styles.ph5, styles.hoveredComponentBG, translate], + [session?.accountID, styles.ph5, styles.mh5, styles.mb3, styles.hoveredComponentBG, translate, isSmallScreenWidth], ); const listHeaderComponent = useCallback(() => { @@ -409,5 +423,8 @@ export default withPolicyAndFullscreenLoading( reports: { key: ONYXKEYS.COLLECTION.REPORT, }, + session: { + key: ONYXKEYS.SESSION, + }, })(WorkspacesListPage), ); From 3775ad084b4ed838a7b4ad32793c2d9568ecc9e1 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 2 Feb 2024 17:53:42 +0100 Subject: [PATCH 07/32] integrate draft api data --- .../AppNavigator/ReportScreenIDSetter.ts | 26 ++------- src/libs/ReportUtils.ts | 48 +++++++++++++++- src/libs/actions/Policy.ts | 56 +++++++++++++++---- src/libs/actions/Report.ts | 42 +------------- 4 files changed, 95 insertions(+), 77 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts b/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts index b4bb56262860..927e4c57d66b 100644 --- a/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts +++ b/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts @@ -7,7 +7,7 @@ import {getPolicyMembersByIdWithoutCurrentUser} from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as App from '@userActions/App'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Policy, PolicyMembers, Report, ReportMetadata} from '@src/types/onyx'; +import type {Policy, PolicyMembers, Report} from '@src/types/onyx'; import type {ReportScreenWrapperProps} from './ReportScreenWrapper'; type ReportScreenIDSetterComponentProps = { @@ -23,9 +23,6 @@ type ReportScreenIDSetterComponentProps = { /** Whether user is a new user */ isFirstTimeNewExpensifyUser: OnyxEntry; - /** The report metadata */ - reportMetadata: OnyxCollection; - /** The accountID of the current user */ accountID?: number; }; @@ -41,25 +38,15 @@ const getLastAccessedReportID = ( policies: OnyxCollection, isFirstTimeNewExpensifyUser: OnyxEntry, openOnAdminRoom: boolean, - reportMetadata: OnyxCollection, policyID?: string, policyMemberAccountIDs?: number[], ): string | undefined => { - const lastReport = ReportUtils.findLastAccessedReport( - reports, - ignoreDefaultRooms, - policies, - !!isFirstTimeNewExpensifyUser, - openOnAdminRoom, - reportMetadata, - policyID, - policyMemberAccountIDs, - ); + const lastReport = ReportUtils.findLastAccessedReport(reports, ignoreDefaultRooms, policies, !!isFirstTimeNewExpensifyUser, openOnAdminRoom, policyID, policyMemberAccountIDs); return lastReport?.reportID; }; // This wrapper is reponsible for opening the last accessed report if there is no reportID specified in the route params -function ReportScreenIDSetter({route, reports, policies, policyMembers = {}, navigation, isFirstTimeNewExpensifyUser = false, reportMetadata, accountID}: ReportScreenIDSetterProps) { +function ReportScreenIDSetter({route, reports, policies, policyMembers = {}, navigation, isFirstTimeNewExpensifyUser = false, accountID}: ReportScreenIDSetterProps) { const {canUseDefaultRooms} = usePermissions(); const {activeWorkspaceID} = useActiveWorkspace(); @@ -84,7 +71,6 @@ function ReportScreenIDSetter({route, reports, policies, policyMembers = {}, nav policies, isFirstTimeNewExpensifyUser, !!reports?.params?.openOnAdminRoom, - reportMetadata, activeWorkspaceID, policyMemberAccountIDs, ); @@ -96,7 +82,7 @@ function ReportScreenIDSetter({route, reports, policies, policyMembers = {}, nav } else { App.confirmReadyToOpenApp(); } - }, [route, navigation, reports, canUseDefaultRooms, policies, isFirstTimeNewExpensifyUser, reportMetadata, activeWorkspaceID, policyMembers, accountID]); + }, [route, navigation, reports, canUseDefaultRooms, policies, isFirstTimeNewExpensifyUser, activeWorkspaceID, policyMembers, accountID]); // The ReportScreen without the reportID set will display a skeleton // until the reportID is loaded and set in the route param @@ -122,10 +108,6 @@ export default withOnyx session?.accountID, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 33ef7982af3c..3f48f550a077 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -491,6 +491,13 @@ Onyx.connect({ }, }); +let reportMetadata: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_METADATA, + waitForCollectionCallback: true, + callback: (value) => (reportMetadata = value), +}); + function getChatType(report: OnyxEntry): ValueOf | undefined { return report?.chatType; } @@ -911,7 +918,7 @@ function filterReportsByPolicyIDAndMemberAccountIDs(reports: Report[], policyMem /** * Given an array of reports, return them sorted by the last read timestamp. */ -function sortReportsByLastRead(reports: Report[], reportMetadata: OnyxCollection): Array> { +function sortReportsByLastRead(reports: Report[]): Array> { return reports .filter((report) => !!report?.reportID && !!(reportMetadata?.[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${report.reportID}`]?.lastVisitTime ?? report?.lastReadTime)) .sort((a, b) => { @@ -991,7 +998,6 @@ function findLastAccessedReport( policies: OnyxCollection, isFirstTimeNewExpensifyUser: boolean, openOnAdminRoom = false, - reportMetadata: OnyxCollection = {}, policyID?: string, policyMemberAccountIDs: number[] = [], ): OnyxEntry { @@ -1007,7 +1013,7 @@ function findLastAccessedReport( reportsValues = filterReportsByPolicyIDAndMemberAccountIDs(reportsValues, policyMemberAccountIDs, policyID); } - let sortedReports = sortReportsByLastRead(reportsValues, reportMetadata); + let sortedReports = sortReportsByLastRead(reportsValues); let adminReport: OnyxEntry | undefined; if (openOnAdminRoom) { @@ -4683,6 +4689,41 @@ function isReportOwner(report: OnyxEntry): boolean { return report?.ownerAccountID === currentUserPersonalDetails?.accountID; } +function navigateUserOnceLeaveReport(reportID: string) { + const sortedReportsByLastRead = sortReportsByLastRead(Object.values(allReports ?? {}) as Report[]); + + // We want to filter out the current report, hidden reports and empty chats + const filteredReportsByLastRead = sortedReportsByLastRead.filter( + (sortedReport) => + sortedReport?.reportID !== reportID && + sortedReport?.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN && + shouldReportBeInOptionList({ + report: sortedReport, + currentReportId: '', + isInGSDMode: false, + betas: [], + policies: {}, + excludeEmptyChats: true, + doesReportHaveViolations: false, + }), + ); + const lastAccessedReportID = filteredReportsByLastRead.at(-1)?.reportID; + + if (lastAccessedReportID) { + // We should call Navigation.goBack to pop the current route first before navigating to Concierge. + Navigation.goBack(ROUTES.HOME); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID)); + } else { + const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.CONCIERGE]); + const chat = getChatByParticipants(participantAccountIDs); + if (chat?.reportID) { + // We should call Navigation.goBack to pop the current route first before navigating to Concierge. + Navigation.goBack(ROUTES.HOME); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chat.reportID)); + } + } +} + export { getReportParticipantsTitle, isReportMessageAttachment, @@ -4870,6 +4911,7 @@ export { isReportFieldDisabled, getAvailableReportFields, isReportOwner, + navigateUserOnceLeaveReport, }; export type { diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index c68bd947faf9..929318ed9856 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1991,30 +1991,60 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { * TODO: Comment */ function leaveWorkspace(policyID: string) { - const optimisticData: OnyxUpdate[] = []; - const successData: OnyxUpdate[] = []; - const failureData: OnyxUpdate[] = []; - + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + avatar: '', + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + errors: null, + }, + }, + ]; const parameters: LeaveWorkspaceParams = { policyID, }; console.group('leaveWorkspace'); console.log('policyID', policyID); - console.log('parameters', parameters); - console.log('{optimisticData, successData, failureData}', {optimisticData, successData, failureData}); + console.log('{parameters, optimisticData}', {parameters, optimisticData}); console.groupEnd(); - return; - API.write(WRITE_COMMANDS.LEAVE_WORKSPACE, parameters, {optimisticData, successData, failureData}); + + API.write(WRITE_COMMANDS.LEAVE_WORKSPACE, parameters, {optimisticData}); } /** * TODO: Comment */ function leavePolicyExpenseChat(reportID: string) { - const optimisticData: OnyxUpdate[] = []; - const successData: OnyxUpdate[] = []; - const failureData: OnyxUpdate[] = []; + const report = ReportUtils.getReport(reportID); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + }, + }, + ]; + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, + }, + }, + ]; + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: report, + }, + ]; const parameters: LeavePolicyExpenseChatParams = { reportID, @@ -2025,8 +2055,10 @@ function leavePolicyExpenseChat(reportID: string) { console.log('parameters', parameters); console.log('{optimisticData, successData, failureData}', {optimisticData, successData, failureData}); console.groupEnd(); - return; + API.write(WRITE_COMMANDS.LEAVE_POLICY_EXPENSE_CHAT, parameters, {optimisticData, successData, failureData}); + + ReportUtils.navigateUserOnceLeaveReport(reportID); } export { diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 782cf2b174c2..27cc7a2af4a7 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -66,7 +66,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; -import type {PersonalDetails, PersonalDetailsList, PolicyReportField, RecentlyUsedReportFields, ReportActionReactions, ReportMetadata, ReportUserIsTyping} from '@src/types/onyx'; +import type {PersonalDetails, PersonalDetailsList, PolicyReportField, RecentlyUsedReportFields, ReportActionReactions, ReportUserIsTyping} from '@src/types/onyx'; import type {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import type {NotificationPreference, WriteCapability} from '@src/types/onyx/Report'; import type Report from '@src/types/onyx/Report'; @@ -159,13 +159,6 @@ Onyx.connect({ }, }); -let reportMetadata: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT_METADATA, - waitForCollectionCallback: true, - callback: (value) => (reportMetadata = value), -}); - const allReports: OnyxCollection = {}; let conciergeChatReportID: string | undefined; const typingWatchTimers: Record = {}; @@ -2248,38 +2241,7 @@ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = fal API.write(WRITE_COMMANDS.LEAVE_ROOM, parameters, {optimisticData, successData, failureData}); - const sortedReportsByLastRead = ReportUtils.sortReportsByLastRead(Object.values(allReports ?? {}) as Report[], reportMetadata); - - // We want to filter out the current report, hidden reports and empty chats - const filteredReportsByLastRead = sortedReportsByLastRead.filter( - (sortedReport) => - sortedReport?.reportID !== reportID && - sortedReport?.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN && - ReportUtils.shouldReportBeInOptionList({ - report: sortedReport, - currentReportId: '', - isInGSDMode: false, - betas: [], - policies: {}, - excludeEmptyChats: true, - doesReportHaveViolations: false, - }), - ); - const lastAccessedReportID = filteredReportsByLastRead.at(-1)?.reportID; - - if (lastAccessedReportID) { - // We should call Navigation.goBack to pop the current route first before navigating to Concierge. - Navigation.goBack(ROUTES.HOME); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID)); - } else { - const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.CONCIERGE]); - const chat = ReportUtils.getChatByParticipants(participantAccountIDs); - if (chat?.reportID) { - // We should call Navigation.goBack to pop the current route first before navigating to Concierge. - Navigation.goBack(ROUTES.HOME); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chat.reportID)); - } - } + ReportUtils.navigateUserOnceLeaveReport(reportID); } /** Invites people to a room */ From 95b8741eb659e4e9b83776fdc40545a523585953 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 5 Feb 2024 17:48:58 +0100 Subject: [PATCH 08/32] use real api for leaving workspaces --- .../LeavePolicyExpenseChatParams.ts | 6 - .../API/parameters/LeaveWorkspaceParams.ts | 6 - src/libs/API/parameters/index.ts | 2 - src/libs/API/types.ts | 5 - src/libs/actions/Policy.ts | 126 ++++++------------ src/pages/home/HeaderView.js | 4 +- src/pages/workspace/WorkspacesListPage.tsx | 3 +- 7 files changed, 46 insertions(+), 106 deletions(-) delete mode 100644 src/libs/API/parameters/LeavePolicyExpenseChatParams.ts delete mode 100644 src/libs/API/parameters/LeaveWorkspaceParams.ts diff --git a/src/libs/API/parameters/LeavePolicyExpenseChatParams.ts b/src/libs/API/parameters/LeavePolicyExpenseChatParams.ts deleted file mode 100644 index 665b28b9e0a2..000000000000 --- a/src/libs/API/parameters/LeavePolicyExpenseChatParams.ts +++ /dev/null @@ -1,6 +0,0 @@ -type LeavePolicyExpenseChatParams = { - // TODO: Clarify - reportID: string; -}; - -export default LeavePolicyExpenseChatParams; diff --git a/src/libs/API/parameters/LeaveWorkspaceParams.ts b/src/libs/API/parameters/LeaveWorkspaceParams.ts deleted file mode 100644 index b28fa3e5aed9..000000000000 --- a/src/libs/API/parameters/LeaveWorkspaceParams.ts +++ /dev/null @@ -1,6 +0,0 @@ -type LeaveWorkspaceParams = { - // TODO: Clarify - policyID: string; -}; - -export default LeaveWorkspaceParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index a41227a60507..b7c3dff7c342 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -90,9 +90,7 @@ export type {default as AddWorkspaceRoomParams} from './AddWorkspaceRoomParams'; export type {default as UpdatePolicyRoomNameParams} from './UpdatePolicyRoomNameParams'; export type {default as AddEmojiReactionParams} from './AddEmojiReactionParams'; export type {default as RemoveEmojiReactionParams} from './RemoveEmojiReactionParams'; -export type {default as LeavePolicyExpenseChatParams} from './LeavePolicyExpenseChatParams'; export type {default as LeaveRoomParams} from './LeaveRoomParams'; -export type {default as LeaveWorkspaceParams} from './LeaveWorkspaceParams'; export type {default as InviteToRoomParams} from './InviteToRoomParams'; export type {default as RemoveFromRoomParams} from './RemoveFromRoomParams'; export type {default as FlagCommentParams} from './FlagCommentParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index aa1f601ed575..c011fa395f0f 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -116,9 +116,6 @@ const WRITE_COMMANDS = { SET_NAME_VALUE_PAIR: 'SetNameValuePair', SET_REPORT_FIELD: 'Report_SetFields', SET_REPORT_NAME: 'RenameReport', - // TODO: Clarify - LEAVE_WORKSPACE: 'LeaveWorkspace', - LEAVE_POLICY_EXPENSE_CHAT: 'LeaveWorkspaceExpenseChat', } as const; type WriteCommand = ValueOf; @@ -232,8 +229,6 @@ type WriteCommandParameters = { [WRITE_COMMANDS.SET_NAME_VALUE_PAIR]: Parameters.SetNameValuePairParams; [WRITE_COMMANDS.SET_REPORT_FIELD]: Parameters.SetReportFieldParams; [WRITE_COMMANDS.SET_REPORT_NAME]: Parameters.SetReportNameParams; - [WRITE_COMMANDS.LEAVE_WORKSPACE]: Parameters.LeaveWorkspaceParams; - [WRITE_COMMANDS.LEAVE_POLICY_EXPENSE_CHAT]: Parameters.LeavePolicyExpenseChatParams; }; const READ_COMMANDS = { diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 929318ed9856..d718fcd1b88a 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -15,8 +15,6 @@ import type { DeleteMembersFromWorkspaceParams, DeleteWorkspaceAvatarParams, DeleteWorkspaceParams, - LeavePolicyExpenseChatParams, - LeaveWorkspaceParams, OpenDraftWorkspaceRequestParams, OpenWorkspaceInvitePageParams, OpenWorkspaceMembersPageParams, @@ -41,6 +39,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 type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type AnnounceRoomMembersOnyxData = { @@ -354,8 +353,8 @@ function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[] /** * Build optimistic data for removing users from the announcement room */ -function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: number[]): AnnounceRoomMembersOnyxData { - const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); +function removeOptimisticAnnounceRoomMembers(policy: Policy | EmptyObject, accountIDs: number[]): AnnounceRoomMembersOnyxData { + const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policy.id); const announceRoomMembers: AnnounceRoomMembersOnyxData = { onyxOptimisticData: [], onyxFailureData: [], @@ -367,21 +366,37 @@ function removeOptimisticAnnounceRoomMembers(policyID: string, accountIDs: numbe 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], visibleChatMemberAccountIDs: [...remainUsers], + ...(accountIDs.includes(sessionAccountID) + ? { + statusNum: CONST.REPORT.STATUS_NUM.CLOSED, + stateNum: CONST.REPORT.STATE_NUM.APPROVED, + oldPolicyName: policy.name, + hasDraft: false, + } + : {}), }, }); - announceRoomMembers.onyxFailureData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${announceReport.reportID}`, value: { participantAccountIDs: announceReport.participantAccountIDs, visibleChatMemberAccountIDs: announceReport.visibleChatMemberAccountIDs, + ...(accountIDs.includes(sessionAccountID) + ? { + statusNum: announceReport.statusNum, + stateNum: announceReport.stateNum, + oldPolicyName: announceReport.oldPolicyName, + hasDraft: announceReport.hasDraft, + } + : {}), }, }); } @@ -404,7 +419,7 @@ function removeMembers(accountIDs: number[], policyID: string) { const workspaceChats = ReportUtils.getWorkspaceChats(policyID, accountIDs); const optimisticClosedReportActions = workspaceChats.map(() => ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy.name, CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY)); - const announceRoomMembers = removeOptimisticAnnounceRoomMembers(policyID, accountIDs); + const announceRoomMembers = removeOptimisticAnnounceRoomMembers(policy, accountIDs); const optimisticMembersState: OnyxCollection = {}; const successMembersState: OnyxCollection = {}; @@ -507,11 +522,34 @@ function removeMembers(accountIDs: number[], policyID: string) { }); }); + if (accountIDs.includes(sessionAccountID)) { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + }, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, + value: { + pendingAction: policy.pendingAction, + }, + }); + } + const params: DeleteMembersFromWorkspaceParams = { emailList: accountIDs.map((accountID) => allPersonalDetails?.[accountID]?.login).join(','), policyID, }; + console.log('WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}', WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, { + optimisticData, + successData, + failureData, + }); + API.write(WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}); } @@ -1987,80 +2025,6 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined { return policyID; } -/** - * TODO: Comment - */ -function leaveWorkspace(policyID: string) { - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, - value: { - avatar: '', - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, - errors: null, - }, - }, - ]; - const parameters: LeaveWorkspaceParams = { - policyID, - }; - - console.group('leaveWorkspace'); - console.log('policyID', policyID); - console.log('{parameters, optimisticData}', {parameters, optimisticData}); - console.groupEnd(); - - API.write(WRITE_COMMANDS.LEAVE_WORKSPACE, parameters, {optimisticData}); -} - -/** - * TODO: Comment - */ -function leavePolicyExpenseChat(reportID: string) { - const report = ReportUtils.getReport(reportID); - - const optimisticData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, - }, - }, - ]; - const successData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: { - notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN, - }, - }, - ]; - const failureData: OnyxUpdate[] = [ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, - value: report, - }, - ]; - - const parameters: LeavePolicyExpenseChatParams = { - reportID, - }; - - console.group('leaveWorkspace'); - console.log('reportID', reportID); - console.log('parameters', parameters); - console.log('{optimisticData, successData, failureData}', {optimisticData, successData, failureData}); - console.groupEnd(); - - API.write(WRITE_COMMANDS.LEAVE_POLICY_EXPENSE_CHAT, parameters, {optimisticData, successData, failureData}); - - ReportUtils.navigateUserOnceLeaveReport(reportID); -} - export { removeMembers, addMembersToWorkspace, @@ -2097,6 +2061,4 @@ export { buildOptimisticPolicyRecentlyUsedTags, createDraftInitialWorkspace, setWorkspaceInviteMessageDraft, - leaveWorkspace, - leavePolicyExpenseChat, }; diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index e957c70ed5cc..9cd78b47271b 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -34,7 +34,6 @@ import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import reportPropTypes from '@pages/reportPropTypes'; import * as Link from '@userActions/Link'; -import * as Policy from '@userActions/Policy'; import * as Report from '@userActions/Report'; import * as Session from '@userActions/Session'; import * as Task from '@userActions/Task'; @@ -164,11 +163,10 @@ function HeaderView(props) { }); } else if (canLeave) { const isWorkspaceMemberLeavingWorkspaceRoom = !isChatThread && lodashGet(props.report, 'visibility', '') === CONST.REPORT.VISIBILITY.RESTRICTED && isPolicyMember; - const action = isPolicyExpenseChat ? () => Policy.leavePolicyExpenseChat(props.reportID) : () => Report.leaveRoom(props.reportID, isWorkspaceMemberLeavingWorkspaceRoom); threeDotMenuItems.push({ icon: Expensicons.ChatBubbles, text: translate('common.leave'), - onSelected: Session.checkIfActionIsAllowed(action), + onSelected: Session.checkIfActionIsAllowed(() => Report.leaveRoom(props.reportID, isWorkspaceMemberLeavingWorkspaceRoom)), }); } diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index c8e518429557..bda81eee0011 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -177,8 +177,7 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r threeDotsMenuItems.push({ icon: Expensicons.ChatBubbles, text: translate('common.leave'), - // TODO: Integrate a handler - onSelected: Session.checkIfActionIsAllowed(() => Policy.leaveWorkspace(item.policyID ?? '')), + onSelected: Session.checkIfActionIsAllowed(() => Policy.removeMembers([session?.accountID ?? 0], item.policyID ?? '')), }); } From af8c733a9e824b07f3accc4131994fc4a0f579fe Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 5 Feb 2024 17:51:55 +0100 Subject: [PATCH 09/32] Revert "integrate draft api data" This reverts commit 3775ad084b4ed838a7b4ad32793c2d9568ecc9e1. --- .../AppNavigator/ReportScreenIDSetter.ts | 26 ++++++++-- src/libs/ReportUtils.ts | 48 ++----------------- src/libs/actions/Report.ts | 42 +++++++++++++++- 3 files changed, 65 insertions(+), 51 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts b/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts index 927e4c57d66b..b4bb56262860 100644 --- a/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts +++ b/src/libs/Navigation/AppNavigator/ReportScreenIDSetter.ts @@ -7,7 +7,7 @@ import {getPolicyMembersByIdWithoutCurrentUser} from '@libs/PolicyUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as App from '@userActions/App'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Policy, PolicyMembers, Report} from '@src/types/onyx'; +import type {Policy, PolicyMembers, Report, ReportMetadata} from '@src/types/onyx'; import type {ReportScreenWrapperProps} from './ReportScreenWrapper'; type ReportScreenIDSetterComponentProps = { @@ -23,6 +23,9 @@ type ReportScreenIDSetterComponentProps = { /** Whether user is a new user */ isFirstTimeNewExpensifyUser: OnyxEntry; + /** The report metadata */ + reportMetadata: OnyxCollection; + /** The accountID of the current user */ accountID?: number; }; @@ -38,15 +41,25 @@ const getLastAccessedReportID = ( policies: OnyxCollection, isFirstTimeNewExpensifyUser: OnyxEntry, openOnAdminRoom: boolean, + reportMetadata: OnyxCollection, policyID?: string, policyMemberAccountIDs?: number[], ): string | undefined => { - const lastReport = ReportUtils.findLastAccessedReport(reports, ignoreDefaultRooms, policies, !!isFirstTimeNewExpensifyUser, openOnAdminRoom, policyID, policyMemberAccountIDs); + const lastReport = ReportUtils.findLastAccessedReport( + reports, + ignoreDefaultRooms, + policies, + !!isFirstTimeNewExpensifyUser, + openOnAdminRoom, + reportMetadata, + policyID, + policyMemberAccountIDs, + ); return lastReport?.reportID; }; // This wrapper is reponsible for opening the last accessed report if there is no reportID specified in the route params -function ReportScreenIDSetter({route, reports, policies, policyMembers = {}, navigation, isFirstTimeNewExpensifyUser = false, accountID}: ReportScreenIDSetterProps) { +function ReportScreenIDSetter({route, reports, policies, policyMembers = {}, navigation, isFirstTimeNewExpensifyUser = false, reportMetadata, accountID}: ReportScreenIDSetterProps) { const {canUseDefaultRooms} = usePermissions(); const {activeWorkspaceID} = useActiveWorkspace(); @@ -71,6 +84,7 @@ function ReportScreenIDSetter({route, reports, policies, policyMembers = {}, nav policies, isFirstTimeNewExpensifyUser, !!reports?.params?.openOnAdminRoom, + reportMetadata, activeWorkspaceID, policyMemberAccountIDs, ); @@ -82,7 +96,7 @@ function ReportScreenIDSetter({route, reports, policies, policyMembers = {}, nav } else { App.confirmReadyToOpenApp(); } - }, [route, navigation, reports, canUseDefaultRooms, policies, isFirstTimeNewExpensifyUser, activeWorkspaceID, policyMembers, accountID]); + }, [route, navigation, reports, canUseDefaultRooms, policies, isFirstTimeNewExpensifyUser, reportMetadata, activeWorkspaceID, policyMembers, accountID]); // The ReportScreen without the reportID set will display a skeleton // until the reportID is loaded and set in the route param @@ -108,6 +122,10 @@ export default withOnyx session?.accountID, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 045ed1d6a1e2..7594d9db0fdd 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -503,13 +503,6 @@ Onyx.connect({ }, }); -let reportMetadata: OnyxCollection = {}; -Onyx.connect({ - key: ONYXKEYS.COLLECTION.REPORT_METADATA, - waitForCollectionCallback: true, - callback: (value) => (reportMetadata = value), -}); - function getChatType(report: OnyxEntry): ValueOf | undefined { return report?.chatType; } @@ -930,7 +923,7 @@ function filterReportsByPolicyIDAndMemberAccountIDs(reports: Report[], policyMem /** * Given an array of reports, return them sorted by the last read timestamp. */ -function sortReportsByLastRead(reports: Report[]): Array> { +function sortReportsByLastRead(reports: Report[], reportMetadata: OnyxCollection): Array> { return reports .filter((report) => !!report?.reportID && !!(reportMetadata?.[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${report.reportID}`]?.lastVisitTime ?? report?.lastReadTime)) .sort((a, b) => { @@ -1010,6 +1003,7 @@ function findLastAccessedReport( policies: OnyxCollection, isFirstTimeNewExpensifyUser: boolean, openOnAdminRoom = false, + reportMetadata: OnyxCollection = {}, policyID?: string, policyMemberAccountIDs: number[] = [], ): OnyxEntry { @@ -1025,7 +1019,7 @@ function findLastAccessedReport( reportsValues = filterReportsByPolicyIDAndMemberAccountIDs(reportsValues, policyMemberAccountIDs, policyID); } - let sortedReports = sortReportsByLastRead(reportsValues); + let sortedReports = sortReportsByLastRead(reportsValues, reportMetadata); let adminReport: OnyxEntry | undefined; if (openOnAdminRoom) { @@ -4771,41 +4765,6 @@ function isReportOwner(report: OnyxEntry): boolean { return report?.ownerAccountID === currentUserPersonalDetails?.accountID; } -function navigateUserOnceLeaveReport(reportID: string) { - const sortedReportsByLastRead = sortReportsByLastRead(Object.values(allReports ?? {}) as Report[]); - - // We want to filter out the current report, hidden reports and empty chats - const filteredReportsByLastRead = sortedReportsByLastRead.filter( - (sortedReport) => - sortedReport?.reportID !== reportID && - sortedReport?.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN && - shouldReportBeInOptionList({ - report: sortedReport, - currentReportId: '', - isInGSDMode: false, - betas: [], - policies: {}, - excludeEmptyChats: true, - doesReportHaveViolations: false, - }), - ); - const lastAccessedReportID = filteredReportsByLastRead.at(-1)?.reportID; - - if (lastAccessedReportID) { - // We should call Navigation.goBack to pop the current route first before navigating to Concierge. - Navigation.goBack(ROUTES.HOME); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID)); - } else { - const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.CONCIERGE]); - const chat = getChatByParticipants(participantAccountIDs); - if (chat?.reportID) { - // We should call Navigation.goBack to pop the current route first before navigating to Concierge. - Navigation.goBack(ROUTES.HOME); - Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chat.reportID)); - } - } -} - export { getReportParticipantsTitle, isReportMessageAttachment, @@ -4994,7 +4953,6 @@ export { isReportFieldDisabled, getAvailableReportFields, isReportOwner, - navigateUserOnceLeaveReport, getAllAncestorReportActionIDs, }; diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index c07b330b5a68..4bff826ceb3a 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -66,7 +66,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; -import type {PersonalDetails, PersonalDetailsList, PolicyReportField, RecentlyUsedReportFields, ReportActionReactions, ReportUserIsTyping} from '@src/types/onyx'; +import type {PersonalDetails, PersonalDetailsList, PolicyReportField, RecentlyUsedReportFields, ReportActionReactions, ReportMetadata, ReportUserIsTyping} from '@src/types/onyx'; import type {Decision, OriginalMessageIOU} from '@src/types/onyx/OriginalMessage'; import type {NotificationPreference, WriteCapability} from '@src/types/onyx/Report'; import type Report from '@src/types/onyx/Report'; @@ -159,6 +159,13 @@ Onyx.connect({ }, }); +let reportMetadata: OnyxCollection = {}; +Onyx.connect({ + key: ONYXKEYS.COLLECTION.REPORT_METADATA, + waitForCollectionCallback: true, + callback: (value) => (reportMetadata = value), +}); + const allReports: OnyxCollection = {}; let conciergeChatReportID: string | undefined; const typingWatchTimers: Record = {}; @@ -2241,7 +2248,38 @@ function leaveRoom(reportID: string, isWorkspaceMemberLeavingWorkspaceRoom = fal API.write(WRITE_COMMANDS.LEAVE_ROOM, parameters, {optimisticData, successData, failureData}); - ReportUtils.navigateUserOnceLeaveReport(reportID); + const sortedReportsByLastRead = ReportUtils.sortReportsByLastRead(Object.values(allReports ?? {}) as Report[], reportMetadata); + + // We want to filter out the current report, hidden reports and empty chats + const filteredReportsByLastRead = sortedReportsByLastRead.filter( + (sortedReport) => + sortedReport?.reportID !== reportID && + sortedReport?.notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN && + ReportUtils.shouldReportBeInOptionList({ + report: sortedReport, + currentReportId: '', + isInGSDMode: false, + betas: [], + policies: {}, + excludeEmptyChats: true, + doesReportHaveViolations: false, + }), + ); + const lastAccessedReportID = filteredReportsByLastRead.at(-1)?.reportID; + + if (lastAccessedReportID) { + // We should call Navigation.goBack to pop the current route first before navigating to Concierge. + Navigation.goBack(ROUTES.HOME); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(lastAccessedReportID)); + } else { + const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins([CONST.EMAIL.CONCIERGE]); + const chat = ReportUtils.getChatByParticipants(participantAccountIDs); + if (chat?.reportID) { + // We should call Navigation.goBack to pop the current route first before navigating to Concierge. + Navigation.goBack(ROUTES.HOME); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chat.reportID)); + } + } } /** Invites people to a room */ From 8a3473f35c8ed37cef7ab45dbeed45467d32c04e Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 5 Feb 2024 17:54:11 +0100 Subject: [PATCH 10/32] minor fix --- src/libs/actions/Policy.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index d718fcd1b88a..d012fba90ef0 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import {PUBLIC_DOMAINS} from 'expensify-common/lib/CONST'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; @@ -39,7 +38,6 @@ 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 type {EmptyObject} from '@src/types/utils/EmptyObject'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type AnnounceRoomMembersOnyxData = { @@ -353,8 +351,8 @@ function buildAnnounceRoomMembersOnyxData(policyID: string, accountIDs: number[] /** * Build optimistic data for removing users from the announcement room */ -function removeOptimisticAnnounceRoomMembers(policy: Policy | EmptyObject, accountIDs: number[]): AnnounceRoomMembersOnyxData { - const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policy.id); +function removeOptimisticAnnounceRoomMembers(policyID: string, policyName: string, accountIDs: number[]): AnnounceRoomMembersOnyxData { + const announceReport = ReportUtils.getRoom(CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, policyID); const announceRoomMembers: AnnounceRoomMembersOnyxData = { onyxOptimisticData: [], onyxFailureData: [], @@ -377,7 +375,7 @@ function removeOptimisticAnnounceRoomMembers(policy: Policy | EmptyObject, accou ? { statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, - oldPolicyName: policy.name, + oldPolicyName: policyName, hasDraft: false, } : {}), @@ -419,7 +417,7 @@ function removeMembers(accountIDs: number[], policyID: string) { const workspaceChats = ReportUtils.getWorkspaceChats(policyID, accountIDs); const optimisticClosedReportActions = workspaceChats.map(() => ReportUtils.buildOptimisticClosedReportAction(sessionEmail, policy.name, CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY)); - const announceRoomMembers = removeOptimisticAnnounceRoomMembers(policy, accountIDs); + const announceRoomMembers = removeOptimisticAnnounceRoomMembers(policy.id, policy.name, accountIDs); const optimisticMembersState: OnyxCollection = {}; const successMembersState: OnyxCollection = {}; @@ -544,12 +542,6 @@ function removeMembers(accountIDs: number[], policyID: string) { policyID, }; - console.log('WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}', WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, { - optimisticData, - successData, - failureData, - }); - API.write(WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}); } From 0ce1db13f56356cba7434de0603a1e0601c7b156 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 6 Feb 2024 15:28:32 +0100 Subject: [PATCH 11/32] move button to top --- src/pages/workspace/WorkspacesListPage.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index bda81eee0011..b51e14adbe9e 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -157,6 +157,14 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r }); } + if (!(isAdmin || isOwner)) { + threeDotsMenuItems.push({ + icon: Expensicons.ChatBubbles, + text: translate('common.leave'), + onSelected: Session.checkIfActionIsAllowed(() => Policy.removeMembers([session?.accountID ?? 0], item.policyID ?? '')), + }); + } + if (isAdmin && item.adminRoom) { threeDotsMenuItems.push({ icon: Expensicons.Hashtag, @@ -173,14 +181,6 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r }); } - if (!(isAdmin || isOwner)) { - threeDotsMenuItems.push({ - icon: Expensicons.ChatBubbles, - text: translate('common.leave'), - onSelected: Session.checkIfActionIsAllowed(() => Policy.removeMembers([session?.accountID ?? 0], item.policyID ?? '')), - }); - } - return ( Date: Tue, 26 Mar 2024 12:51:25 +0100 Subject: [PATCH 12/32] integrate leave policy command --- src/libs/API/parameters/LeavePolicyParams.ts | 7 +++++++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 2 ++ src/libs/actions/Policy.ts | 15 +++++++++++++++ 4 files changed, 25 insertions(+) create mode 100644 src/libs/API/parameters/LeavePolicyParams.ts diff --git a/src/libs/API/parameters/LeavePolicyParams.ts b/src/libs/API/parameters/LeavePolicyParams.ts new file mode 100644 index 000000000000..bc2962eebb66 --- /dev/null +++ b/src/libs/API/parameters/LeavePolicyParams.ts @@ -0,0 +1,7 @@ +type LeavePolicyParams = { + authToken: string; + policyID: string; + email: string; +}; + +export default LeavePolicyParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 1895c2426e1a..a3a7fc3cd836 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -196,3 +196,4 @@ export type {default as SetPolicyCustomTaxNameParams} from './SetPolicyCustomTax export type {default as SetPolicyForeignCurrencyDefaultParams} from './SetPolicyForeignCurrencyDefaultParams'; export type {default as SetPolicyCurrencyDefaultParams} from './SetPolicyCurrencyDefaultParams'; export type {default as RenamePolicyTaxParams} from './RenamePolicyTaxParams'; +export type {default as LeavePolicyParams} from './LeavePolicyParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 9d6e6b3929b8..f4945821a14a 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -193,6 +193,7 @@ const WRITE_COMMANDS = { UPDATE_POLICY_DISTANCE_RATE_VALUE: 'UpdatePolicyDistanceRateValue', SET_POLICY_DISTANCE_RATES_ENABLED: 'SetPolicyDistanceRatesEnabled', DELETE_POLICY_DISTANCE_RATES: 'DeletePolicyDistanceRates', + LEAVE_POLICY: 'LeavePolicy', } as const; type WriteCommand = ValueOf; @@ -384,6 +385,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.UPDATE_POLICY_DISTANCE_RATE_VALUE]: Parameters.UpdatePolicyDistanceRateValueParams; [WRITE_COMMANDS.SET_POLICY_DISTANCE_RATES_ENABLED]: Parameters.SetPolicyDistanceRatesEnabledParams; [WRITE_COMMANDS.DELETE_POLICY_DISTANCE_RATES]: Parameters.DeletePolicyDistanceRatesParams; + [WRITE_COMMANDS.LEAVE_POLICY]: Parameters.LeavePolicyParams; }; const READ_COMMANDS = { diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 4b3dea92ba2e..fd4108bf0e64 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -24,6 +24,7 @@ import type { EnablePolicyTagsParams, EnablePolicyTaxesParams, EnablePolicyWorkflowsParams, + LeavePolicyParams, OpenDraftWorkspaceRequestParams, OpenPolicyCategoriesPageParams, OpenPolicyDistanceRatesPageParams, @@ -194,11 +195,13 @@ Onyx.connect({ let sessionEmail = ''; let sessionAccountID = 0; +let sessionAuthToken = ''; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { sessionEmail = val?.email ?? ''; sessionAccountID = val?.accountID ?? -1; + sessionAuthToken = val?.authToken ?? ''; }, }); @@ -961,6 +964,17 @@ function removeMembers(accountIDs: number[], policyID: string) { pendingAction: policy.pendingAction, }, }); + + const params: LeavePolicyParams = { + policyID, + email: sessionEmail, + authToken: sessionAuthToken, + }; + + // TODO: Extract into a distinct function + API.write(WRITE_COMMANDS.LEAVE_POLICY, params, {optimisticData, successData, failureData}); + + return; } const params: DeleteMembersFromWorkspaceParams = { @@ -968,6 +982,7 @@ function removeMembers(accountIDs: number[], policyID: string) { policyID, }; + // eslint-disable-next-line rulesdir/no-multiple-api-calls API.write(WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}); } From 5ca68a6e77f9fabb67431c7347593414247d722f Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 27 Mar 2024 11:39:37 +0100 Subject: [PATCH 13/32] Add leave policy change log const --- src/CONST.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CONST.ts b/src/CONST.ts index 47caa5e64a90..660bb994b047 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -729,6 +729,7 @@ const CONST = { UPDATE_TAG_NAME: 'POLICYCHANGELOG_UPDATE_TAG_NAME', UPDATE_TIME_ENABLED: 'POLICYCHANGELOG_UPDATE_TIME_ENABLED', UPDATE_TIME_RATE: 'POLICYCHANGELOG_UPDATE_TIME_RATE', + LEAVE_POLICY: 'POLICYCHANGELOG_LEAVE_POLICY', }, ROOMCHANGELOG: { INVITE_TO_ROOM: 'INVITETOROOM', From f8c839534ab10d9f3a5f1c468ff420a101190ed8 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 29 Mar 2024 10:18:58 +0100 Subject: [PATCH 14/32] Revert "Add leave policy change log const" This reverts commit 5ca68a6e77f9fabb67431c7347593414247d722f. --- src/CONST.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CONST.ts b/src/CONST.ts index 74fb802a9653..0fa1c64be44d 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -735,7 +735,6 @@ const CONST = { UPDATE_TAG_NAME: 'POLICYCHANGELOG_UPDATE_TAG_NAME', UPDATE_TIME_ENABLED: 'POLICYCHANGELOG_UPDATE_TIME_ENABLED', UPDATE_TIME_RATE: 'POLICYCHANGELOG_UPDATE_TIME_RATE', - LEAVE_POLICY: 'POLICYCHANGELOG_LEAVE_POLICY', }, ROOMCHANGELOG: { INVITE_TO_ROOM: 'INVITETOROOM', From 80caecdba561492ca3b336864c00ba10bf219320 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 29 Mar 2024 13:49:06 +0100 Subject: [PATCH 15/32] integrate leave policy function --- src/libs/actions/Policy.ts | 100 +++++++++++++++++---- src/pages/workspace/WorkspacesListPage.tsx | 2 +- 2 files changed, 82 insertions(+), 20 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index ddcf21b86f68..84fdac33e5f1 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -971,41 +971,102 @@ function removeMembers(accountIDs: number[], policyID: string) { }); }); - if (accountIDs.includes(sessionAccountID)) { - optimisticData.push({ + const params: DeleteMembersFromWorkspaceParams = { + emailList: accountIDs.map((accountID) => allPersonalDetails?.[accountID]?.login).join(','), + policyID, + }; + + API.write(WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}); +} + +function leaveWorkspace(policyID: string) { + const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}` as const; + const policy = ReportUtils.getPolicy(policyID); + const workspaceChats = ReportUtils.getWorkspaceChats(policyID, [sessionAccountID]); + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: membersListKey, + value: { + [sessionAccountID]: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + }, + }, + }, + { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, }, - }); - failureData.push({ + }, + ]; + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: membersListKey, + value: { + [sessionAccountID]: null, + }, + }, + ]; + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: membersListKey, + value: { + [sessionAccountID]: { + errors: ErrorUtils.getMicroSecondOnyxError('workspace.people.error.genericRemove'), + }, + }, + }, + { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { pendingAction: policy.pendingAction, }, - }); - - const params: LeavePolicyParams = { - policyID, - email: sessionEmail, - authToken: sessionAuthToken, - }; + }, + ]; - // TODO: Extract into a distinct function - API.write(WRITE_COMMANDS.LEAVE_POLICY, params, {optimisticData, successData, failureData}); + const pendingChatMembers = ReportUtils.getPendingChatMembers([sessionAccountID], [], CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); - return; - } + workspaceChats.forEach((report) => { + optimisticData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, + value: { + statusNum: CONST.REPORT.STATUS_NUM.CLOSED, + stateNum: CONST.REPORT.STATE_NUM.APPROVED, + oldPolicyName: policy.name, + hasDraft: false, + pendingChatMembers, + }, + }); + successData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, + value: { + pendingChatMembers: null, + }, + }); + failureData.push({ + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, + value: { + pendingChatMembers: null, + }, + }); + }); - const params: DeleteMembersFromWorkspaceParams = { - emailList: accountIDs.map((accountID) => allPersonalDetails?.[accountID]?.login).join(','), + const params: LeavePolicyParams = { policyID, + email: sessionEmail, + authToken: sessionAuthToken, }; - // eslint-disable-next-line rulesdir/no-multiple-api-calls - API.write(WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}); + API.write(WRITE_COMMANDS.LEAVE_POLICY, params, {optimisticData, successData, failureData}); } function updateWorkspaceMembersRole(policyID: string, accountIDs: number[], newRole: typeof CONST.POLICY.ROLE.ADMIN | typeof CONST.POLICY.ROLE.USER) { @@ -4830,6 +4891,7 @@ function setForeignCurrencyDefault(policyID: string, taxCode: string) { export { removeMembers, + leaveWorkspace, updateWorkspaceMembersRole, requestWorkspaceOwnerChange, clearWorkspaceOwnerChangeFlow, diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 320ea1378685..d28f3ceef75f 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -174,7 +174,7 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, r threeDotsMenuItems.push({ icon: Expensicons.ChatBubbles, text: translate('common.leave'), - onSelected: Session.checkIfActionIsAllowed(() => Policy.removeMembers([session?.accountID ?? 0], item.policyID ?? '')), + onSelected: Session.checkIfActionIsAllowed(() => Policy.leaveWorkspace(item.policyID ?? '')), }); } From 44bcc093a1eaab6d488f834eec099f0c011d38a3 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 3 Apr 2024 16:10:14 +0200 Subject: [PATCH 16/32] add comments & use allPolicies --- src/libs/actions/Policy.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index eff44bcb705b..80bd0d5c4e4f 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -974,9 +974,10 @@ function removeMembers(accountIDs: number[], policyID: string) { API.write(WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}); } +/** Leave a workspace */ function leaveWorkspace(policyID: string) { const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}` as const; - const policy = ReportUtils.getPolicy(policyID); + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; const workspaceChats = ReportUtils.getWorkspaceChats(policyID, [sessionAccountID]); const optimisticData: OnyxUpdate[] = [ @@ -1020,7 +1021,7 @@ function leaveWorkspace(policyID: string) { onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`, value: { - pendingAction: policy.pendingAction, + pendingAction: policy?.pendingAction, }, }, ]; @@ -1034,7 +1035,7 @@ function leaveWorkspace(policyID: string) { value: { statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, - oldPolicyName: policy.name, + oldPolicyName: policy?.name ?? '', hasDraft: false, pendingChatMembers, }, From 4b48baff8f9bba768a34af91ecc941e598315a6c Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 3 Apr 2024 16:11:34 +0200 Subject: [PATCH 17/32] add comments & create canLeavePolicyExpenseChat --- src/libs/ReportUtils.ts | 6 ++++++ src/pages/home/HeaderView.tsx | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 6906f6d80eee..16421a7890a2 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5642,6 +5642,7 @@ function canBeAutoReimbursed(report: OnyxEntry, policy: OnyxEntry): boolean { return report?.ownerAccountID === currentUserPersonalDetails?.accountID; } @@ -5705,6 +5706,10 @@ function hasActionsWithErrors(reportID: string): boolean { return Object.values(reportActions ?? {}).some((action) => !isEmptyObject(action.errors)); } +function canLeavePolicyExpenseChat(report: OnyxEntry, policy: OnyxEntry): boolean { + return isPolicyExpenseChat(report) && !(PolicyUtils.isPolicyAdmin(policy) || PolicyUtils.isPolicyOwner(policy, currentUserAccountID ?? -1) || isReportOwner(report)); +} + export { getReportParticipantsTitle, isReportMessageAttachment, @@ -5932,6 +5937,7 @@ export { isTrackExpenseReport, hasActionsWithErrors, getGroupChatName, + canLeavePolicyExpenseChat, }; export type { diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index 0576738609a9..c27420288e51 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -26,7 +26,6 @@ import Navigation from '@libs/Navigation/Navigation'; import type {ReportWithoutHasDraft} from '@libs/OnyxSelectors/reportWithoutHasDraftSelector'; import reportWithoutHasDraftSelector from '@libs/OnyxSelectors/reportWithoutHasDraftSelector'; import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as PolicyUtils from '@libs/PolicyUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; import * as Link from '@userActions/Link'; @@ -99,8 +98,7 @@ function HeaderView({report, personalDetails, parentReport, parentReportAction, const isUserCreatedPolicyRoom = ReportUtils.isUserCreatedPolicyRoom(report); const isPolicyMember = useMemo(() => !isEmptyObject(policy), [policy]); const canLeaveRoom = ReportUtils.canLeaveRoom(report, isPolicyMember); - const canLeavePolicyExpenseChat = - isPolicyExpenseChat && !(PolicyUtils.isPolicyAdmin(policy) || PolicyUtils.isPolicyOwner(policy, session?.accountID ?? -1) || ReportUtils.isReportOwner(report)); + const canLeavePolicyExpenseChat = ReportUtils.canLeavePolicyExpenseChat(report, policy); const reportDescription = ReportUtils.getReportDescriptionText(report); const policyName = ReportUtils.getPolicyName(report, true); const policyDescription = ReportUtils.getPolicyDescriptionText(policy); From 9e9f7bfead305a29a744d12c6dbdae3b2d266284 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 4 Apr 2024 14:56:17 +0200 Subject: [PATCH 18/32] integrate getAllWorkspaceReports --- src/libs/ReportUtils.ts | 10 ++++++++++ src/libs/actions/Policy.ts | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 242a63117f53..f37c73ab8dab 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5036,6 +5036,15 @@ function getWorkspaceChats(policyID: string, accountIDs: number[]): Array isPolicyExpenseChat(report) && (report?.policyID ?? '') === policyID && accountIDs.includes(report?.ownerAccountID ?? -1)); } +/** + * Gets all reports that relate to the policy + * + * @param policyID - the workspace ID to get all associated reports + */ +function getAllWorkspaceReports(policyID: string): Array> { + return Object.values(allReports ?? {}).filter((report) => (report?.policyID ?? '') === policyID); +} + /** * @param policy - the workspace the report is on, null if the user isn't a member of the workspace */ @@ -5860,6 +5869,7 @@ export { isDM, isSelfDM, getWorkspaceChats, + getAllWorkspaceReports, shouldDisableRename, hasSingleParticipant, getReportRecipientAccountIDs, diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 89101988ee56..c1b07dd60995 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -986,7 +986,7 @@ function removeMembers(accountIDs: number[], policyID: string) { function leaveWorkspace(policyID: string) { const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}` as const; const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]; - const workspaceChats = ReportUtils.getWorkspaceChats(policyID, [sessionAccountID]); + const workspaceChats = ReportUtils.getAllWorkspaceReports(policyID); const optimisticData: OnyxUpdate[] = [ { From d40d9ffa0cb746e1bcfe264c691e97603f3be767 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 4 Apr 2024 18:46:23 +0200 Subject: [PATCH 19/32] prettify --- 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 f37c73ab8dab..63cd586e2414 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5038,7 +5038,7 @@ function getWorkspaceChats(policyID: string, accountIDs: number[]): Array> { From c55cd1329205524ac908a31077ea1f9949ac00f8 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 5 Apr 2024 16:26:35 +0200 Subject: [PATCH 20/32] fix ts --- src/libs/actions/Policy.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 02613eeb05fc..7ac620d924a9 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1039,7 +1039,6 @@ function leaveWorkspace(policyID: string) { statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, oldPolicyName: policy?.name ?? '', - hasDraft: false, pendingChatMembers, }, }); From 2b955b3d4eed7b29c6d726733454c21abe4509d6 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 5 Apr 2024 16:28:56 +0200 Subject: [PATCH 21/32] fix ts --- src/libs/actions/Policy.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 7ac620d924a9..ba203d912000 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -799,7 +799,6 @@ function removeOptimisticAnnounceRoomMembers(policyID: string, policyName: strin statusNum: CONST.REPORT.STATUS_NUM.CLOSED, stateNum: CONST.REPORT.STATE_NUM.APPROVED, oldPolicyName: policyName, - hasDraft: false, } : {}), }, @@ -814,7 +813,6 @@ function removeOptimisticAnnounceRoomMembers(policyID: string, policyName: strin statusNum: announceReport.statusNum, stateNum: announceReport.stateNum, oldPolicyName: announceReport.oldPolicyName, - hasDraft: announceReport.hasDraft, } : {}), }, From 2c2ae9f6f9f6633f705fbed06d0962d2d82d87a8 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 8 Apr 2024 16:27:33 +0200 Subject: [PATCH 22/32] check if expense chat --- src/pages/home/HeaderView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index d3f04b748175..aacf8ebaa333 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -153,7 +153,7 @@ function HeaderView({report, personalDetails, parentReport, parentReportAction, onSelected: join, }); } else if (canLeave) { - const isWorkspaceMemberLeavingWorkspaceRoom = !isChatThread && report.visibility === CONST.REPORT.VISIBILITY.RESTRICTED && isPolicyMember; + const isWorkspaceMemberLeavingWorkspaceRoom = !isChatThread && (report.visibility === CONST.REPORT.VISIBILITY.RESTRICTED || isPolicyExpenseChat) && isPolicyMember; threeDotMenuItems.push({ icon: Expensicons.ChatBubbles, text: translate('common.leave'), From 858a872fcdb7b6e9cff1e625096909ea74e40a8c Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 9 Apr 2024 12:50:50 +0200 Subject: [PATCH 23/32] fix finding a chat by participants --- src/libs/ReportUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f0cad072de2c..27ad6cede414 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -4573,9 +4573,9 @@ function getChatByParticipantsAndPolicy(newParticipantList: number[], policyID: if (!report?.participantAccountIDs) { return false; } - const sortedParticipanctsAccountIDs = report.participantAccountIDs?.sort(); + const sortedParticipantsAccountIDs = report.participantAccountIDs?.sort(); // Only return the room if it has all the participants and is not a policy room - return report.policyID === policyID && lodashIsEqual(newParticipantList, sortedParticipanctsAccountIDs); + return report.policyID === policyID && newParticipantList.every((newParticipant) => sortedParticipantsAccountIDs.includes(newParticipant)); }) ?? null ); } From 0b12883e71655d71e2dd545c92842b0ea54c2601 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 9 Apr 2024 15:40:26 +0200 Subject: [PATCH 24/32] check if the user should leave a chat --- src/libs/actions/Policy.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 1bb152eec918..154a519fff1e 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1030,6 +1030,10 @@ function leaveWorkspace(policyID: string) { const pendingChatMembers = ReportUtils.getPendingChatMembers([sessionAccountID], [], CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); workspaceChats.forEach((report) => { + if (ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isReportOwner(report)) { + return; + } + optimisticData.push({ onyxMethod: Onyx.METHOD.MERGE, key: `${ONYXKEYS.COLLECTION.REPORT}${report?.reportID}`, @@ -1061,7 +1065,6 @@ function leaveWorkspace(policyID: string) { email: sessionEmail, authToken: sessionAuthToken, }; - API.write(WRITE_COMMANDS.LEAVE_POLICY, params, {optimisticData, successData, failureData}); } From 4de342aa75a84b327aa42e0ada7266e1e1426b68 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Tue, 9 Apr 2024 16:35:50 +0200 Subject: [PATCH 25/32] integrate clearReportNotFoundErrors --- src/libs/actions/Report.ts | 13 +++++++++++++ src/pages/home/ReportScreen.tsx | 6 +++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts index 91128ac89178..24b635d9f8bd 100644 --- a/src/libs/actions/Report.ts +++ b/src/libs/actions/Report.ts @@ -2052,6 +2052,18 @@ function clearPolicyRoomNameErrors(reportID: string) { }); } +/** + * @param reportID The reportID of the report. + */ +// eslint-disable-next-line rulesdir/no-negated-variables +function clearReportNotFoundErrors(reportID: string) { + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { + errorFields: { + notFound: null, + }, + }); +} + function setIsComposerFullSize(reportID: string, isComposerFullSize: boolean) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_IS_COMPOSER_FULL_SIZE}${reportID}`, isComposerFullSize); } @@ -3053,6 +3065,7 @@ export { toggleSubscribeToChildReport, updatePolicyRoomNameAndNavigate, clearPolicyRoomNameErrors, + clearReportNotFoundErrors, clearIOUError, subscribeToNewActionEvent, notifyNewAction, diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index 940cba181db7..be28db991e65 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -387,7 +387,11 @@ function ReportScreen({ return; } Report.updateLastVisitTime(report.reportID); - }, [report.reportID, isFocused]); + + if (report?.errorFields?.notFound) { + Report.clearReportNotFoundErrors(report.reportID); + } + }, [report.reportID, isFocused, report?.errorFields?.notFound]); const fetchReportIfNeeded = useCallback(() => { // Report ID will be empty when the reports collection is empty. From e63bb9e409cace2b8f8b7ce4da8b4ff3f36b76e4 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 10 Apr 2024 13:59:09 +0200 Subject: [PATCH 26/32] add left a workspace action --- src/CONST.ts | 1 + src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/libs/ReportActionsUtils.ts | 18 ++++++++++++++++-- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index b07b622cec05..87462ff1b21b 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -748,6 +748,7 @@ const CONST = { UPDATE_TAG_NAME: 'POLICYCHANGELOG_UPDATE_TAG_NAME', UPDATE_TIME_ENABLED: 'POLICYCHANGELOG_UPDATE_TIME_ENABLED', UPDATE_TIME_RATE: 'POLICYCHANGELOG_UPDATE_TIME_RATE', + LEAVE_POLICY: 'POLICYCHANGELOG_LEAVE_POLICY', }, ROOMCHANGELOG: { INVITE_TO_ROOM: 'INVITETOROOM', diff --git a/src/languages/en.ts b/src/languages/en.ts index 3b670f7b6ebc..4571f1b4e32f 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2144,6 +2144,7 @@ export default { users: 'users', invited: 'invited', removed: 'removed', + leftWorkspace: 'left the workspace', to: 'to', from: 'from', }, diff --git a/src/languages/es.ts b/src/languages/es.ts index 5027174b2922..523654b57e77 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2172,6 +2172,7 @@ export default { users: 'usuarios', invited: 'invitó', removed: 'eliminó', + leftWorkspace: 'dejó el espacio de trabajo', to: 'a', from: 'de', }, diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index b09f58b969f0..4e9c8c199d9d 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -157,7 +157,8 @@ function isMemberChangeAction(reportAction: OnyxEntry) { reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.REMOVE_FROM_ROOM || reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM || - reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.REMOVE_FROM_ROOM + reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.REMOVE_FROM_ROOM || + reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.LEAVE_POLICY ); } @@ -165,6 +166,10 @@ function isInviteMemberAction(reportAction: OnyxEntry) { return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM; } +function isLeaveMemberAction(reportAction: OnyxEntry) { + return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.LEAVE_POLICY; +} + function isReimbursementDeQueuedAction(reportAction: OnyxEntry): reportAction is ReportActionBase & OriginalMessageReimbursementDequeued { return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.REIMBURSEMENTDEQUEUED; } @@ -839,9 +844,18 @@ function isNotifiableReportAction(reportAction: OnyxEntry): boolea function getMemberChangeMessageElements(reportAction: OnyxEntry): readonly MemberChangeMessageElement[] { const isInviteAction = isInviteMemberAction(reportAction); + const isLeaveAction = isLeaveMemberAction(reportAction); // Currently, we only render messages when members are invited - const verb = isInviteAction ? Localize.translateLocal('workspace.invite.invited') : Localize.translateLocal('workspace.invite.removed'); + let verb = Localize.translateLocal('workspace.invite.removed'); + + if (isInviteAction) { + verb = Localize.translateLocal('workspace.invite.invited'); + } + + if (isLeaveAction) { + verb = Localize.translateLocal('workspace.invite.leftWorkspace'); + } const originalMessage = reportAction?.originalMessage as ChangeLog; const targetAccountIDs: number[] = originalMessage?.targetAccountIDs ?? []; From 6b2efac933e21125b87ce83bf0af1c322cfa8b0c Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 10 Apr 2024 14:16:18 +0200 Subject: [PATCH 27/32] use separate useEffect --- src/pages/home/ReportScreen.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx index b28dc974e2bc..307bd7f02d50 100644 --- a/src/pages/home/ReportScreen.tsx +++ b/src/pages/home/ReportScreen.tsx @@ -383,15 +383,21 @@ function ReportScreen({ }, [reportIDFromRoute, reportActionIDFromRoute]); useEffect(() => { - if (!report.reportID || !isFocused) { + if (!report.reportID) { return; } - Report.updateLastVisitTime(report.reportID); if (report?.errorFields?.notFound) { Report.clearReportNotFoundErrors(report.reportID); } - }, [report.reportID, isFocused, report?.errorFields?.notFound]); + }, [report?.errorFields?.notFound, report.reportID]); + + useEffect(() => { + if (!report.reportID || !isFocused) { + return; + } + Report.updateLastVisitTime(report.reportID); + }, [report.reportID, isFocused]); const fetchReportIfNeeded = useCallback(() => { // Report ID will be empty when the reports collection is empty. From 8ab074eb9bbb7be31984106bc27bba783e173b05 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Wed, 10 Apr 2024 14:43:51 +0200 Subject: [PATCH 28/32] show in sidebar --- src/libs/SidebarUtils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index c5439d687089..1981aa603011 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -351,6 +351,8 @@ function getOptionData({ : ` ${Localize.translate(preferredLocale, 'workspace.invite.from')}`; result.alternateText += `${preposition} ${roomName}`; } + } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.LEAVE_POLICY) { + result.alternateText = Localize.translateLocal('workspace.invite.leftWorkspace'); } else if (lastAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.REPORTPREVIEW && lastActorDisplayName && lastMessageTextFromReport) { result.alternateText = `${lastActorDisplayName}: ${lastMessageText}`; } else { From 97cab94419b1d4245351ae851ee5f4f7b58022a0 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 11 Apr 2024 12:47:13 +0200 Subject: [PATCH 29/32] omit authToken --- src/libs/API/parameters/LeavePolicyParams.ts | 1 - src/libs/actions/Policy.ts | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/libs/API/parameters/LeavePolicyParams.ts b/src/libs/API/parameters/LeavePolicyParams.ts index bc2962eebb66..ad728f06e6ee 100644 --- a/src/libs/API/parameters/LeavePolicyParams.ts +++ b/src/libs/API/parameters/LeavePolicyParams.ts @@ -1,5 +1,4 @@ type LeavePolicyParams = { - authToken: string; policyID: string; email: string; }; diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 1cffe033edda..c0310d6e597b 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -199,13 +199,11 @@ Onyx.connect({ let sessionEmail = ''; let sessionAccountID = 0; -let sessionAuthToken = ''; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { sessionEmail = val?.email ?? ''; sessionAccountID = val?.accountID ?? -1; - sessionAuthToken = val?.authToken ?? ''; }, }); @@ -1064,7 +1062,6 @@ function leaveWorkspace(policyID: string) { const params: LeavePolicyParams = { policyID, email: sessionEmail, - authToken: sessionAuthToken, }; API.write(WRITE_COMMANDS.LEAVE_POLICY, params, {optimisticData, successData, failureData}); } From 53139004e68ca1a8499f1d9fd161b09c6febd7f5 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Thu, 11 Apr 2024 13:06:34 +0200 Subject: [PATCH 30/32] leave a nested report --- src/libs/actions/Policy.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index c0310d6e597b..70dc4bcef732 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1029,7 +1029,10 @@ function leaveWorkspace(policyID: string) { const pendingChatMembers = ReportUtils.getPendingChatMembers([sessionAccountID], [], CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); workspaceChats.forEach((report) => { - if (ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isReportOwner(report)) { + const parentReport = ReportUtils.getRootParentReport(report); + const reportToCheckOwner = isEmptyObject(parentReport) ? report : parentReport; + + if (ReportUtils.isPolicyExpenseChat(report) && !ReportUtils.isReportOwner(reportToCheckOwner)) { return; } From b028a6f54f0fbc7a43d0de62eba13841ca664fa7 Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Fri, 12 Apr 2024 17:22:19 +0200 Subject: [PATCH 31/32] fix spanish string --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 56e3210e16d2..d251f6bfe5e2 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2172,7 +2172,7 @@ export default { users: 'usuarios', invited: 'invitó', removed: 'eliminó', - leftWorkspace: 'dejó el espacio de trabajo', + leftWorkspace: 'salió del espacio de trabajo', to: 'a', from: 'de', }, From 7321ad2800873756b03f841ac10beff07f7ad63d Mon Sep 17 00:00:00 2001 From: Mykhailo Kravchenko Date: Mon, 15 Apr 2024 09:27:11 +0200 Subject: [PATCH 32/32] address comments --- src/libs/ReportActionsUtils.ts | 5 ++--- src/libs/actions/Policy.ts | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index 61ac0f22ee48..e339dce5b81b 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -170,7 +170,7 @@ function isInviteMemberAction(reportAction: OnyxEntry) { return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM || reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.INVITE_TO_ROOM; } -function isLeaveMemberAction(reportAction: OnyxEntry) { +function isLeavePolicyAction(reportAction: OnyxEntry) { return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICYCHANGELOG.LEAVE_POLICY; } @@ -866,11 +866,10 @@ function isNotifiableReportAction(reportAction: OnyxEntry): boolea function getMemberChangeMessageElements(reportAction: OnyxEntry): readonly MemberChangeMessageElement[] { const isInviteAction = isInviteMemberAction(reportAction); - const isLeaveAction = isLeaveMemberAction(reportAction); + const isLeaveAction = isLeavePolicyAction(reportAction); // Currently, we only render messages when members are invited let verb = Localize.translateLocal('workspace.invite.removed'); - if (isInviteAction) { verb = Localize.translateLocal('workspace.invite.invited'); } diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 7152b2048795..74965bcaca13 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -976,7 +976,6 @@ function removeMembers(accountIDs: number[], policyID: string) { API.write(WRITE_COMMANDS.DELETE_MEMBERS_FROM_WORKSPACE, params, {optimisticData, successData, failureData}); } -/** Leave a workspace */ function leaveWorkspace(policyID: string) { const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}` as const; const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`];