Skip to content

Commit

Permalink
Merge pull request #47958 from bernhardoj/fix/46990-ws-doesnt-show-wh…
Browse files Browse the repository at this point in the history
…en-created-from-another-device

Fix workspace list doesn't get updated unless user opens workspace chat
  • Loading branch information
lakchote authored Sep 2, 2024
2 parents b396e49 + 5bdd016 commit 3d8ee65
Show file tree
Hide file tree
Showing 11 changed files with 44 additions and 35 deletions.
5 changes: 3 additions & 2 deletions src/components/MoneyRequestConfirmationListFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ function MoneyRequestConfirmationListFooter({
const {translate, toLocaleDigit} = useLocalize();
const {isOffline} = useNetwork();
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email});

// A flag and a toggler for showing the rest of the form fields
const [shouldExpandFields, toggleShouldExpandFields] = useReducer((state) => !state, false);
Expand All @@ -232,8 +233,8 @@ function MoneyRequestConfirmationListFooter({
const canUpdateSenderWorkspace = useMemo(() => {
const isInvoiceRoomParticipant = selectedParticipants.some((participant) => participant.isInvoiceRoom);

return PolicyUtils.canSendInvoice(allPolicies) && !!transaction?.isFromGlobalCreate && !isInvoiceRoomParticipant;
}, [allPolicies, selectedParticipants, transaction?.isFromGlobalCreate]);
return PolicyUtils.canSendInvoice(allPolicies, currentUserLogin) && !!transaction?.isFromGlobalCreate && !isInvoiceRoomParticipant;
}, [allPolicies, currentUserLogin, selectedParticipants, transaction?.isFromGlobalCreate]);

