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
4 changes: 0 additions & 4 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,6 @@ const ROUTES = {
route: 'r/:reportID/assignee',
getRoute: (reportID: string) => `r/${reportID}/assignee` as const,
},
PRIVATE_NOTES_VIEW: {
route: 'r/:reportID/notes/:accountID',
getRoute: (reportID: string, accountID: string | number) => `r/${reportID}/notes/${accountID}` as const,
},
PRIVATE_NOTES_LIST: {
route: 'r/:reportID/notes',
getRoute: (reportID: string) => `r/${reportID}/notes` as const,
Expand Down
1 change: 0 additions & 1 deletion src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ const SCREENS = {
},

PRIVATE_NOTES: {
VIEW: 'PrivateNotes_View',
LIST: 'PrivateNotes_List',
EDIT: 'PrivateNotes_Edit',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,6 @@ const EditRequestStackNavigator = createModalStackNavigator<EditRequestNavigator
});

const PrivateNotesModalStackNavigator = createModalStackNavigator<PrivateNotesNavigatorParamList>({
[SCREENS.PRIVATE_NOTES.VIEW]: () => require('../../../pages/PrivateNotes/PrivateNotesViewPage').default as React.ComponentType,
[SCREENS.PRIVATE_NOTES.LIST]: () => require('../../../pages/PrivateNotes/PrivateNotesListPage').default as React.ComponentType,
[SCREENS.PRIVATE_NOTES.EDIT]: () => require('../../../pages/PrivateNotes/PrivateNotesEditPage').default as React.ComponentType,
});
Expand Down
1 change: 0 additions & 1 deletion src/libs/Navigation/linkingConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ const linkingConfig: LinkingOptions<RootStackParamList> = {
},
[SCREENS.RIGHT_MODAL.PRIVATE_NOTES]: {
screens: {
[SCREENS.PRIVATE_NOTES.VIEW]: ROUTES.PRIVATE_NOTES_VIEW.route,
[SCREENS.PRIVATE_NOTES.LIST]: ROUTES.PRIVATE_NOTES_LIST.route,
[SCREENS.PRIVATE_NOTES.EDIT]: ROUTES.PRIVATE_NOTES_EDIT.route,
},
Expand Down
9 changes: 1 addition & 8 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,14 +333,7 @@ type ProcessMoneyRequestHoldNavigatorParamList = {
};

type PrivateNotesNavigatorParamList = {
[SCREENS.PRIVATE_NOTES.VIEW]: {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this page removed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reportID: string;
accountID: string;
};
[SCREENS.PRIVATE_NOTES.LIST]: {
reportID: string;
accountID: string;
};
[SCREENS.PRIVATE_NOTES.LIST]: undefined;
[SCREENS.PRIVATE_NOTES.EDIT]: {
reportID: string;
accountID: string;
Expand Down
2 changes: 1 addition & 1 deletion src/libs/updateMultilineInputRange/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {TextInput} from 'react-native';

type UpdateMultilineInputRange = (input: HTMLInputElement | TextInput, shouldAutoFocus?: boolean) => void;
type UpdateMultilineInputRange = (input: HTMLInputElement | TextInput | null, shouldAutoFocus?: boolean) => void;

export default UpdateMultilineInputRange;
Original file line number Diff line number Diff line change
@@ -1,81 +1,71 @@
import {useFocusEffect} from '@react-navigation/native';
import type {StackScreenProps} from '@react-navigation/stack';
import ExpensiMark from 'expensify-common/lib/ExpensiMark';
import Str from 'expensify-common/lib/str';
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import lodashDebounce from 'lodash/debounce';
import React, {useCallback, useMemo, useRef, useState} from 'react';
import {Keyboard} from 'react-native';
import type {TextInput as TextInputRN} from 'react-native';
import type {OnyxCollection} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import FormProvider from '@components/Form/FormProvider';
import InputWrapper from '@components/Form/InputWrapper';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import TextInput from '@components/TextInput';
import withLocalize from '@components/withLocalize';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import type {PrivateNotesNavigatorParamList} from '@libs/Navigation/types';
import * as ReportUtils from '@libs/ReportUtils';
import updateMultilineInputRange from '@libs/updateMultilineInputRange';
import withReportAndPrivateNotesOrNotFound from '@pages/home/report/withReportAndPrivateNotesOrNotFound';
import personalDetailsPropType from '@pages/personalDetailsPropType';
import reportPropTypes from '@pages/reportPropTypes';
import * as Report from '@userActions/Report';
import * as ReportActions from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type {PersonalDetails, Report} from '@src/types/onyx';
import type {Note} from '@src/types/onyx/Report';

const propTypes = {
type PrivateNotesEditPageOnyxProps = {
/** All of the personal details for everyone */
personalDetailsList: PropTypes.objectOf(personalDetailsPropType),

/** The report currently being looked at */
report: reportPropTypes,
route: PropTypes.shape({
/** Params from the URL path */
params: PropTypes.shape({
/** reportID and accountID passed via route: /r/:reportID/notes */
reportID: PropTypes.string,
accountID: PropTypes.string,
}),
}).isRequired,
personalDetailsList: OnyxCollection<PersonalDetails>;
};

const defaultProps = {
report: {},
personalDetailsList: {},
};
type PrivateNotesEditPageProps = PrivateNotesEditPageOnyxProps &
StackScreenProps<PrivateNotesNavigatorParamList, typeof SCREENS.PRIVATE_NOTES.EDIT> & {
/** The report currently being looked at */
report: Report;
};

function PrivateNotesEditPage({route, personalDetailsList, report}) {
function PrivateNotesEditPage({route, personalDetailsList, report}: PrivateNotesEditPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();

// We need to edit the note in markdown format, but display it in HTML format
const parser = new ExpensiMark();
const [privateNote, setPrivateNote] = useState(
() => Report.getDraftPrivateNote(report.reportID).trim() || parser.htmlToMarkdown(lodashGet(report, ['privateNotes', route.params.accountID, 'note'], '')).trim(),
() => ReportActions.getDraftPrivateNote(report.reportID).trim() || parser.htmlToMarkdown(report?.privateNotes?.[Number(route.params.accountID)]?.note ?? '').trim(),
);

/**
* Save the draft of the private note. This debounced so that we're not ceaselessly saving your edit. Saving the draft
* allows one to navigate somewhere else and come back to the private note and still have it in edit mode.
* @param {String} newDraft
*/
const debouncedSavePrivateNote = useMemo(

@blazejkustra blazejkustra Jan 18, 2024 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove newDraft param from JSDOC (it doesn't have a description)

() =>
_.debounce((text) => {
Report.savePrivateNotesDraft(report.reportID, text);
lodashDebounce((text: string) => {
ReportActions.savePrivateNotesDraft(report.reportID, text);
}, 1000),
[report.reportID],
);

// To focus on the input field when the page loads
const privateNotesInput = useRef(null);
const focusTimeoutRef = useRef(null);
const privateNotesInput = useRef<HTMLInputElement | TextInputRN | null>(null);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const privateNotesInput = useRef<HTMLInputElement | TextInputRN | null>(null);
const privateNotesInput = useRef<TextInput>(null);
import RNTextInput from '@components/RNTextInput';

we can rename the component like this and use it for consistency across other files.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean to make it TextInput as RNTextInput instead of TextInputRN?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import the type as TextInput and import the actual component as RNTextInput does that make sense?

const focusTimeoutRef = useRef<NodeJS.Timeout | null>(null);

useFocusEffect(
useCallback(() => {
Expand All @@ -94,18 +84,18 @@ function PrivateNotesEditPage({route, personalDetailsList, report}) {
);

const savePrivateNote = () => {
const originalNote = lodashGet(report, ['privateNotes', route.params.accountID, 'note'], '');
const originalNote = report?.privateNotes?.[Number(route.params.accountID)]?.note ?? '';
let editedNote = '';
if (privateNote.trim() !== originalNote.trim()) {
editedNote = Report.handleUserDeletedLinksInHtml(privateNote.trim(), parser.htmlToMarkdown(originalNote).trim());
Report.updatePrivateNotes(report.reportID, route.params.accountID, editedNote);
editedNote = ReportActions.handleUserDeletedLinksInHtml(privateNote.trim(), parser.htmlToMarkdown(originalNote).trim());
ReportActions.updatePrivateNotes(report.reportID, Number(route.params.accountID), editedNote);
}

// We want to delete saved private note draft after saving the note
debouncedSavePrivateNote('');

Keyboard.dismiss();
if (!_.some({...report.privateNotes, [route.params.accountID]: {note: editedNote}}, (item) => item.note)) {
if (!Object.values<Note>({...report.privateNotes, [route.params.accountID]: {note: editedNote}}).some((item) => item.note)) {
ReportUtils.navigateToDetailsPage(report);
} else {
Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST.getRoute(report.reportID));
Expand All @@ -120,10 +110,11 @@ function PrivateNotesEditPage({route, personalDetailsList, report}) {
>
<HeaderWithBackButton
title={translate('privateNotes.title')}
onBackButtonPress={() => Navigation.goBack(ROUTES.PRIVATE_NOTES_VIEW.getRoute(report.reportID, route.params.accountID))}
onBackButtonPress={() => Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST.getRoute(report.reportID))}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change created a bug where we went to the wrong screen on the back after reloading. More details #40502

shouldShowBackButton
onCloseButtonPress={() => Navigation.dismissModal()}
/>
{/* @ts-expect-error TODO: Remove this once FormProvider (https://github.com/Expensify/App/issues/31972) is migrated to TypeScript. */}
<FormProvider
formID={ONYXKEYS.FORMS.PRIVATE_NOTES_FORM}
onSubmit={savePrivateNote}
Expand All @@ -133,19 +124,20 @@ function PrivateNotesEditPage({route, personalDetailsList, report}) {
>
<Text style={[styles.mb5]}>
{translate(
Str.extractEmailDomain(lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN
Str.extractEmailDomain(personalDetailsList?.[route.params.accountID]?.login ?? '') === CONST.EMAIL.GUIDES_DOMAIN
? 'privateNotes.sharedNoteMessage'
: 'privateNotes.personalNoteMessage',
)}
</Text>
<OfflineWithFeedback
errors={{
...lodashGet(report, ['privateNotes', route.params.accountID, 'errors'], ''),
...(report?.privateNotes?.[Number(route.params.accountID)]?.errors ?? ''),
}}
onClose={() => Report.clearPrivateNotesError(report.reportID, route.params.accountID)}
onClose={() => ReportActions.clearPrivateNotesError(report.reportID, Number(route.params.accountID))}
style={[styles.mb3]}
>
<InputWrapper
// @ts-expect-error TODO: Remove this once InputWrapper (https://github.com/Expensify/App/issues/31972) is migrated to TypeScript.
InputComponent={TextInput}
role={CONST.ROLE.PRESENTATION}
inputID="privateNotes"
Expand All @@ -158,7 +150,7 @@ function PrivateNotesEditPage({route, personalDetailsList, report}) {
containerStyles={[styles.autoGrowHeightMultilineInput]}
defaultValue={privateNote}
value={privateNote}
onChangeText={(text) => {
onChangeText={(text: string) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that work?

Suggested change
onChangeText={(text: string) => {
onChangeText={(text) => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without type it shows an error Parameter 'text' implicitly has an 'any'

debouncedSavePrivateNote(text);
setPrivateNote(text);
}}
Expand All @@ -177,15 +169,11 @@ function PrivateNotesEditPage({route, personalDetailsList, report}) {
}

PrivateNotesEditPage.displayName = 'PrivateNotesEditPage';
PrivateNotesEditPage.propTypes = propTypes;
PrivateNotesEditPage.defaultProps = defaultProps;

export default compose(
withLocalize,
withReportAndPrivateNotesOrNotFound('privateNotes.title'),
withOnyx({
export default withReportAndPrivateNotesOrNotFound('privateNotes.title')(
withOnyx<PrivateNotesEditPageProps, PrivateNotesEditPageOnyxProps>({
personalDetailsList: {
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
},
}),
)(PrivateNotesEditPage);
})(PrivateNotesEditPage),
);
Loading