Skip to content

Commit

Permalink
Merge pull request #38924 from fabioh8010/feature/useOnyx-testing
Browse files Browse the repository at this point in the history
Refactor some components / screens to use `useOnyx`
  • Loading branch information
roryabraham authored Apr 10, 2024
2 parents 536f71a + 0adfb7b commit 3257088
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 191 deletions.
4 changes: 1 addition & 3 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import type CONST from './CONST';
import type * as FormTypes from './types/form';
Expand Down Expand Up @@ -657,12 +656,11 @@ type OnyxFormDraftKey = keyof OnyxFormDraftValuesMapping;
type OnyxValueKey = keyof OnyxValuesMapping;

type OnyxKey = OnyxValueKey | OnyxCollectionKey | OnyxFormKey | OnyxFormDraftKey;
type OnyxValue<TOnyxKey extends OnyxKey> = TOnyxKey extends keyof OnyxCollectionValuesMapping ? OnyxCollection<OnyxValues[TOnyxKey]> : OnyxEntry<OnyxValues[TOnyxKey]>;

type MissingOnyxKeysError = `Error: Types don't match, OnyxKey type is missing: ${Exclude<AllOnyxKeys, OnyxKey>}`;
/** If this type errors, it means that the `OnyxKey` type is missing some keys. */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type AssertOnyxKeys = AssertTypesEqual<AllOnyxKeys, OnyxKey, MissingOnyxKeysError>;

export default ONYXKEYS;
export type {OnyxValues, OnyxKey, OnyxCollectionKey, OnyxValue, OnyxValueKey, OnyxFormKey, OnyxFormValuesMapping, OnyxFormDraftKey, OnyxCollectionValuesMapping};
export type {OnyxCollectionKey, OnyxCollectionValuesMapping, OnyxFormDraftKey, OnyxFormKey, OnyxFormValuesMapping, OnyxKey, OnyxValueKey, OnyxValues};
3 changes: 2 additions & 1 deletion src/components/createOnyxContext.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import Str from 'expensify-common/lib/str';
import type {ComponentType, ForwardedRef, ForwardRefExoticComponent, PropsWithoutRef, ReactNode, RefAttributes} from 'react';
import React, {createContext, forwardRef, useContext} from 'react';
import type {OnyxValue} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import getComponentDisplayName from '@libs/getComponentDisplayName';
import type {OnyxKey, OnyxValue} from '@src/ONYXKEYS';
import type {OnyxKey} from '@src/ONYXKEYS';
import type ChildrenProps from '@src/types/utils/ChildrenProps';

// Provider types
Expand Down
6 changes: 3 additions & 3 deletions src/libs/actions/FormActions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {NullishDeep, OnyxValue} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {NullishDeep} from 'react-native-onyx';
import type {OnyxFormDraftKey, OnyxFormKey, OnyxValue} from '@src/ONYXKEYS';
import type {OnyxFormDraftKey, OnyxFormKey} from '@src/ONYXKEYS';
import type * as OnyxCommon from '@src/types/onyx/OnyxCommon';