const isTypeSend = iouType === CONST.IOU.TYPE.PAY;
const taxRates = policy?.taxRates ?? null;
Expand Down
5 changes: 2 additions & 3 deletions src/components/SettlementButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,9 @@ function SettlementButton({
const {translate} = useLocalize();
const {isOffline} = useNetwork();
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);

const primaryPolicy = useMemo(() => PolicyActions.getPrimaryPolicy(activePolicyID), [activePolicyID]);

const session = useSession();
const primaryPolicy = useMemo(() => PolicyActions.getPrimaryPolicy(activePolicyID, session?.email), [activePolicyID, session?.email]);

// The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here.
const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || -1}`);
const isInvoiceReport = (!isEmptyObject(iouReport) && ReportUtils.isInvoiceReport(iouReport)) || false;
Expand Down
22 changes: 12 additions & 10 deletions src/libs/PolicyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,19 +166,23 @@ function getPolicyBrickRoadIndicatorStatus(policy: OnyxEntry<Policy>): ValueOf<t
return undefined;
}

function getPolicyRole(policy: OnyxInputOrEntry<Policy>, currentUserLogin: string | undefined) {
return policy?.role ?? policy?.employeeList?.[currentUserLogin ?? '-1']?.role;
}

/**
* Check if the policy can be displayed
* If offline, always show the policy pending deletion.
* If online, show the policy pending deletion only if there is an error.
* Note: Using a local ONYXKEYS.NETWORK subscription will cause a delay in
* updating the screen. Passing the offline status from the component.
*/
function shouldShowPolicy(policy: OnyxEntry<Policy>, isOffline: boolean): boolean {
function shouldShowPolicy(policy: OnyxEntry<Policy>, isOffline: boolean, currentUserLogin: string | undefined): boolean {
return (
!!policy &&
(policy?.type !== CONST.POLICY.TYPE.PERSONAL || !!policy?.isJoinRequestPending) &&
(isOffline || policy?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || Object.keys(policy.errors ?? {}).length > 0) &&
!!policy?.role
!!getPolicyRole(policy, currentUserLogin)
);
}

Expand All @@ -190,14 +194,12 @@ function isExpensifyTeam(email: string | undefined): boolean {
/**
* Checks if the current user is an admin of the policy.
*/
const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, currentUserLogin?: string): boolean =>
(policy?.role ?? (currentUserLogin && policy?.employeeList?.[currentUserLogin]?.role)) === CONST.POLICY.ROLE.ADMIN;
const isPolicyAdmin = (policy: OnyxInputOrEntry<Policy>, currentUserLogin?: string): boolean => getPolicyRole(policy, currentUserLogin) === CONST.POLICY.ROLE.ADMIN;

/**
* Checks if the current user is of the role "user" on the policy.
*/
const isPolicyUser = (policy: OnyxInputOrEntry<Policy>, currentUserLogin?: string): boolean =>
(policy?.role ?? (currentUserLogin && policy?.employeeList?.[currentUserLogin]?.role)) === CONST.POLICY.ROLE.USER;
const isPolicyUser = (policy: OnyxInputOrEntry<Policy>, currentUserLogin?: string): boolean => getPolicyRole(policy, currentUserLogin) === CONST.POLICY.ROLE.USER;

/**
* Checks if the policy is a free group policy.
Expand Down Expand Up @@ -564,9 +566,9 @@ function getPolicy(policyID: string | undefined): OnyxEntry<Policy> {
}

/** Return active policies where current user is an admin */
function getActiveAdminWorkspaces(policies: OnyxCollection<Policy> | null): Policy[] {
function getActiveAdminWorkspaces(policies: OnyxCollection<Policy> | null, currentUserLogin: string | undefined): Policy[] {
const activePolicies = getActivePolicies(policies);
return activePolicies.filter((policy) => shouldShowPolicy(policy, NetworkStore.isOffline()) && isPolicyAdmin(policy));
return activePolicies.filter((policy) => shouldShowPolicy(policy, NetworkStore.isOffline(), currentUserLogin) && isPolicyAdmin(policy, currentUserLogin));
}

/** Whether the user can send invoice from the workspace */
Expand All @@ -576,8 +578,8 @@ function canSendInvoiceFromWorkspace(policyID: string | undefined): boolean {
}

/** Whether the user can send invoice */
function canSendInvoice(policies: OnyxCollection<Policy> | null): boolean {
return getActiveAdminWorkspaces(policies).length > 0;
function canSendInvoice(policies: OnyxCollection<Policy> | null, currentUserLogin: string | undefined): boolean {
return getActiveAdminWorkspaces(policies, currentUserLogin).length > 0;
// TODO: Uncomment the following line when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
// return getActiveAdminWorkspaces(policies).some((policy) => canSendInvoiceFromWorkspace(policy.id));
}
Expand Down
8 changes: 4 additions & 4 deletions src/libs/actions/Policy/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ function getPolicy(policyID: string | undefined): OnyxEntry<Policy> {
* Returns a primary policy for the user
*/
// TODO: Use getInvoicePrimaryWorkspace when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
function getPrimaryPolicy(activePolicyID?: OnyxEntry<string>): Policy | undefined {
const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies);
function getPrimaryPolicy(activePolicyID: OnyxEntry<string>, currentUserLogin: string | undefined): Policy | undefined {
const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies, currentUserLogin);
const primaryPolicy: Policy | null | undefined = activeAdminWorkspaces.find((policy) => policy.id === activePolicyID);
return primaryPolicy ?? activeAdminWorkspaces[0];
}
Expand All @@ -225,11 +225,11 @@ function hasInvoicingDetails(policy: OnyxEntry<Policy>): boolean {
/**
* Returns a primary invoice workspace for the user
*/
function getInvoicePrimaryWorkspace(activePolicyID?: OnyxEntry<string>): Policy | undefined {
function getInvoicePrimaryWorkspace(activePolicyID: OnyxEntry<string>, currentUserLogin: string | undefined): Policy | undefined {
if (PolicyUtils.canSendInvoiceFromWorkspace(activePolicyID)) {
return allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${activePolicyID ?? '-1'}`];
}
const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies);
const activeAdminWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies, currentUserLogin);
return activeAdminWorkspaces.find((policy) => PolicyUtils.canSendInvoiceFromWorkspace(policy.id));
}

Expand Down
5 changes: 3 additions & 2 deletions src/pages/WorkspaceSwitcherPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ function WorkspaceSwitcherPage() {
const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
const [reportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS);
const [policies, fetchStatus] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email});

const brickRoadsForPolicies = useMemo(() => getWorkspacesBrickRoads(reports, policies, reportActions), [reports, policies, reportActions]);
const unreadStatusesForPolicies = useMemo(() => getWorkspacesUnreadStatuses(reports), [reports]);
Expand Down Expand Up @@ -104,7 +105,7 @@ function WorkspaceSwitcherPage() {
}

