Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TS migration] Migrate 'withReportAndPrivateNotesOrNotFound.js' HOC to TypeScript #34014

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
53fd25f
refactor(typescript): migrate withreportandprivatenotesornotfound
pac-guerreiro Jan 3, 2024
77ce449
refactor: apply suggestions
pac-guerreiro Jan 8, 2024
70cebb0
Merge branch 'main' into pac-guerreiro/refactor/migrate-withreportand…
pac-guerreiro Jan 9, 2024
6689538
chore(typescript): add missing import type
pac-guerreiro Jan 9, 2024
6318542
Merge branch 'main' into pac-guerreiro/refactor/migrate-withreportand…
pac-guerreiro Jan 11, 2024
3fd6d5d
refactor(typescript): apply pull request feedback
pac-guerreiro Jan 12, 2024
71389f8
Merge branch 'main' into pac-guerreiro/refactor/migrate-withreportand…
pac-guerreiro Jan 26, 2024
d1ea8c1
Merge branch 'main' into pac-guerreiro/refactor/migrate-withreportand…
pac-guerreiro Jan 29, 2024
8c2764a
chore(typescript): resolve typing errors
pac-guerreiro Jan 30, 2024
3e32f66
Merge branch 'main' into pac-guerreiro/refactor/migrate-withreportand…
pac-guerreiro Jan 30, 2024
82af310
Merge branch 'main' into pac-guerreiro/refactor/migrate-withreportand…
pac-guerreiro Jan 30, 2024
204b07b
chore(typescript): resolve typing issues
pac-guerreiro Jan 31, 2024
ca3b14a
chore(typescript): apply pull request feedback
pac-guerreiro Feb 1, 2024
af5eff3
Merge branch 'main' into pac-guerreiro/refactor/migrate-withreportand…
pac-guerreiro Feb 2, 2024
6e64db6
Merge branch 'main' into pac-guerreiro/refactor/migrate-withreportand…
pac-guerreiro Feb 5, 2024
cbc0e7b
fix: infinite loading when editing a private note
pac-guerreiro Feb 5, 2024
a6e4145
Merge branch 'main' into pac-guerreiro/refactor/migrate-withreportand…
pac-guerreiro Feb 5, 2024
657dd5f
refactor(typescript): add missing types
pac-guerreiro Feb 5, 2024
cfa5677
fix(typescript): route type issues
pac-guerreiro Feb 7, 2024
9b6db94
fix: infinite loader on private notes list
pac-guerreiro Feb 8, 2024
f58d61f
Merge branch 'main' into pac-guerreiro/refactor/migrate-withreportand…
pac-guerreiro Feb 12, 2024
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
2 changes: 1 addition & 1 deletion src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2440,7 +2440,7 @@ const updatePrivateNotes = (reportID: string, accountID: number, note: string) =
};

