Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ function MoneyRequestReportActionsList({
const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRY_NEW_DOT);
const isTryNewDotNVPDismissed = !!tryNewDot?.classicRedirect?.dismissed;
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);
const {isDelegateAccessRestricted} = useDelegateNoAccessState();
const {showDelegateNoAccessModal} = useDelegateNoAccessActions();

Expand Down Expand Up @@ -732,15 +733,15 @@ function MoneyRequestReportActionsList({
setIsFloatingMessageCounterVisible(false);

if (!hasNewestReportAction) {
openReport({reportID: report.reportID, introSelected});
openReport({reportID: report.reportID, introSelected, betas});
reportScrollManager.scrollToEnd();
return;
}

reportScrollManager.scrollToEnd();
readActionSkipped.current = false;
readNewestAction(report.reportID, !!reportMetadata?.hasOnceLoadedReportActions);
}, [setIsFloatingMessageCounterVisible, hasNewestReportAction, reportScrollManager, report.reportID, reportMetadata?.hasOnceLoadedReportActions]);
}, [setIsFloatingMessageCounterVisible, hasNewestReportAction, reportScrollManager, report.reportID, reportMetadata?.hasOnceLoadedReportActions, introSelected, betas]);

const scrollToNewTransaction = useCallback(
(pageY: number) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function AuthScreensInitHandler() {

const [session] = useOnyx(ONYXKEYS.SESSION);
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [initialLastUpdateIDAppliedToClient] = useOnyx(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT);
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector});
Expand Down Expand Up @@ -122,7 +123,7 @@ function AuthScreensInitHandler() {
} else if (SessionUtils.didUserLogInDuringSession()) {
const reportID = getReportIDFromLink(initialURL ?? null);
if (reportID && !isAuthenticatedAtStartup) {
Report.openReport({reportID, introSelected});
Report.openReport({reportID, introSelected, betas});
// Don't want to call `openReport` again when logging out and then logging in
setIsAuthenticatedAtStartup(true);
}
Expand Down
17 changes: 13 additions & 4 deletions src/libs/actions/Report/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,9 @@

/** The current user's account ID */
currentUserAccountID?: number;

/** Beta features list. TODO: Remove optional (?) once buildPolicyData is updated (https://github.com/Expensify/App/issues/66417) */
betas?: OnyxEntry<Beta[]>;
};