return Object.values(policies)
.filter((policy) => PolicyUtils.shouldShowPolicy(policy, !!isOffline) && !policy?.isJoinRequestPending)
.filter((policy) => PolicyUtils.shouldShowPolicy(policy, !!isOffline, currentUserLogin) && !policy?.isJoinRequestPending)
.map((policy) => ({
text: policy?.name ?? '',
policyID: policy?.id ?? '-1',
Expand All @@ -123,7 +124,7 @@ function WorkspaceSwitcherPage() {
isPolicyAdmin: PolicyUtils.isPolicyAdmin(policy),
isSelected: activeWorkspaceID === policy?.id,
}));
}, [policies, isOffline, getIndicatorTypeForPolicy, hasUnreadData, activeWorkspaceID]);
}, [policies, isOffline, currentUserLogin, getIndicatorTypeForPolicy, hasUnreadData, activeWorkspaceID]);

const filteredAndSortedUserWorkspaces = useMemo<WorkspaceListItem[]>(
() =>
Expand Down
7 changes: 5 additions & 2 deletions src/pages/home/report/SystemChatReportFooterMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,17 @@ type SystemChatReportFooterMessageProps = SystemChatReportFooterMessageOnyxProps
function SystemChatReportFooterMessage({choice, policies, activePolicyID}: SystemChatReportFooterMessageProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email});

const adminChatReportID = useMemo(() => {
const adminPolicy = activePolicyID
? PolicyUtils.getPolicy(activePolicyID)
: Object.values(policies ?? {}).find((policy) => PolicyUtils.shouldShowPolicy(policy, false) && policy?.role === CONST.POLICY.ROLE.ADMIN && policy?.chatReportIDAdmins);
: Object.values(policies ?? {}).find(
(policy) => PolicyUtils.shouldShowPolicy(policy, false, currentUserLogin) && policy?.role === CONST.POLICY.ROLE.ADMIN && policy?.chatReportIDAdmins,
);

return String(adminPolicy?.chatReportIDAdmins ?? -1);
}, [activePolicyID, policies]);
}, [activePolicyID, policies, currentUserLogin]);