/** Fetches all the private notes for a given report */
function getReportPrivateNote(reportID: string) {
function getReportPrivateNote(reportID?: string) {
if (Session.isAnonymousUser()) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,87 +1,59 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import type {ComponentType, ForwardedRef, RefAttributes} from 'react';
import React, {useEffect, useMemo} from 'react';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import networkPropTypes from '@components/networkPropTypes';
import {withNetwork} from '@components/OnyxProvider';
import {type OnyxEntry, withOnyx} from 'react-native-onyx';

Check failure on line 3 in src/pages/home/report/withReportAndPrivateNotesOrNotFound.tsx

View workflow job for this annotation

GitHub Actions / lint

Prefer using a top-level type-only import instead of inline type specifiers
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import usePrevious from '@hooks/usePrevious';
import * as Report from '@libs/actions/Report';
import compose from '@libs/compose';
import getComponentDisplayName from '@libs/getComponentDisplayName';
import * as ReportUtils from '@libs/ReportUtils';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import LoadingPage from '@pages/LoadingPage';
import reportPropTypes from '@pages/reportPropTypes';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import type * as OnyxTypes from '@src/types/onyx';
import type {WithReportOrNotFoundProps} from './withReportOrNotFound';
import withReportOrNotFound from './withReportOrNotFound';

const propTypes = {
/** The HOC takes an optional ref as a prop and passes it as a ref to the wrapped component.
* That way, if a ref is passed to a component wrapped in the HOC, the ref is a reference to the wrapped component, not the HOC. */
forwardedRef: PropTypes.func,

/** The report currently being looked at */
report: reportPropTypes,

/** Information about the network */
network: networkPropTypes.isRequired,

type WithReportAndPrivateNotesOrNotFoundOnyxProps = {
/** Session of currently logged in user */
session: PropTypes.shape({
/** accountID of currently logged in user */
accountID: PropTypes.number,
}),

route: PropTypes.shape({
/** Params from the URL path */
params: PropTypes.shape({
/** reportID and accountID passed via route: /r/:reportID/notes/:accountID */
reportID: PropTypes.string,
accountID: PropTypes.string,
}),
}).isRequired,
session: OnyxEntry<OnyxTypes.Session>;
};

const defaultProps = {
forwardedRef: () => {},
report: {},
session: {
accountID: null,
},
};
type WithReportAndPrivateNotesOrNotFoundProps = WithReportAndPrivateNotesOrNotFoundOnyxProps & WithReportOrNotFoundProps;

export default function (pageTitle) {
export default function <TProps extends WithReportAndPrivateNotesOrNotFoundProps, TRef>(pageTitle: TranslationPaths) {
// eslint-disable-next-line rulesdir/no-negated-variables
return (WrappedComponent) => {
return (WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>) => {
// eslint-disable-next-line rulesdir/no-negated-variables
function WithReportAndPrivateNotesOrNotFound({forwardedRef, ...props}) {
function WithReportAndPrivateNotesOrNotFound(props: TProps & {forwardedRef: ForwardedRef<TRef>}) {
const {translate} = useLocalize();
const {route, report, network, session} = props;
const network = useNetwork();
const {route, report, session, forwardedRef} = props;
const accountID = route.params.accountID;
const isPrivateNotesFetchTriggered = !_.isUndefined(report.isLoadingPrivateNotes);
const isPrivateNotesFetchTriggered = report?.isLoadingPrivateNotes !== undefined;
const prevIsOffline = usePrevious(network.isOffline);
const isReconnecting = prevIsOffline && !network.isOffline;
const isOtherUserNote = accountID && Number(session.accountID) !== Number(accountID);
const isPrivateNotesEmpty = accountID ? _.has(lodashGet(report, ['privateNotes', accountID, 'note'], '')) : _.isEmpty(report.privateNotes);
const isOtherUserNote = !!accountID && Number(session?.accountID) !== Number(accountID);
const isPrivateNotesEmpty = accountID ? !report?.privateNotes?.[Number(accountID)].note : Object.keys(report?.privateNotes ?? {}).length === 0;

useEffect(() => {
// Do not fetch private notes if isLoadingPrivateNotes is already defined, or if network is offline.
if ((isPrivateNotesFetchTriggered && !isReconnecting) || network.isOffline) {
return;
}

Report.getReportPrivateNote(report.reportID);
Report.getReportPrivateNote(report?.reportID);
// eslint-disable-next-line react-hooks/exhaustive-deps -- do not add report.isLoadingPrivateNotes to dependencies
}, [report.reportID, network.isOffline, isPrivateNotesFetchTriggered, isReconnecting]);
}, [report?.reportID, network.isOffline, isPrivateNotesFetchTriggered, isReconnecting]);

const shouldShowFullScreenLoadingIndicator = !isPrivateNotesFetchTriggered || (isPrivateNotesEmpty && (report.isLoadingPrivateNotes || !isOtherUserNote));
const shouldShowFullScreenLoadingIndicator = !isPrivateNotesFetchTriggered || (isPrivateNotesEmpty && (report?.isLoadingPrivateNotes ?? !isOtherUserNote));

// eslint-disable-next-line rulesdir/no-negated-variables
const shouldShowNotFoundPage = useMemo(() => {
// Show not found view if the report is archived, or if the note is not of current user.
if (ReportUtils.isArchivedRoom(report) || (accountID && Number(session.accountID) !== Number(accountID))) {
if (ReportUtils.isArchivedRoom(report) || (accountID && Number(session?.accountID) !== Number(accountID))) {
return true;
}

Expand All @@ -92,7 +64,7 @@

// As notes being empty and not loading is a valid case, show not found view only in offline mode.
return network.isOffline;
}, [report, network.isOffline, accountID, session.accountID, isPrivateNotesEmpty, shouldShowFullScreenLoadingIndicator, isReconnecting]);
}, [report, network.isOffline, accountID, session?.accountID, isPrivateNotesEmpty, shouldShowFullScreenLoadingIndicator, isReconnecting]);

if (shouldShowFullScreenLoadingIndicator) {
return <LoadingPage title={translate(pageTitle)} />;
Expand All @@ -111,29 +83,24 @@
);
}

WithReportAndPrivateNotesOrNotFound.propTypes = propTypes;
WithReportAndPrivateNotesOrNotFound.defaultProps = defaultProps;
WithReportAndPrivateNotesOrNotFound.displayName = `withReportAndPrivateNotesOrNotFound(${getComponentDisplayName(WrappedComponent)})`;

// eslint-disable-next-line rulesdir/no-negated-variables
const WithReportAndPrivateNotesOrNotFoundWithRef = React.forwardRef((props, ref) => (
const WithReportAndPrivateNotesOrNotFoundWithRef = React.forwardRef((props: TProps, ref: ForwardedRef<TRef>) => (
<WithReportAndPrivateNotesOrNotFound
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));

WithReportAndPrivateNotesOrNotFoundWithRef.displayName = 'WithReportAndPrivateNotesOrNotFoundWithRef';

return compose(
withReportOrNotFound(),
withOnyx({
withOnyx<TProps, WithReportAndPrivateNotesOrNotFoundOnyxProps>({
session: {
key: ONYXKEYS.SESSION,
},
}),
withNetwork(),
withReportOrNotFound(),
)(WithReportAndPrivateNotesOrNotFoundWithRef);
};
}
16 changes: 9 additions & 7 deletions src/pages/home/report/withReportOrNotFound.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import ONYXKEYS from '@src/ONYXKEYS';
import type * as OnyxTypes from '@src/types/onyx';

type OnyxProps = {
type WithReportOrNotFoundOnyxProps = {
/** The report currently being looked at */
report: OnyxEntry<OnyxTypes.Report>;
/** The policies which the user has access to */
Expand All @@ -22,16 +22,16 @@ type OnyxProps = {
isLoadingReportData: OnyxEntry<boolean>;
};

type ComponentProps = OnyxProps & {
route: RouteProp<{params: {reportID: string}}>;
type WithReportOrNotFoundProps = WithReportOrNotFoundOnyxProps & {
route: RouteProp<{params: {reportID: string; accountID: string}}>;
};

export default function (
shouldRequireReportID = true,
): <TProps extends ComponentProps, TRef>(
): <TProps extends WithReportOrNotFoundProps, TRef>(
WrappedComponent: React.ComponentType<TProps & React.RefAttributes<TRef>>,
) => React.ComponentType<Omit<TProps & React.RefAttributes<TRef>, keyof OnyxProps>> {
return function <TProps extends ComponentProps, TRef>(WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>) {
) => React.ComponentType<Omit<TProps & React.RefAttributes<TRef>, keyof WithReportOrNotFoundOnyxProps>> {
return function <TProps extends WithReportOrNotFoundProps, TRef>(WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>) {
function WithReportOrNotFound(props: TProps, ref: ForwardedRef<TRef>) {
const contentShown = React.useRef(false);

Expand Down Expand Up @@ -73,7 +73,7 @@ export default function (

WithReportOrNotFound.displayName = `withReportOrNotFound(${getComponentDisplayName(WrappedComponent)})`;

return withOnyx<TProps & RefAttributes<TRef>, OnyxProps>({
return withOnyx<TProps & RefAttributes<TRef>, WithReportOrNotFoundOnyxProps>({
report: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID}`,
},
Expand All @@ -89,3 +89,5 @@ export default function (
})(React.forwardRef(WithReportOrNotFound));
};
}

export type {WithReportOrNotFoundProps, WithReportOrNotFoundOnyxProps};
Loading