type PregeneratedResponseParams = {
Expand Down Expand Up @@ -365,7 +368,7 @@
// map of reportID to all reportActions for that report
const allReportActions: OnyxCollection<ReportActions> = {};

Onyx.connect({

Check warning on line 371 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT_ACTIONS,
callback: (actions, key) => {
if (!key || !actions) {
Expand All @@ -377,7 +380,7 @@
});

let allReports: OnyxCollection<Report>;
Onyx.connect({

Check warning on line 383 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.REPORT,
waitForCollectionCallback: true,
callback: (value) => {
Expand All @@ -386,7 +389,7 @@
});

let allPersonalDetails: OnyxEntry<PersonalDetailsList> = {};
Onyx.connect({

Check warning on line 392 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (value) => {
allPersonalDetails = value ?? {};
Expand All @@ -404,7 +407,7 @@
});

let onboarding: OnyxEntry<Onboarding>;
Onyx.connect({

Check warning on line 410 in src/libs/actions/Report/index.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.NVP_ONBOARDING,
callback: (val) => {
if (Array.isArray(val)) {
Expand Down Expand Up @@ -1165,7 +1168,7 @@
* Returns the onyx data arrays and guidedSetupData string to include in the API parameters,
* or undefined if no guided setup is needed.
*/
function getGuidedSetupDataForOpenReport(introSelected: OnyxEntry<IntroSelected>): GuidedSetupDataForOpenReport | undefined {
function getGuidedSetupDataForOpenReport(introSelected: OnyxEntry<IntroSelected>, betas: OnyxEntry<Beta[]>): GuidedSetupDataForOpenReport | undefined {
const isInviteOnboardingComplete = introSelected?.isInviteOnboardingComplete ?? false;
const isOnboardingCompleted = onboarding?.hasCompletedGuidedSetupFlow ?? false;

Expand Down Expand Up @@ -1199,6 +1202,7 @@
engagementChoice: choice,
onboardingMessage,
companySize: introSelected?.companySize as OnboardingCompanySize,
betas,
});

if (!onboardingData) {
Expand Down Expand Up @@ -1244,6 +1248,7 @@
optimisticSelfDMReport,
currentUserLogin,
currentUserAccountID,
betas,
} = params;
if (!reportID) {
return;
Expand Down Expand Up @@ -1465,7 +1470,7 @@
});
}

const guidedSetup = getGuidedSetupDataForOpenReport(introSelected);
const guidedSetup = getGuidedSetupDataForOpenReport(introSelected, betas);
if (guidedSetup) {
optimisticData.push(...guidedSetup.optimisticData);
successData.push(...guidedSetup.successData);
Expand Down Expand Up @@ -1644,6 +1649,8 @@
currentUserLogin: string,
introSelected: OnyxEntry<IntroSelected>,
avatar?: File | CustomRNImageManipulatorResult,
// TODO: Remove optional (?) once buildPolicyData is updated (https://github.com/Expensify/App/issues/66417)
betas?: OnyxEntry<Beta[]>,
) {
const optimisticReport = {
reportName: CONST.REPORT.DEFAULT_REPORT_NAME,
Expand Down Expand Up @@ -1804,7 +1811,7 @@
}

// Preserve guided setup data when creating group chats
const guidedSetup = getGuidedSetupDataForOpenReport(introSelected);
const guidedSetup = getGuidedSetupDataForOpenReport(introSelected, betas);
if (guidedSetup) {
optimisticData.push(...guidedSetup.optimisticData);
successData.push(...guidedSetup.successData);
Expand Down Expand Up @@ -1975,12 +1982,14 @@
introSelected: OnyxEntry<IntroSelected>,
avatarUri?: string,
avatarFile?: File | CustomRNImageManipulatorResult | undefined,
// TODO: Remove optional (?) once buildPolicyData is updated (https://github.com/Expensify/App/issues/66417)
betas?: OnyxEntry<Beta[]>,
) {
const participantAccountIDs = PersonalDetailsUtils.getAccountIDsByLogins(userLogins);

// If we are creating a group chat then participantAccountIDs is expected to contain currentUserAccountID
const newChat = buildOptimisticGroupChatReport(participantAccountIDs, reportName, avatarUri ?? '', optimisticReportID, CONST.REPORT.NOTIFICATION_PREFERENCE.HIDDEN);
createGroupChat(newChat.reportID, userLogins, newChat, currentUserLogin, introSelected, avatarFile);
createGroupChat(newChat.reportID, userLogins, newChat, currentUserLogin, introSelected, avatarFile, betas);

navigateToReport(newChat.reportID);
}
Expand Down
14 changes: 12 additions & 2 deletions src/pages/NewChatConfirmPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ function NewChatConfirmPage() {
const [newGroupDraft, newGroupDraftMetaData] = useOnyx(ONYXKEYS.NEW_GROUP_CHAT_DRAFT);
const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);

const icons = useMemoizedLazyExpensifyIcons(['Camera']);

Expand Down Expand Up @@ -101,8 +102,17 @@ function NewChatConfirmPage() {
}

const logins: string[] = (newGroupDraft.participants ?? []).map((participant) => participant.login).filter((login): login is string => !!login);
navigateToAndCreateGroupChat(logins, newGroupDraft.reportName ?? '', personalData.login ?? '', optimisticReportID.current, introSelected, newGroupDraft.avatarUri ?? '', avatarFile);
}, [newGroupDraft, avatarFile, personalData.login, introSelected]);
navigateToAndCreateGroupChat(
logins,
newGroupDraft.reportName ?? '',
personalData.login ?? '',
optimisticReportID.current,
introSelected,
newGroupDraft.avatarUri ?? '',
avatarFile,
betas,
);
}, [newGroupDraft, avatarFile, personalData.login, introSelected, betas]);

const stashedLocalAvatarImage = newGroupDraft?.avatarUri;

Expand Down
5 changes: 3 additions & 2 deletions src/pages/Search/SearchMoneyRequestReportPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
}
Navigation.dismissModal();
}
}, [report]);

Check warning on line 109 in src/pages/Search/SearchMoneyRequestReportPage.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

React Hook useEffect has missing dependencies: 'isFocused' and 'prevReport'. Either include them or remove the dependency array

