Skip to content

Commit c387beb

Browse files
committed
fix conflcits
2 parents f53dee4 + e3a3020 commit c387beb

36 files changed

Lines changed: 404 additions & 366 deletions

.github/actions/javascript/isDeployChecklistLocked/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11816,8 +11816,8 @@ async function generateDeployChecklistBodyAndAssignees({ tag, PRList, PRListMobi
1181611816
const check = (checked) => (checked ? 'x' : ' ');
1181711817
sections.push((0, dedent_1.default)(`
1181811818
**Deployer verifications:**
11819-
- [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).
11820-
- [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).
11819+
- [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=4510228107427840&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).
11820+
- [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=4510228107427840&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).
1182111821
- [${check(isGHStatusChecked)}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.
1182211822
`).trimEnd());
1182311823
// Footer

.github/libs/DeployChecklistUtils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,8 @@ async function generateDeployChecklistBodyAndAssignees({
249249
sections.push(
250250
dedent(`
251251
**Deployer verifications:**
252-
- [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).
253-
- [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).
252+
- [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=4510228107427840&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).
253+
- [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=4510228107427840&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).
254254
- [${check(isGHStatusChecked)}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.
255255
`).trimEnd(),
256256
);

src/GlobalModals.tsx

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import React, {Suspense, useEffect, useState} from 'react';
1+
import React, {startTransition, useEffect, useState} from 'react';
22
import DelegateNoAccessModalProvider from './components/DelegateNoAccessModalProvider';
33
import EmojiPicker from './components/EmojiPicker/EmojiPicker';
44
import GrowlNotification from './components/GrowlNotification';
5-
import ProactiveAppReviewModalManager from './components/ProactiveAppReviewModalManager';
6-
import ScreenShareRequestModal from './components/ScreenShareRequestModal';
7-
import UpdateAppModal from './components/UpdateAppModal';
5+
import LazyModalSlot from './components/LazyModalSlot';
86
import * as EmojiPickerAction from './libs/actions/EmojiPickerAction';
97
import {growlRef} from './libs/Growl';
108
import * as ReportActionContextMenu from './pages/inbox/report/ContextMenu/ReportActionContextMenu';
119

1210
const LazyPopoverReportActionContextMenu = React.lazy(() => import('./pages/inbox/report/ContextMenu/PopoverReportActionContextMenu'));
11+
const LazyUpdateAppModal = React.lazy(() => import('./components/UpdateAppModal'));
12+
const LazyScreenShareRequestModal = React.lazy(() => import('./components/ScreenShareRequestModal'));
13+
const LazyProactiveAppReviewModalManager = React.lazy(() => import('./components/ProactiveAppReviewModalManager'));
1314

1415
// Maximum time (ms) the context menu mount can stay deferred before requestIdleCallback forces it to run,
1516
// guaranteeing mount even if the main thread never becomes idle.
@@ -20,40 +21,60 @@ const IDLE_CALLBACK_TIMEOUT_MS = 2000;
2021
*/
2122
function GlobalModals() {
2223
const [shouldRenderContextMenu, setShouldRenderContextMenu] = useState(false);
24+
const [shouldRenderDeferredModals, setShouldRenderDeferredModals] = useState(false);
2325

26+
// Defer loading the context menu and rare-condition modals until after startup to avoid
27+
// pulling in their dependencies (ContextMenuActions, ReportUtils, ModifiedExpenseMessage,
28+
// ProactiveAppReviewModal, etc.) and their useOnyx subscriptions during the ManualAppStartup span.
2429
useEffect(() => {
25-
// Defer loading the context menu until after startup to avoid pulling in heavy
26-
// dependencies (ContextMenuActions, ReportUtils, ModifiedExpenseMessage, etc.)
27-
// during the ManualAppStartup span.
28-
const id = requestIdleCallback(() => setShouldRenderContextMenu(true), {timeout: IDLE_CALLBACK_TIMEOUT_MS});
30+
const id = requestIdleCallback(
31+
() => {
32+
startTransition(() => {
33+
setShouldRenderContextMenu(true);
34+
setShouldRenderDeferredModals(true);
35+
});
36+
},
37+
{timeout: IDLE_CALLBACK_TIMEOUT_MS},
38+
);
39+
return () => cancelIdleCallback(id);
40+
}, []);
2941

30-
// Allow showContextMenu() to force eager mount if the user interacts before the idle callback fires.
42+
// Allow showContextMenu() to force eager mount if the user interacts before the idle callback fires.
43+
useEffect(() => {
3144
ReportActionContextMenu.registerEnsureContextMenuMounted(() => setShouldRenderContextMenu(true));
32-
33-
return () => {
34-
cancelIdleCallback(id);
35-
ReportActionContextMenu.registerEnsureContextMenuMounted(null);
36-
};
45+
return () => ReportActionContextMenu.registerEnsureContextMenuMounted(null);
3746
}, []);
3847

3948
return (
4049
<>
41-
<UpdateAppModal />
42-
{/* Those below are only available to the authenticated user. */}
4350
<GrowlNotification ref={growlRef} />
4451
<DelegateNoAccessModalProvider>
4552
{shouldRenderContextMenu && (
46-
<Suspense fallback={null}>
53+
<LazyModalSlot>
4754
{/* eslint-disable-next-line react-hooks/refs -- module-level createRef, safe to pass as ref prop */}
4855
<LazyPopoverReportActionContextMenu ref={ReportActionContextMenu.contextMenuRef} />
49-
</Suspense>
56+
</LazyModalSlot>
5057
)}
5158
</DelegateNoAccessModalProvider>
5259
{/* eslint-disable-next-line react-hooks/refs -- module-level createRef, safe to pass as ref prop */}
5360
<EmojiPicker ref={EmojiPickerAction.emojiPickerRef} />
54-
{/* Proactive app review modal shown when user has completed a trigger action */}
55-
<ProactiveAppReviewModalManager />
56-
<ScreenShareRequestModal />
61+
{shouldRenderDeferredModals && (
62+
<>
63+
{/* Order matters: BaseModal hardcodes zIndex: 1 on every modal, so DOM source order
64+
determines stacking when modals coincide. UpdateAppModal is last so the forced-update
65+
prompt sits on top if it ever overlaps with the others. */}
66+
<LazyModalSlot>
67+
{/* Proactive app review modal shown when user has completed a trigger action */}
68+
<LazyProactiveAppReviewModalManager />
69+
</LazyModalSlot>
70+
<LazyModalSlot>
71+
<LazyScreenShareRequestModal />
72+
</LazyModalSlot>
73+
<LazyModalSlot>
74+
<LazyUpdateAppModal />
75+
</LazyModalSlot>
76+
</>
77+
)}
5778
</>
5879
);
5980
}

src/components/LazyModalSlot.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React, {Suspense} from 'react';
2+
import {ErrorBoundary as ReactErrorBoundary} from 'react-error-boundary';
3+
import Log from '@libs/Log';
4+
5+
const logModalChunkFailure = (error: Error, info: {componentStack?: string | null}) =>
6+
Log.alert(`[GlobalModals] lazy chunk failure - ${error.message}`, {componentStack: info.componentStack ?? undefined}, false);
7+
8+
/**
9+
* Wraps a lazy-loaded child in its own ErrorBoundary + Suspense pair so a chunk-load failure
10+
* (or unrelated load latency) in one slot cannot tear down sibling slots or surrounding components.
11+
*/
12+
function LazyModalSlot({children}: {children: React.ReactNode}) {
13+
return (
14+
<ReactErrorBoundary
15+
fallback={null}
16+
onError={logModalChunkFailure}
17+
>
18+
<Suspense fallback={null}>{children}</Suspense>
19+
</ReactErrorBoundary>
20+
);
21+
}
22+
23+
export default LazyModalSlot;

src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ function ExpenseReportListItem<TItem extends ListItem>({
144144
goToItem: () => onSelectRow(reportItem as unknown as TItem),
145145
snapshotReport,
146146
snapshotPolicy,
147+
policy: parentPolicy,
147148
lastPaymentMethod,
148149
userBillingGracePeriodEnds,
149150
currentSearchKey,
@@ -160,6 +161,7 @@ function ExpenseReportListItem<TItem extends ListItem>({
160161
onSelectRow,
161162
snapshotReport,
162163
snapshotPolicy,
164+
parentPolicy,
163165
lastPaymentMethod,
164166
userBillingGracePeriodEnds,
165167
personalPolicyID,

src/components/Search/SearchList/ListItem/ReportListItemHeader.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
1414
import useStyleUtils from '@hooks/useStyleUtils';
1515
import useTheme from '@hooks/useTheme';
1616
import useThemeStyles from '@hooks/useThemeStyles';
17+
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
1718
import {handleActionButtonPress} from '@userActions/Search';
1819
import CONST from '@src/CONST';
1920
import ONYXKEYS from '@src/ONYXKEYS';
@@ -219,6 +220,7 @@ function ReportListItemHeader<TItem extends ListItem>({
219220
const snapshotPolicy = useMemo(() => {
220221
return (snapshot?.data?.[`${ONYXKEYS.COLLECTION.POLICY}${reportItem.policyID}`] ?? {}) as Policy;
221222
}, [snapshot, reportItem.policyID]);
223+
const [parentPolicy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getNonEmptyStringOnyxID(snapshotReport?.policyID ?? reportItem.policyID)}`);
222224
const {isDelegateAccessRestricted} = useDelegateNoAccessState();
223225
const {showDelegateNoAccessModal} = useDelegateNoAccessActions();
224226
const [amountOwed] = useOnyx(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED);
@@ -233,6 +235,7 @@ function ReportListItemHeader<TItem extends ListItem>({
233235
goToItem: () => onSelectRow(reportItem as unknown as TItem),
234236
snapshotReport,
235237
snapshotPolicy,
238+
policy: parentPolicy,
236239
lastPaymentMethod,
237240
userBillingGracePeriodEnds,
238241
currentSearchKey,

src/components/Search/SearchList/ListItem/TransactionListItem.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ function TransactionListItem<TItem extends ListItem>({
181181
goToItem: () => onSelectRow(item, transactionPreviewData),
182182
snapshotReport,
183183
snapshotPolicy,
184+
policy: parentPolicy,
184185
lastPaymentMethod,
185186
userBillingGracePeriodEnds,
186187
currentSearchKey,

src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ import ONYXKEYS from '@src/ONYXKEYS';
2929
import ROUTES from '@src/ROUTES';
3030
import type {ReportAttributesDerivedValue} from '@src/types/onyx';
3131

32-
function initializePusher(currentUserAccountID?: number, getReportAttributes?: () => ReportAttributesDerivedValue['reports'] | undefined) {
32+
function initializePusher(currentUserAccountID?: number, currentUserEmail?: string, getReportAttributes?: () => ReportAttributesDerivedValue['reports'] | undefined) {
3333
return Pusher.init({
3434
appKey: CONFIG.PUSHER.APP_KEY,
3535
cluster: CONFIG.PUSHER.CLUSTER,
3636
authEndpoint: `${CONFIG.EXPENSIFY.DEFAULT_API_ROOT}api/AuthenticatePusher?`,
3737
}).then(() => {
38-
User.subscribeToUserEvents(currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID, getReportAttributes);
38+
User.subscribeToUserEvents(currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID, currentUserEmail ?? '', getReportAttributes);
3939
});
4040
}
4141

@@ -76,8 +76,8 @@ function AuthScreensInitHandler() {
7676
return;
7777
}
7878
// This means sign in in RHP was successful, so we can subscribe to user events
79-
initializePusher(session?.accountID, () => reportAttributesRef.current);
80-
}, [session?.accountID]);
79+
initializePusher(session?.accountID, session?.email, () => reportAttributesRef.current);
80+
}, [session?.accountID, session?.email]);
8181

8282
useEffect(() => {
8383
const isLoggingInAsNewUser = !!session?.email && SessionUtils.isLoggingInAsNewUser(currentUrl, session.email);
@@ -99,7 +99,7 @@ function AuthScreensInitHandler() {
9999
});
100100
PusherConnectionManager.init();
101101

102-
initializePusher(session?.accountID, () => reportAttributesRef.current).finally(() => {
102+
initializePusher(session?.accountID, session?.email, () => reportAttributesRef.current).finally(() => {
103103
endSpan(CONST.TELEMETRY.SPAN_NAVIGATION.PUSHER_INIT);
104104
});
105105

src/libs/Navigation/AppNavigator/UserStatusHandler.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
import {useEffect} from 'react';
22
import useAutoUpdateTimezone from '@hooks/useAutoUpdateTimezone';
33
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
4-
import * as User from '@userActions/User';
4+
import {clearCustomStatus, clearDraftCustomStatus} from '@userActions/User';
55
import CONST from '@src/CONST';
66

7-
function clearStatus() {
8-
User.clearCustomStatus();
9-
User.clearDraftCustomStatus();
10-
}
11-
127
/**
138
* Component that does not render anything and owns the timezone auto-update logic and
149
* the status-clear timer effect.
@@ -21,6 +16,11 @@ function UserStatusHandler() {
2116

2217
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
2318

19+
const clearStatus = () => {
20+
clearCustomStatus(currentUserPersonalDetails.accountID);
21+
clearDraftCustomStatus();
22+
};
23+
2424
useEffect(() => {
2525
if (!currentUserPersonalDetails.status?.clearAfter) {
2626
return;
@@ -71,7 +71,7 @@ function UserStatusHandler() {
7171
}
7272

7373
clearStatus();
74-
}, [currentUserPersonalDetails.status?.clearAfter]);
74+
}, [clearStatus, currentUserPersonalDetails.status?.clearAfter]);
7575

7676
return null;
7777
}

src/libs/SubscriptionUtils.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,6 @@ Onyx.connect({
6060
},
6161
});
6262

63-
let deprecatedAllPolicies: OnyxCollection<Policy>;
64-
Onyx.connect({
65-
key: ONYXKEYS.COLLECTION.POLICY,
66-
callback: (value) => (deprecatedAllPolicies = value),
67-
waitForCollectionCallback: true,
68-
});
69-
7063
/**
7164
* @returns Whether the workspace owner's grace period is overdue.
7265
*/
@@ -489,13 +482,12 @@ function canCancelSubscription(
489482
* Whether the user's billable actions should be restricted.
490483
*/
491484
function shouldRestrictUserBillableActions(
492-
policyIDOrPolicy: string | OnyxEntry<Policy>,
485+
policy: OnyxEntry<Policy>,
493486
ownerBillingGracePeriodEnd: OnyxEntry<number>,
494487
userBillingGracePeriodEnds: OnyxCollection<BillingGraceEndPeriod>,
495488
amountOwed: OnyxEntry<number>,
496489
currentUserAccountID: number = deprecatedCurrentUserAccountID,
497490
): boolean {
498-
const policy = typeof policyIDOrPolicy === 'string' ? deprecatedAllPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyIDOrPolicy}`] : policyIDOrPolicy;
499491
const currentDate = new Date();
500492

501493
// This logic will be executed if the user is a workspace's non-owner (normal user or admin).

0 commit comments

Comments
 (0)