function setIsLoading(formID: OnyxFormKey, isLoading: boolean) {
Expand Down Expand Up @@ -31,4 +31,4 @@ function clearDraftValues(formID: OnyxFormKey) {
Onyx.set(`${formID}Draft`, null);
}

export {setDraftValues, setErrorFields, setErrors, clearErrors, clearErrorFields, setIsLoading, clearDraftValues};
export {clearDraftValues, clearErrorFields, clearErrors, setDraftValues, setErrorFields, setErrors, setIsLoading};
49 changes: 10 additions & 39 deletions src/pages/NewChatPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import KeyboardAvoidingView from '@components/KeyboardAvoidingView';
import OfflineIndicator from '@components/OfflineIndicator';
import {useOptionsList} from '@components/OptionListContextProvider';
Expand All @@ -25,32 +24,21 @@ import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
import type {SelectedParticipant} from '@src/types/onyx/NewGroupChatDraft';

type NewChatPageWithOnyxProps = {
/** New group chat draft data */
newGroupDraft: OnyxEntry<OnyxTypes.NewGroupChatDraft>;

/** All of the personal details for everyone */
personalDetails: OnyxEntry<OnyxTypes.PersonalDetailsList>;

betas: OnyxEntry<OnyxTypes.Beta[]>;

/** An object that holds data about which referral banners have been dismissed */
dismissedReferralBanners: OnyxEntry<OnyxTypes.DismissedReferralBanners>;

/** Whether we are searching for reports in the server */
isSearchingForReports: OnyxEntry<boolean>;
};

type NewChatPageProps = NewChatPageWithOnyxProps & {
type NewChatPageProps = {
isGroupChat?: boolean;
};

const excludedGroupEmails = CONST.EXPENSIFY_EMAILS.filter((value) => value !== CONST.EMAIL.CONCIERGE);

function NewChatPage({betas, isGroupChat, personalDetails, isSearchingForReports, dismissedReferralBanners, newGroupDraft}: NewChatPageProps) {
function NewChatPage({isGroupChat}: NewChatPageProps) {
const [dismissedReferralBanners] = useOnyx(ONYXKEYS.NVP_DISMISSED_REFERRAL_BANNERS);
const [newGroupDraft] = useOnyx(ONYXKEYS.NEW_GROUP_CHAT_DRAFT);
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
const [betas] = useOnyx(ONYXKEYS.BETAS);
const [isSearchingForReports] = useOnyx(ONYXKEYS.IS_SEARCHING_FOR_REPORTS, {initWithStoredValues: false});

const {translate} = useLocalize();

const styles = useThemeStyles();
Expand Down Expand Up @@ -311,21 +299,4 @@ function NewChatPage({betas, isGroupChat, personalDetails, isSearchingForReports

NewChatPage.displayName = 'NewChatPage';

export default withOnyx<NewChatPageProps, NewChatPageWithOnyxProps>({
dismissedReferralBanners: {
key: ONYXKEYS.NVP_DISMISSED_REFERRAL_BANNERS,
},
newGroupDraft: {
key: ONYXKEYS.NEW_GROUP_CHAT_DRAFT,
},
personalDetails: {
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
},
betas: {
key: ONYXKEYS.BETAS,
},
isSearchingForReports: {
key: ONYXKEYS.IS_SEARCHING_FOR_REPORTS,
initWithStoredValues: false,
},
})(NewChatPage);
export default NewChatPage;
107 changes: 39 additions & 68 deletions src/pages/ProfilePage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type {StackScreenProps} from '@react-navigation/stack';
import Str from 'expensify-common/lib/str';
import React, {useEffect} from 'react';
import React, {useEffect, useMemo} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import AutoUpdateTime from '@components/AutoUpdateTime';
import Avatar from '@components/Avatar';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
Expand Down Expand Up @@ -36,31 +36,11 @@ import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {PersonalDetails, PersonalDetailsList, PersonalDetailsMetadata, Report, Session} from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type {PersonalDetails, Report} from '@src/types/onyx';
import type {EmptyObject} from '@src/types/utils/EmptyObject';
import {isEmptyObject} from '@src/types/utils/EmptyObject';

type ProfilePageOnyxProps = {
/** The personal details of the person who is logged in */
personalDetails: OnyxEntry<PersonalDetailsList>;

/** Loading status of the personal details */
personalDetailsMetadata: OnyxEntry<Record<string, PersonalDetailsMetadata>>;

/** The report currently being looked at */
report: OnyxEntry<Report>;

/** The list of all reports
* ONYXKEYS.COLLECTION.REPORT is needed for report key function
*/
// eslint-disable-next-line react/no-unused-prop-types
reports: OnyxCollection<Report>;

/** Session info for the currently logged in user. */
session: OnyxEntry<Session>;
};

type ProfilePageProps = ProfilePageOnyxProps & StackScreenProps<ProfileNavigatorParamList, typeof SCREENS.PROFILE_ROOT>;
type ProfilePageProps = StackScreenProps<ProfileNavigatorParamList, typeof SCREENS.PROFILE_ROOT>;

/**
* Gets the phone number to display for SMS logins
Expand All @@ -77,7 +57,38 @@ const getPhoneNumber = ({login = '', displayName = ''}: PersonalDetails | EmptyO
return login ? Str.removeSMSDomain(login) : '';
};

function ProfilePage({personalDetails, personalDetailsMetadata, route, session, report}: ProfilePageProps) {
/**
* This function narrow down the data from Onyx to just the properties that we want to trigger a re-render of the component. This helps minimize re-rendering
* and makes the entire component more performant because it's not re-rendering when a bunch of properties change which aren't ever used in the UI.
*/
const chatReportSelector = (report: OnyxEntry<Report>): OnyxEntry<Report> =>
report && {
reportID: report.reportID,
participantAccountIDs: report.participantAccountIDs,
parentReportID: report.parentReportID,
parentReportActionID: report.parentReportActionID,
type: report.type,
chatType: report.chatType,
isPolicyExpenseChat: report.isPolicyExpenseChat,
};

function ProfilePage({route}: ProfilePageProps) {
const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {selector: chatReportSelector});
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
const [personalDetailsMetadata] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_METADATA);
const [session] = useOnyx(ONYXKEYS.SESSION);

const reportKey = useMemo(() => {
const accountID = Number(route.params?.accountID ?? 0);
const reportID = ReportUtils.getChatByParticipants([accountID], reports)?.reportID ?? '';

if ((Boolean(session) && Number(session?.accountID) === accountID) || SessionActions.isAnonymousUser() || !reportID) {
return `${ONYXKEYS.COLLECTION.REPORT}0` as const;
}
return `${ONYXKEYS.COLLECTION.REPORT}${reportID}` as const;
}, [reports, route.params?.accountID, session]);
const [report] = useOnyx(reportKey);

const styles = useThemeStyles();
const {translate, formatPhoneNumber} = useLocalize();
const accountID = Number(route.params?.accountID ?? 0);
Expand Down Expand Up @@ -247,44 +258,4 @@ function ProfilePage({personalDetails, personalDetailsMetadata, route, session,

ProfilePage.displayName = 'ProfilePage';

/**
* This function narrow down the data from Onyx to just the properties that we want to trigger a re-render of the component. This helps minimize re-rendering
* and makes the entire component more performant because it's not re-rendering when a bunch of properties change which aren't ever used in the UI.
*/
const chatReportSelector = (report: OnyxEntry<Report>): Report =>
(report && {
reportID: report.reportID,
participantAccountIDs: report.participantAccountIDs,
parentReportID: report.parentReportID,
parentReportActionID: report.parentReportActionID,
type: report.type,
chatType: report.chatType,
isPolicyExpenseChat: report.isPolicyExpenseChat,
}) as Report;

export default withOnyx<ProfilePageProps, ProfilePageOnyxProps>({
reports: {
key: ONYXKEYS.COLLECTION.REPORT,
selector: chatReportSelector,
},
personalDetails: {
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
},
personalDetailsMetadata: {
key: ONYXKEYS.PERSONAL_DETAILS_METADATA,
},
session: {
key: ONYXKEYS.SESSION,
},
report: {
key: ({route, session, reports}) => {
const accountID = Number(route.params?.accountID ?? 0);
const reportID = ReportUtils.getChatByParticipants([accountID], reports)?.reportID ?? '';

if ((Boolean(session) && Number(session?.accountID) === accountID) || SessionActions.isAnonymousUser() || !reportID) {
return `${ONYXKEYS.COLLECTION.REPORT}0`;
}
return `${ONYXKEYS.COLLECTION.REPORT}${reportID}`;
},
},
})(ProfilePage);
export default ProfilePage;
33 changes: 6 additions & 27 deletions src/pages/settings/Preferences/PreferencesPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Illustrations from '@components/Icon/Illustrations';
import LottieAnimations from '@components/LottieAnimations';
Expand All @@ -20,22 +19,12 @@ import * as User from '@userActions/User';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {PreferredTheme, PriorityMode, User as UserType} from '@src/types/onyx';

type PreferencesPageOnyxProps = {
/** The chat priority mode */
priorityMode: PriorityMode;
function PreferencesPage() {
const [priorityMode] = useOnyx(ONYXKEYS.NVP_PRIORITY_MODE);
const [user] = useOnyx(ONYXKEYS.USER);
const [preferredTheme] = useOnyx(ONYXKEYS.PREFERRED_THEME);

/** The app's color theme */
preferredTheme: PreferredTheme;

/** The details about the user that is signed in */
user: OnyxEntry<UserType>;
};

type PreferencesPageProps = PreferencesPageOnyxProps;

function PreferencesPage({priorityMode, preferredTheme, user}: PreferencesPageProps) {
const styles = useThemeStyles();
const {translate, preferredLocale} = useLocalize();
const {isSmallScreenWidth} = useWindowDimensions();
Expand Down Expand Up @@ -125,14 +114,4 @@ function PreferencesPage({priorityMode, preferredTheme, user}: PreferencesPagePr

PreferencesPage.displayName = 'PreferencesPage';

export default withOnyx<PreferencesPageProps, PreferencesPageOnyxProps>({
priorityMode: {
key: ONYXKEYS.NVP_PRIORITY_MODE,
},
user: {
key: ONYXKEYS.USER,
},
preferredTheme: {
key: ONYXKEYS.PREFERRED_THEME,
},
})(PreferencesPage);
export default PreferencesPage;
Loading

0 comments on commit 3257088

Please sign in to comment.