useEffect(() => {
// Update last visit time when the expense super wide RHP report is focused
Expand Down Expand Up @@ -158,6 +158,7 @@

const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`);
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);
const {reportActions: unfilteredReportActions} = usePaginatedReportActions(reportIDFromRoute);
const {transactions: allReportTransactions, violations: allReportViolations} = useTransactionsAndViolationsForReport(reportIDFromRoute);
const reportActions = useMemo(() => getFilteredReportActionsForReportView(unfilteredReportActions), [unfilteredReportActions]);
Expand Down Expand Up @@ -247,7 +248,7 @@
return;
}

openReport({reportID: reportIDFromRoute, introSelected});
openReport({reportID: reportIDFromRoute, introSelected, betas});
isInitialMountRef.current = false;

// oneTransactionID dependency handles the case when deleting a transaction:
Expand All @@ -256,7 +257,7 @@
// For more details see https://github.com/Expensify/App/pull/80107
// We don't want this hook to re-run on the every report change
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [reportIDFromRoute, transactionThreadReportID, oneTransactionID]);
}, [reportIDFromRoute, transactionThreadReportID, oneTransactionID, betas]);

useEffect(() => {
hasCreatedLegacyThreadRef.current = false;
Expand Down
2 changes: 2 additions & 0 deletions src/pages/Share/ShareDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ function ShareDetailsPage({route}: ShareDetailsPageProps) {
const [currentAttachment] = useOnyx(ONYXKEYS.SHARE_TEMP_FILE);
const [validatedFile] = useOnyx(ONYXKEYS.VALIDATED_FILE_OBJECT);
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);

const reportAttributesDerived = useReportAttributes();
const personalDetails = usePersonalDetails();
Expand Down Expand Up @@ -157,6 +158,7 @@ function ShareDetailsPage({route}: ShareDetailsPageProps) {
introSelected,
participantLoginList: displayReport.participantsList?.filter((u) => u.accountID !== personalDetail.accountID).map((u) => u.login ?? '') ?? [],
newReportObject: report,
betas,
});
}
if (report.reportID) {
Expand Down
5 changes: 3 additions & 2 deletions src/pages/TransactionDuplicate/Review.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ function TransactionDuplicateReview() {
const originalTransactionIDsListRef = useRef<string[] | null>(null);
const [transactionIDsList = getEmptyArray<string>()] = useOnyx(ONYXKEYS.TRANSACTION_THREAD_NAVIGATION_TRANSACTION_IDS);
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);

const onPreviewPressed = useCallback(
(reportID: string) => {
Expand Down Expand Up @@ -115,8 +116,8 @@ function TransactionDuplicateReview() {
if (!route.params.threadReportID || report?.reportID) {
return;
}
openReport({reportID: route.params.threadReportID, introSelected});
}, [report?.reportID, route.params.threadReportID, introSelected]);
openReport({reportID: route.params.threadReportID, introSelected, betas});
}, [report?.reportID, route.params.threadReportID, introSelected, betas]);

useEffect(() => {
if (!transactionID) {
Expand Down
20 changes: 16 additions & 4 deletions src/pages/inbox/ReportScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr
const [reportMetadata = defaultReportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportIDFromRoute}`);
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getNonEmptyStringOnyxID(reportOnyx?.policyID)}`);
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [onboarding] = useOnyx(ONYXKEYS.NVP_ONBOARDING);
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);

Expand Down Expand Up @@ -578,8 +579,19 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr
}
}

openReport({reportID: reportIDFromRoute, introSelected, reportActionID: reportActionIDFromRoute});
}, [reportMetadata.isOptimisticReport, report, isOffline, isLoadingApp, introSelected, isOnboardingCompleted, isInviteOnboardingComplete, reportIDFromRoute, reportActionIDFromRoute]);
openReport({reportID: reportIDFromRoute, introSelected, reportActionID: reportActionIDFromRoute, betas});
}, [
reportMetadata.isOptimisticReport,
report,
isOffline,
isLoadingApp,
introSelected,
isOnboardingCompleted,
isInviteOnboardingComplete,
reportIDFromRoute,
reportActionIDFromRoute,
betas,
]);

useEffect(() => {
if (!isAnonymousUser) {
Expand Down Expand Up @@ -704,12 +716,12 @@ function ReportScreen({route, navigation, isInSidePanel = false}: ReportScreenPr
if (!shouldUseNarrowLayout || !isFocused || prevIsFocused || !isChatThread(report) || !isHiddenForCurrentUser(report) || isTransactionThreadView) {
return;
}
openReport({reportID, introSelected});
openReport({reportID, introSelected, betas});

// We don't want to run this useEffect every time `report` is changed
// Excluding shouldUseNarrowLayout from the dependency list to prevent re-triggering on screen resize events.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [prevIsFocused, report?.participants, isFocused, isTransactionThreadView, reportID]);
}, [prevIsFocused, report?.participants, isFocused, isTransactionThreadView, reportID, introSelected, betas]);

useEffect(() => {
// We don't want this effect to run on the first render.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ function BaseReportActionContextMenu({
translate,
harvestReport,
introSelected,
betas,
isDelegateAccessRestricted,
showDelegateNoAccessModal,
currentUserAccountID: currentUserPersonalDetails?.accountID,
Expand Down
5 changes: 3 additions & 2 deletions src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ type ContextMenuActionPayload = {
translate: LocalizedTranslate;
harvestReport?: OnyxEntry<ReportType>;
introSelected: OnyxEntry<IntroSelected>;
betas: OnyxEntry<Beta[]>;
isDelegateAccessRestricted?: boolean;
showDelegateNoAccessModal?: () => void;
currentUserPersonalDetails: ReturnType<typeof useCurrentUserPersonalDetails>;
Expand Down Expand Up @@ -507,11 +508,11 @@ const ContextMenuActions: ContextMenuAction[] = [
icon: 'Pencil',
shouldShow: ({type, reportAction, isArchivedRoom, isChronosReport, moneyRequestAction}) =>
type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && (canEditReportAction(reportAction) || canEditReportAction(moneyRequestAction)) && !isArchivedRoom && !isChronosReport,
onPress: (closePopover, {reportID, reportAction, draftMessage, moneyRequestAction, introSelected}) => {
onPress: (closePopover, {reportID, reportAction, draftMessage, moneyRequestAction, introSelected, betas}) => {
if (isMoneyRequestAction(reportAction) || isMoneyRequestAction(moneyRequestAction)) {
const editExpense = () => {
const childReportID = reportAction?.childReportID;
openReport({reportID: childReportID, introSelected});
openReport({reportID: childReportID, introSelected, betas});
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(childReportID));
};
if (closePopover) {
Expand Down
5 changes: 3 additions & 2 deletions src/pages/inbox/report/ReportActionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ function ReportActionsList({
const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRY_NEW_DOT);
const isTryNewDotNVPDismissed = !!tryNewDot?.classicRedirect?.dismissed;
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [isScrollToBottomEnabled, setIsScrollToBottomEnabled] = useState(false);
const [actionIdToHighlight, setActionIdToHighlight] = useState('');
const [reportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${report.reportID}`);
Expand Down Expand Up @@ -659,14 +660,14 @@ function ReportActionsList({
if (!Navigation.getReportRHPActiveRoute()) {
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report.reportID, undefined, undefined, backTo));
}
openReport({reportID: report.reportID, introSelected});
openReport({reportID: report.reportID, introSelected, betas});
reportScrollManager.scrollToBottom();
return;
}
reportScrollManager.scrollToBottom();
readActionSkipped.current = false;
readNewestAction(report.reportID, !!reportMetadata?.hasOnceLoadedReportActions);
}, [setIsFloatingMessageCounterVisible, hasNewestReportAction, reportScrollManager, report.reportID, backTo, introSelected, reportMetadata?.hasOnceLoadedReportActions]);
}, [setIsFloatingMessageCounterVisible, hasNewestReportAction, reportScrollManager, report.reportID, backTo, introSelected, reportMetadata?.hasOnceLoadedReportActions, betas]);