const [adminChatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${adminChatReportID}`);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ function FloatingActionButtonAndPopover(
const {isOffline} = useNetwork();

const {canUseSpotnanaTravel} = usePermissions();
const canSendInvoice = useMemo(() => PolicyUtils.canSendInvoice(allPolicies as OnyxCollection<OnyxTypes.Policy>), [allPolicies]);
const canSendInvoice = useMemo(() => PolicyUtils.canSendInvoice(allPolicies as OnyxCollection<OnyxTypes.Policy>, session?.email), [allPolicies, session?.email]);

const quickActionAvatars = useMemo(() => {
if (quickActionReport) {
Expand Down
5 changes: 3 additions & 2 deletions src/pages/iou/request/MoneyRequestParticipantsSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const policy = usePolicy(activePolicyID);
const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false});
const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email});
const {options, areOptionsInitialized} = useOptionsList({
shouldInitialize: didScreenTransitionEnd,
});
Expand Down Expand Up @@ -255,7 +256,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF

if (iouType === CONST.IOU.TYPE.INVOICE) {
// TODO: Use getInvoicePrimaryWorkspace when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
const policyID = option.item && ReportUtils.isInvoiceRoom(option.item) ? option.policyID : Policy.getPrimaryPolicy(activePolicyID)?.id;
const policyID = option.item && ReportUtils.isInvoiceRoom(option.item) ? option.policyID : Policy.getPrimaryPolicy(activePolicyID, currentUserLogin)?.id;
newParticipants.push({
policyID,
isSender: true,
Expand All @@ -268,7 +269,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF
onFinish();
},
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps -- we don't want to trigger this callback when iouType changes
[onFinish, onParticipantsAdded],
[onFinish, onParticipantsAdded, currentUserLogin],
);

/**
Expand Down
7 changes: 4 additions & 3 deletions src/pages/iou/request/step/IOURequestStepSendFrom.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {useMemo} from 'react';
import {withOnyx} from 'react-native-onyx';
import {useOnyx, withOnyx} from 'react-native-onyx';
import type {OnyxCollection} from 'react-native-onyx';
import * as Expensicons from '@components/Icon/Expensicons';
import SelectionList from '@components/SelectionList';
Expand Down Expand Up @@ -37,11 +37,12 @@ type IOURequestStepSendFromProps = IOURequestStepSendFromOnyxProps &
function IOURequestStepSendFrom({route, transaction, allPolicies}: IOURequestStepSendFromProps) {
const {translate} = useLocalize();
const {transactionID, backTo} = route.params;
const [currentUserLogin] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email});

const selectedWorkspace = useMemo(() => transaction?.participants?.find((participant) => participant.isSender), [transaction]);

const workspaceOptions: WorkspaceListItem[] = useMemo(() => {
const availableWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies);
const availableWorkspaces = PolicyUtils.getActiveAdminWorkspaces(allPolicies, currentUserLogin);
// TODO: Uncomment the following line when the invoices screen is ready - https://github.com/Expensify/App/issues/45175.
// .filter((policy) => PolicyUtils.canSendInvoiceFromWorkspace(policy.id));

Expand All @@ -62,7 +63,7 @@ function IOURequestStepSendFrom({route, transaction, allPolicies}: IOURequestSte
],
isSelected: selectedWorkspace?.policyID === policy.id,
}));
}, [allPolicies, selectedWorkspace]);
}, [allPolicies, currentUserLogin, selectedWorkspace]);

const navigateBack = () => {
Navigation.goBack(backTo);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/workspace/AccessOrNotFoundWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const ACCESS_VARIANTS = {
IOUUtils.isValidMoneyRequestType(iouType) &&
// Allow the user to submit the expense if we are submitting the expense in global menu or the report can create the expense
(isEmptyObject(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType)) &&
(iouType !== CONST.IOU.TYPE.INVOICE || PolicyUtils.canSendInvoice(allPolicies)),
(iouType !== CONST.IOU.TYPE.INVOICE || PolicyUtils.canSendInvoice(allPolicies, login)),
} as const satisfies Record<
string,
(policy: OnyxTypes.Policy, login: string, report: OnyxTypes.Report, allPolicies: NonNullable<OnyxCollection<OnyxTypes.Policy>> | null, iouType?: IOUType) => boolean
Expand Down
11 changes: 6 additions & 5 deletions src/pages/workspace/WorkspacesListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import WorkspacesListRow from './WorkspacesListRow';
type WorkspaceItem = Required<Pick<MenuItemProps, 'title' | 'disabled'>> &
Pick<MenuItemProps, 'brickRoadIndicator' | 'iconFill' | 'fallbackIcon'> &
Pick<OfflineWithFeedbackProps, 'errors' | 'pendingAction'> &
Pick<PolicyType, 'role' | 'type' | 'ownerAccountID'> & {
Pick<PolicyType, 'role' | 'type' | 'ownerAccountID' | 'employeeList'> & {
icon: AvatarSource;
action: () => void;
dismissError: () => void;
Expand Down Expand Up @@ -151,7 +151,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}:
*/
const getMenuItem = useCallback(
({item, index}: GetMenuItem) => {
const isAdmin = item.role === CONST.POLICY.ROLE.ADMIN;
const isAdmin = PolicyUtils.isPolicyAdmin(item as unknown as PolicyType, session?.email);
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.
Expand Down Expand Up @@ -231,7 +231,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}:
</OfflineWithFeedback>
);
},
[isLessThanMediumScreen, styles.mb3, styles.mh5, styles.ph5, styles.hoveredComponentBG, translate, styles.offlineFeedback.deleted, session?.accountID],
[isLessThanMediumScreen, styles.mb3, styles.mh5, styles.ph5, styles.hoveredComponentBG, translate, styles.offlineFeedback.deleted, session?.accountID, session?.email],
);

const listHeaderComponent = useCallback(() => {
Expand Down Expand Up @@ -312,7 +312,7 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}:
}

return Object.values(policies)
.filter((policy): policy is PolicyType => PolicyUtils.shouldShowPolicy(policy, !!isOffline))
.filter((policy): policy is PolicyType => PolicyUtils.shouldShowPolicy(policy, !!isOffline, session?.email))
.map((policy): WorkspaceItem => {
if (policy?.isJoinRequestPending && policy?.policyDetailsForNonMembers) {
const policyInfo = Object.values(policy.policyDetailsForNonMembers)[0];
Expand Down Expand Up @@ -352,10 +352,11 @@ function WorkspacesListPage({policies, reimbursementAccount, reports, session}:
ownerAccountID: policy.ownerAccountID,
role: policy.role,
type: policy.type,
employeeList: policy.employeeList,
};
})
.sort((a, b) => localeCompare(a.title, b.title));
}, [reimbursementAccount?.errors, policies, isOffline, theme.textLight, policyRooms]);
}, [reimbursementAccount?.errors, policies, isOffline, theme.textLight, policyRooms, session?.email]);

const getHeaderButton = () => (
<Button
Expand Down

0 comments on commit 3d8ee65

Please sign in to comment.