/**
* Calculates the ideal number of report actions to render in the first render, based on the screen height and on
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export default function <TProps extends WithReportAndReportActionOrNotFoundProps
if (!shouldUseNarrowLayout || (!isEmptyObject(report) && !isEmptyObject(linkedReportAction))) {
return;
}
openReport({reportID: props.route.params.reportID, introSelected});
openReport({reportID: props.route.params.reportID, introSelected, betas});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [shouldUseNarrowLayout, props.route.params.reportID]);

Expand Down
2 changes: 1 addition & 1 deletion src/pages/inbox/report/withReportOrNotFound.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default function (shouldRequireReportID = true): <TProps extends WithRepo
return;
}

openReport({reportID: props.route.params.reportID, introSelected});
openReport({reportID: props.route.params.reportID, introSelected, betas});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [shouldFetchReport, isReportLoaded, props.route.params.reportID]);

Expand Down
2 changes: 1 addition & 1 deletion src/pages/iou/request/step/IOURequestStepDistance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ function IOURequestStepDistance({
if (!transaction?.reportID || hasRoute(transaction, true)) {
return;
}
openReport({reportID: transaction?.reportID, introSelected});
openReport({reportID: transaction?.reportID, introSelected, betas});
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/iou/request/step/IOURequestStepDistanceMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ function IOURequestStepDistanceMap({
if (!transaction?.reportID || hasRoute(transaction, true)) {
return;
}
openReport({reportID: transaction?.reportID, introSelected});
openReport({reportID: transaction?.reportID, introSelected, betas});
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export default function <TProps extends WithWritableReportOrNotFoundProps<MoneyR
const [isLoadingApp = true] = useOnyx(ONYXKEYS.IS_LOADING_APP);
const [reportDraft] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_DRAFT}${route.params.reportID}`);
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const [betas] = useOnyx(ONYXKEYS.BETAS);
const isReportArchived = useReportIsArchived(report?.reportID);

const iouTypeParamIsInvalid = !Object.values(CONST.IOU.TYPE)
Expand All @@ -85,7 +86,7 @@ export default function <TProps extends WithWritableReportOrNotFoundProps<MoneyR
if (!!report?.reportID || !route.params.reportID || !!reportDraft || !isEditing) {
return;
}
openReport({reportID: route.params.reportID, introSelected});
openReport({reportID: route.params.reportID, introSelected, betas});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

Expand Down
Loading
Loading