From c506f34623ec092de604de8af10b6e694deaf790 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Wed, 23 Aug 2023 18:02:32 +0530 Subject: [PATCH 01/29] added new route for private notes page --- src/ROUTES.js | 7 +++++++ .../AppNavigator/ModalStackNavigators.js | 18 ++++++++++++++++++ .../Navigators/RightModalNavigator.js | 4 ++++ src/libs/Navigation/linkingConfig.js | 6 ++++++ 4 files changed, 35 insertions(+) diff --git a/src/ROUTES.js b/src/ROUTES.js index c73002482171..abaebe129800 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -155,6 +155,13 @@ export default { GOOGLE_SIGN_IN: 'sign-in-with-google', DESKTOP_SIGN_IN_REDIRECT: 'desktop-signin-redirect', + // Routes related to private notes added to the report + PRIVATE_NOTES: 'r/:reportID/notes/:accountID', + getPrivateNotesRoute: (reportID, accountID) => `r/${reportID}/notes/${accountID}`, + + PRIVATE_NOTES_LIST: 'r/:reportID/notes', + getPrivateNotesListRoute: (reportID) => `r/${reportID}/notes`, + // This is a special validation URL that will take the user to /workspace/new after validation. This is used // when linking users from e.com in order to share a session in this app. ENABLE_PAYMENTS: 'enable-payments', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index aa4fcc02c906..f72ce643b274 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -725,6 +725,23 @@ const EditRequestStackNavigator = createModalStackNavigator([ }, ]); +const PrivateNotesModalStackNavigator = createModalStackNavigator([ + { + getComponent: () => { + const PrivateNotesPage = require('../../../pages/PrivateNotes/PrivateNotesPage').default; + return PrivateNotesPage; + }, + name: 'PrivateNotes_Root', + }, + { + getComponent: () => { + const PrivateNotesListPage = require('../../../pages/PrivateNotes/PrivateNotesListPage').default; + return PrivateNotesListPage; + }, + name: 'PrivateNotes_List', + }, +]); + const SignInModalStackNavigator = createModalStackNavigator([ { getComponent: () => { @@ -756,5 +773,6 @@ export { WalletStatementStackNavigator, FlagCommentStackNavigator, EditRequestStackNavigator, + PrivateNotesModalStackNavigator, SignInModalStackNavigator, }; diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js index 8e3f769ed13f..8b0f4a3a8bcf 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js @@ -93,6 +93,10 @@ function RightModalNavigator() { name="EditRequest" component={ModalStackNavigators.EditRequestStackNavigator} /> + Date: Wed, 23 Aug 2023 18:03:07 +0530 Subject: [PATCH 02/29] added texts and logic for routing and api call --- src/languages/en.js | 6 ++++ src/languages/es.js | 5 ++++ src/libs/ReportUtils.js | 26 +++++++++++++++++ src/libs/actions/Report.js | 57 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+) diff --git a/src/languages/en.js b/src/languages/en.js index c931d2b07861..93276821db7f 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -635,6 +635,12 @@ export default { passwordUpdated: 'Password updated!', allSet: 'You’re all set. Keep your new password safe.', }, + privateNotes: { + title: 'Private notes', + personalNoteMessage: 'Keep notes about this chat here. You are the only person who can add, edit or view these notes.', + sharedNoteMessage: 'Keep notes about this chat here. Expensify employee and other users on team.expensify.com domain can view these notes.', + notesUnavailable: 'No notes found for the user', + }, addPayPalMePage: { enterYourUsernameToGetPaidViaPayPal: 'Get paid back via PayPal.', payPalMe: 'PayPal.me/', diff --git a/src/languages/es.js b/src/languages/es.js index 0e6cfb43a0a5..a2a9918f65c4 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -636,6 +636,11 @@ export default { passwordUpdated: 'Contraseña actualizada!', allSet: 'Todo está listo. Guarda tu contraseña en un lugar seguro.', }, + privateNotes: { + title: 'Private notes', + personalNoteMessage: 'Keep notes about this chat here. You are the only person who can add, edit or view these notes.', + sharedNoteMessage: 'Keep notes about this chat here. Expensify employee and other users on team.expensify.com domain can view these notes.', + }, addPayPalMePage: { enterYourUsernameToGetPaidViaPayPal: 'Recibe pagos vía PayPal.', payPalMe: 'PayPal.me/', diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index f1eee1c6f258..f9b00dc366b7 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3263,6 +3263,31 @@ function getTaskAssigneeChatOnyxData(accountID, assigneeEmail, assigneeAccountID }; } +/** + * Get the last 3 transactions with receipts of an IOU report that will be displayed on the report preview + * + * @param {Object} reportPreviewAction + * @returns {Object} + */ +function navigateToPrivateNotesPage(report, accountID) { + if (_.isEmpty(report)) { + return; + } + + const privateNotes = lodashGet(report, 'privateNotes', {}); + + // Redirect the user to privateNotesList page in case there are multiple notes and accountID is not set + if (_.isEmpty(accountID) && Object.keys(privateNotes) > 1) { + Navigation.navigate(ROUTES.getPrivateNotesListRoute(reportID)); + } + + // Default the accountID to current user's accountID in case it is empty + if (_.isEmpty(accountID)) { + accountID = currentUserAccountID; + } + Navigation.navigate(ROUTES.getPrivateNotesRoute(reportID, accountID)); +} + /** * Get the last 3 transactions with receipts of an IOU report that will be displayed on the report preview * @@ -3416,5 +3441,6 @@ export { getTaskAssigneeChatOnyxData, areAllRequestsBeingSmartScanned, getReportPreviewDisplayTransactions, + navigateToPrivateNotesPage, getTransactionsWithReceipts, }; diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index ade3791fc0c7..81b6f0f92e85 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1934,6 +1934,62 @@ function flagComment(reportID, reportAction, severity) { API.write('FlagComment', parameters, {optimisticData, successData, failureData}); } +const addPrivateNotes = (reportID, accountID, note) => { + const optimisticData = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + privateNotes: { + [accountID]: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + errors: null, + note + }, + } + }, + }, + ]; + + const successData = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + privateNotes: { + [accountID]: { + pendingAction: null, + errors: null, + }, + } + }, + }, + ]; + + const failureData = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + privateNotes: { + [accountID]: { + errors: ErrorUtils.getMicroSecondOnyxError('Private notes couldn\'t be saved'), + }, + } + }, + }, + ]; + + API.write( + 'UpdateReportPrivateNote', + { + reportID, + note, + }, + {optimisticData, successData, failureData}, + ); +}; + export { addComment, addAttachment, @@ -1981,4 +2037,5 @@ export { setLastOpenedPublicRoom, flagComment, openLastOpenedPublicRoom, + addPrivateNotes, }; From 5b44c304fc20eb458ce93da38803d9c2a9a2bcf4 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Wed, 23 Aug 2023 18:03:21 +0530 Subject: [PATCH 03/29] added new pages' --- .../PrivateNotes/PrivateNotesListPage.js | 142 ++++++++++++++++++ src/pages/PrivateNotes/PrivateNotesPage.js | 134 +++++++++++++++++ 2 files changed, 276 insertions(+) create mode 100644 src/pages/PrivateNotes/PrivateNotesListPage.js create mode 100644 src/pages/PrivateNotes/PrivateNotesPage.js diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.js b/src/pages/PrivateNotes/PrivateNotesListPage.js new file mode 100644 index 000000000000..c86ac046782e --- /dev/null +++ b/src/pages/PrivateNotes/PrivateNotesListPage.js @@ -0,0 +1,142 @@ +import React, {useMemo} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import PropTypes from 'prop-types'; +import _ from 'underscore'; +import Navigation from '../../libs/Navigation/Navigation'; +import ROUTES from '../../ROUTES'; +import ONYXKEYS from '../../ONYXKEYS'; +import CONST from '../../CONST'; +import styles from '../../styles/styles'; +import compose from '../../libs/compose'; +import OfflineWithFeedback from '../../components/OfflineWithFeedback'; +import * as Expensicons from '../../components/Icon/Expensicons'; +import MenuItem from '../../components/MenuItem'; +import * as ReportUtils from '../../libs/ReportUtils'; +import * as CurrencyUtils from '../../libs/CurrencyUtils'; +import useLocalize from '../../hooks/useLocalize'; +import useNetwork from '../../hooks/useNetwork'; +import usePermissions from '../../hooks/usePermissions'; + +const propTypes = { + /** 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, +}; + +const defaultProps = { + report: {}, +}; + +/** + * Dismisses the errors on one item + * + * @param {string} policyID + * @param {string} pendingAction + */ +function dismissWorkspaceError(policyID, pendingAction) { + if (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { + Policy.clearDeleteWorkspaceError(policyID); + return; + } + + if (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { + Policy.removeWorkspace(policyID); + return; + } + throw new Error('Not implemented'); +} + +function PrivateNotesListPage({report}) { + const {translate} = useLocalize(); + + /** + * Gets the menu item for each workspace + * + * @param {Object} item + * @param {Number} index + * @returns {JSX} + */ + function getMenuItem(item, index) { + const keyTitle = item.translationKey ? translate(item.translationKey) : item.title; + const isPaymentItem = item.translationKey === 'common.wallet'; + + return ( + + + + ); + } + + /** + * Add free policies (workspaces) to the list of menu items and returns the list of menu items + * @returns {Array} the menu item list + */ + const workspaces = useMemo(() => { + const reimbursementAccountBrickRoadIndicator = !_.isEmpty(reimbursementAccount.errors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; + return _.chain(policies) + .filter((policy) => PolicyUtils.shouldShowPolicy(policy, isOffline)) + .map((policy) => ({ + title: policy.name, + icon: policy.avatar ? policy.avatar : ReportUtils.getDefaultWorkspaceAvatar(policy.name), + iconType: policy.avatar ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_ICON, + action: () => Navigation.navigate(ROUTES.getWorkspaceInitialRoute(policy.id)), + iconFill: themeColors.textLight, + fallbackIcon: Expensicons.FallbackWorkspaceAvatar, + brickRoadIndicator: reimbursementAccountBrickRoadIndicator || PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, allPolicyMembers), + pendingAction: policy.pendingAction, + errors: policy.errors, + dismissError: () => dismissWorkspaceError(policy.id, policy.pendingAction), + disabled: policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + })) + .sortBy((policy) => policy.title.toLowerCase()) + .value(); + }, [reimbursementAccount.errors, policies, isOffline, allPolicyMembers]); + + return ( + + Navigation.dismissModal()} + onBackButtonPress={() => Navigation.goBack()} + /> + {_.map(workspaces, (item, index) => getMenuItem(item, index))} + + ); +} + +PrivateNotesListPage.propTypes = propTypes; +PrivateNotesListPage.defaultProps = defaultProps; + +export default compose( + withOnyx({ + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, + }, + }), +)(PrivateNotesListPage); diff --git a/src/pages/PrivateNotes/PrivateNotesPage.js b/src/pages/PrivateNotes/PrivateNotesPage.js new file mode 100644 index 000000000000..7ea1f10fb012 --- /dev/null +++ b/src/pages/PrivateNotes/PrivateNotesPage.js @@ -0,0 +1,134 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {View, Keyboard} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; +import lodashGet from 'lodash/get'; +import withLocalize from '../../components/withLocalize'; +import ScreenWrapper from '../../components/ScreenWrapper'; +import HeaderWithBackButton from '../../components/HeaderWithBackButton'; +import Navigation from '../../libs/Navigation/Navigation'; +import styles from '../../styles/styles'; +import compose from '../../libs/compose'; +import ONYXKEYS from '../../ONYXKEYS'; +import TextInput from '../../components/TextInput'; +import CONST from '../../CONST'; +import Text from '../../components/Text'; +import ROUTES from '../../ROUTES'; +import Form from '../../components/Form'; +import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; +import reportPropTypes from '../reportPropTypes'; + +const propTypes = { + /** 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, + + /** Returns translated string for given locale and phrase */ + translate: PropTypes.func.isRequired, +}; + +const defaultProps = { + report: {}, +}; + +class PrivateNotesPage extends React.Component { + constructor(props) { + super(props); + + this.state = { + privateNote: lodashGet(this.props.report, `privateNotes.${this.props.route.params.accountID}.note`, ''), + }; + } + + componentWillUnmount() { + if (!this.focusTimeout) { + return; + } + clearTimeout(this.focusTimeout); + } + + savePrivateNote() { + Keyboard.dismiss(); + + } + + focusWelcomeMessageInput() { + this.focusTimeout = setTimeout(() => { + this.welcomeMessageInputRef.focus(); + // Below condition is needed for web, desktop and mweb only, for native cursor is set at end by default. + if (this.welcomeMessageInputRef.value && this.welcomeMessageInputRef.setSelectionRange) { + const length = this.welcomeMessageInputRef.value.length; + this.welcomeMessageInputRef.setSelectionRange(length, length); + } + }, CONST.ANIMATED_TRANSITION); + } + + render() { + + return ( + + Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST)} + > + Navigation.dismissModal()} + onBackButtonPress={() => Navigation.goBack()} + /> +
+ + {this.props.translate('workspace.inviteMessage.inviteMessagePrompt')} + + + (this.welcomeMessageInputRef = el)} + accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} + inputID="welcomeMessage" + label={this.props.translate('workspace.inviteMessage.personalMessagePrompt')} + accessibilityLabel={this.props.translate('workspace.inviteMessage.personalMessagePrompt')} + autoCompleteType="off" + autoCorrect={false} + autoGrowHeight + textAlignVertical="top" + containerStyles={[styles.autoGrowHeightMultilineInput]} + defaultValue={this.state.privateNote} + value={this.state.privateNote} + onChangeText={(text) => this.setState({privateNote: text})} + /> + +
+
+
+ ); + } +} + +PrivateNotesPage.propTypes = propTypes; +PrivateNotesPage.defaultProps = defaultProps; + +export default compose( + withLocalize, + withOnyx({ + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, + }, + }), +)(PrivateNotesPage); From c981714c932e78e176e77a953e168146c76e2449 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Thu, 24 Aug 2023 16:17:43 +0530 Subject: [PATCH 04/29] few minor fixes --- src/languages/en.js | 2 +- .../Navigators/RightModalNavigator.js | 2 +- src/libs/ReportUtils.js | 15 ++++++--------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 93276821db7f..efb14cacb9c6 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -638,7 +638,7 @@ export default { privateNotes: { title: 'Private notes', personalNoteMessage: 'Keep notes about this chat here. You are the only person who can add, edit or view these notes.', - sharedNoteMessage: 'Keep notes about this chat here. Expensify employee and other users on team.expensify.com domain can view these notes.', + sharedNoteMessage: 'Keep notes about this chat here. Expensify employees and other users on the team.expensify.com domain can view these notes.', notesUnavailable: 'No notes found for the user', }, addPayPalMePage: { diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js index 8b0f4a3a8bcf..7eb9ed4a623f 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.js @@ -94,7 +94,7 @@ function RightModalNavigator() { component={ModalStackNavigators.EditRequestStackNavigator} /> 1) { - Navigation.navigate(ROUTES.getPrivateNotesListRoute(reportID)); + if (_.isEmpty(accountID) && _.keys(privateNotes) > 1) { + Navigation.navigate(ROUTES.getPrivateNotesListRoute(report.reportID)); } // Default the accountID to current user's accountID in case it is empty - if (_.isEmpty(accountID)) { - accountID = currentUserAccountID; - } - Navigation.navigate(ROUTES.getPrivateNotesRoute(reportID, accountID)); + Navigation.navigate(ROUTES.getPrivateNotesRoute(report.reportID, _.isEmpty(accountID) ? currentUserAccountID : accountID)); } /** From 6c53902661922346892171a2b1c9360bd4e1695a Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Thu, 24 Aug 2023 20:11:35 +0530 Subject: [PATCH 05/29] added read call --- src/libs/actions/Report.js | 42 +++++++ .../PrivateNotes/PrivateNotesListPage.js | 110 +++++++++--------- 2 files changed, 100 insertions(+), 52 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 81b6f0f92e85..eb442190ee25 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1990,6 +1990,47 @@ const addPrivateNotes = (reportID, accountID, note) => { ); }; +function getReportPrivateNote(reportID) { + if (_.isEmpty(reportID)) { + return; + } + API.read( + 'GetReportPrivateNote', + { + reportID, + }, + { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + isLoadingPrivateNotes: true, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + isLoadingPrivateNotes: false, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT}${reportID}`, + value: { + isLoadingPrivateNotes: false, + }, + }, + ], + }, + ); +} + export { addComment, addAttachment, @@ -2038,4 +2079,5 @@ export { flagComment, openLastOpenedPublicRoom, addPrivateNotes, + getReportPrivateNote, }; diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.js b/src/pages/PrivateNotes/PrivateNotesListPage.js index c86ac046782e..0924b598c2f3 100644 --- a/src/pages/PrivateNotes/PrivateNotesListPage.js +++ b/src/pages/PrivateNotes/PrivateNotesListPage.js @@ -1,21 +1,27 @@ -import React, {useMemo} from 'react'; +import React, {useMemo, useEffect} from 'react'; import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; import _ from 'underscore'; +import lodashGet from 'lodash/get'; import Navigation from '../../libs/Navigation/Navigation'; -import ROUTES from '../../ROUTES'; import ONYXKEYS from '../../ONYXKEYS'; import CONST from '../../CONST'; import styles from '../../styles/styles'; import compose from '../../libs/compose'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; -import * as Expensicons from '../../components/Icon/Expensicons'; import MenuItem from '../../components/MenuItem'; import * as ReportUtils from '../../libs/ReportUtils'; -import * as CurrencyUtils from '../../libs/CurrencyUtils'; import useLocalize from '../../hooks/useLocalize'; import useNetwork from '../../hooks/useNetwork'; -import usePermissions from '../../hooks/usePermissions'; +import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator'; +import * as Report from '../../libs/actions/Report'; +import personalDetailsPropType from '../personalDetailsPropType'; +import * as UserUtils from '../../libs/UserUtils'; +import reportPropTypes from '../reportPropTypes'; +import ScreenWrapper from '../../components/ScreenWrapper'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; +import HeaderWithBackButton from '../../components/HeaderWithBackButton'; const propTypes = { /** The report currently being looked at */ @@ -28,34 +34,34 @@ const propTypes = { accountID: PropTypes.string, }), }).isRequired, + + /** Session info for the currently logged in user. */ + session: PropTypes.shape({ + /** Currently logged in user email */ + accountID: PropTypes.number, + }), + + /** All of the personal details for everyone */ + personalDetailsList: PropTypes.objectOf(personalDetailsPropType), + ...withLocalizePropTypes, }; const defaultProps = { report: {}, + session: { + accountID: null, + }, + personalDetailsList: {}, }; -/** - * Dismisses the errors on one item - * - * @param {string} policyID - * @param {string} pendingAction - */ -function dismissWorkspaceError(policyID, pendingAction) { - if (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { - Policy.clearDeleteWorkspaceError(policyID); - return; - } - - if (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD) { - Policy.removeWorkspace(policyID); - return; - } - throw new Error('Not implemented'); -} - -function PrivateNotesListPage({report}) { +function PrivateNotesListPage({session, report, personalDetailsList}) { const {translate} = useLocalize(); + useEffect(() => { + Report.getReportPrivateNote(report.reportID); + }, [report.reportID]); + + useNetwork({onReconnect: () => Report.getReportPrivateNote(report.reportID)}); /** * Gets the menu item for each workspace * @@ -65,7 +71,6 @@ function PrivateNotesListPage({report}) { */ function getMenuItem(item, index) { const keyTitle = item.translationKey ? translate(item.translationKey) : item.title; - const isPaymentItem = item.translationKey === 'common.wallet'; return ( ); @@ -96,36 +97,34 @@ function PrivateNotesListPage({report}) { * Add free policies (workspaces) to the list of menu items and returns the list of menu items * @returns {Array} the menu item list */ - const workspaces = useMemo(() => { - const reimbursementAccountBrickRoadIndicator = !_.isEmpty(reimbursementAccount.errors) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; - return _.chain(policies) - .filter((policy) => PolicyUtils.shouldShowPolicy(policy, isOffline)) - .map((policy) => ({ - title: policy.name, - icon: policy.avatar ? policy.avatar : ReportUtils.getDefaultWorkspaceAvatar(policy.name), - iconType: policy.avatar ? CONST.ICON_TYPE_AVATAR : CONST.ICON_TYPE_ICON, - action: () => Navigation.navigate(ROUTES.getWorkspaceInitialRoute(policy.id)), - iconFill: themeColors.textLight, - fallbackIcon: Expensicons.FallbackWorkspaceAvatar, - brickRoadIndicator: reimbursementAccountBrickRoadIndicator || PolicyUtils.getPolicyBrickRoadIndicatorStatus(policy, allPolicyMembers), - pendingAction: policy.pendingAction, - errors: policy.errors, - dismissError: () => dismissWorkspaceError(policy.id, policy.pendingAction), - disabled: policy.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE, + const privateNotes = useMemo(() => { + const privateNoteBrickRoadIndicator = (accountID) => !_.isEmpty(lodashGet(report, `privateNotes.${accountID}.errors`, '')) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; + return _.chain(lodashGet(report, 'privateNotes')) + .map(([accountID]) => ({ + title: lodashGet(personalDetailsList, `${accountID}.login`, ''), + icon: UserUtils.getAvatar(lodashGet(personalDetailsList, `${accountID}.avatar`, UserUtils.getDefaultAvatar(accountID))), + iconType: CONST.ICON_TYPE_AVATAR, + action: () => ReportUtils.navigateToPrivateNotesPage(report, accountID), + brickRoadIndicator: privateNoteBrickRoadIndicator(accountID), })) - .sortBy((policy) => policy.title.toLowerCase()) .value(); - }, [reimbursementAccount.errors, policies, isOffline, allPolicyMembers]); + }, [report.privateNotes]); return ( - Navigation.goBack()} + > + Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack()} - /> - {_.map(workspaces, (item, index) => getMenuItem(item, index))} + /> + {report.isLoading && } + {!report.isLoading && _.map(privateNotes, (item, index) => getMenuItem(item, index))} + ); } @@ -138,5 +137,12 @@ export default compose( report: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, }, + session: { + key: ONYXKEYS.SESSION, + }, + personalDetailsList: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, }), + withLocalize, )(PrivateNotesListPage); From deb393ab21d8a248741b6285b34607bd86bbd05d Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Sun, 27 Aug 2023 03:43:19 +0530 Subject: [PATCH 06/29] tested private notes list page --- .../PrivateNotes/PrivateNotesListPage.js | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.js b/src/pages/PrivateNotes/PrivateNotesListPage.js index 0924b598c2f3..f0fec127a083 100644 --- a/src/pages/PrivateNotes/PrivateNotesListPage.js +++ b/src/pages/PrivateNotes/PrivateNotesListPage.js @@ -12,7 +12,6 @@ import OfflineWithFeedback from '../../components/OfflineWithFeedback'; import MenuItem from '../../components/MenuItem'; import * as ReportUtils from '../../libs/ReportUtils'; import useLocalize from '../../hooks/useLocalize'; -import useNetwork from '../../hooks/useNetwork'; import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator'; import * as Report from '../../libs/actions/Report'; import personalDetailsPropType from '../personalDetailsPropType'; @@ -22,6 +21,8 @@ import ScreenWrapper from '../../components/ScreenWrapper'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; +import { withNetwork } from '../../components/OnyxProvider'; +import networkPropTypes from '../../components/networkPropTypes'; const propTypes = { /** The report currently being looked at */ @@ -43,6 +44,9 @@ const propTypes = { /** All of the personal details for everyone */ personalDetailsList: PropTypes.objectOf(personalDetailsPropType), + + /** Information about the network */ + network: networkPropTypes.isRequired, ...withLocalizePropTypes, }; @@ -54,14 +58,16 @@ const defaultProps = { personalDetailsList: {}, }; -function PrivateNotesListPage({session, report, personalDetailsList}) { +function PrivateNotesListPage({report, personalDetailsList, network, session}) { const {translate} = useLocalize(); useEffect(() => { + if (network.isOffline) { + return; + } Report.getReportPrivateNote(report.reportID); - }, [report.reportID]); + }, [report.reportID, network.isOffline]); - useNetwork({onReconnect: () => Report.getReportPrivateNote(report.reportID)}); /** * Gets the menu item for each workspace * @@ -94,26 +100,26 @@ function PrivateNotesListPage({session, report, personalDetailsList}) { } /** - * Add free policies (workspaces) to the list of menu items and returns the list of menu items + * Returns a list of private notes on the given chat report * @returns {Array} the menu item list */ const privateNotes = useMemo(() => { const privateNoteBrickRoadIndicator = (accountID) => !_.isEmpty(lodashGet(report, `privateNotes.${accountID}.errors`, '')) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; return _.chain(lodashGet(report, 'privateNotes')) - .map(([accountID]) => ({ - title: lodashGet(personalDetailsList, `${accountID}.login`, ''), - icon: UserUtils.getAvatar(lodashGet(personalDetailsList, `${accountID}.avatar`, UserUtils.getDefaultAvatar(accountID))), - iconType: CONST.ICON_TYPE_AVATAR, - action: () => ReportUtils.navigateToPrivateNotesPage(report, accountID), - brickRoadIndicator: privateNoteBrickRoadIndicator(accountID), - })) + .map((privateNote, accountID) => ({ + title: Number(lodashGet(session, 'accountID', null)) === accountID ? 'Your note' : lodashGet(personalDetailsList, `${accountID}.login`, ''), + icon: UserUtils.getAvatar(lodashGet(personalDetailsList, `${accountID}.avatar`, UserUtils.getDefaultAvatar(accountID)), accountID), + iconType: CONST.ICON_TYPE_AVATAR, + action: () => ReportUtils.navigateToPrivateNotesPage(report, accountID), + brickRoadIndicator: privateNoteBrickRoadIndicator(accountID), + })) .value(); - }, [report.privateNotes]); + }, [report, personalDetailsList, session]); return ( Navigation.goBack()} > Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack()} /> - {report.isLoading && } - {!report.isLoading && _.map(privateNotes, (item, index) => getMenuItem(item, index))} + {report.isLoadingPrivateNotes && _.isEmpty(lodashGet(report, 'privateNotes', [])) ? : _.map(privateNotes, (item, index) => getMenuItem(item, index))} ); @@ -133,6 +138,7 @@ PrivateNotesListPage.propTypes = propTypes; PrivateNotesListPage.defaultProps = defaultProps; export default compose( + withLocalize, withOnyx({ report: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, @@ -144,5 +150,5 @@ export default compose( key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, }), - withLocalize, + withNetwork(), )(PrivateNotesListPage); From 3cfea9c837b70b8e05e927da53499680de2291a6 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Sun, 27 Aug 2023 04:55:13 +0530 Subject: [PATCH 07/29] added private notes compose page --- src/ONYXKEYS.ts | 1 + src/languages/en.js | 1 + src/libs/actions/Report.js | 4 +- src/pages/PrivateNotes/PrivateNotesPage.js | 48 +++++++++++++++++----- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 074a5e99e6b1..4eec895798a9 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -272,6 +272,7 @@ const ONYXKEYS = { SETTINGS_STATUS_SET_FORM: 'settingsStatusSetForm', SETTINGS_STATUS_CLEAR_AFTER_FORM: 'settingsStatusClearAfterForm', SETTINGS_STATUS_SET_CLEAR_AFTER_FORM: 'settingsStatusSetClearAfterForm', + PRIVATE_NOTES_FORM: 'privateNotesForm', }, } as const; diff --git a/src/languages/en.js b/src/languages/en.js index efb14cacb9c6..87cbe0e1681e 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -640,6 +640,7 @@ export default { personalNoteMessage: 'Keep notes about this chat here. You are the only person who can add, edit or view these notes.', sharedNoteMessage: 'Keep notes about this chat here. Expensify employees and other users on the team.expensify.com domain can view these notes.', notesUnavailable: 'No notes found for the user', + composerLabel: 'Notes', }, addPayPalMePage: { enterYourUsernameToGetPaidViaPayPal: 'Get paid back via PayPal.', diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index eb442190ee25..4cd84ad0e85b 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1934,7 +1934,7 @@ function flagComment(reportID, reportAction, severity) { API.write('FlagComment', parameters, {optimisticData, successData, failureData}); } -const addPrivateNotes = (reportID, accountID, note) => { +const updatePrivateNotes = (reportID, accountID, note) => { const optimisticData = [ { onyxMethod: Onyx.METHOD.MERGE, @@ -2078,6 +2078,6 @@ export { setLastOpenedPublicRoom, flagComment, openLastOpenedPublicRoom, - addPrivateNotes, + updatePrivateNotes, getReportPrivateNote, }; diff --git a/src/pages/PrivateNotes/PrivateNotesPage.js b/src/pages/PrivateNotes/PrivateNotesPage.js index 7ea1f10fb012..62a190590b0d 100644 --- a/src/pages/PrivateNotes/PrivateNotesPage.js +++ b/src/pages/PrivateNotes/PrivateNotesPage.js @@ -18,8 +18,16 @@ import ROUTES from '../../ROUTES'; import Form from '../../components/Form'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import reportPropTypes from '../reportPropTypes'; +import personalDetailsPropType from '../personalDetailsPropType'; +import Str from 'expensify-common/lib/str'; +import RenderHTML from '../../components/RenderHTML'; +import { PressableWithoutFeedback } from '../../components/Pressable'; +import * as Report from '../../libs/actions/Report'; const propTypes = { + /** All of the personal details for everyone */ + personalDetailsList: PropTypes.objectOf(personalDetailsPropType), + /** The report currently being looked at */ report: reportPropTypes, route: PropTypes.shape({ @@ -31,12 +39,21 @@ const propTypes = { }), }).isRequired, + /** Session of currently logged in user */ + session: PropTypes.shape({ + /** Currently logged in user authToken */ + authToken: PropTypes.string, + }), + /** Returns translated string for given locale and phrase */ translate: PropTypes.func.isRequired, }; const defaultProps = { report: {}, + session: { + accountID: null, + } }; class PrivateNotesPage extends React.Component { @@ -45,6 +62,7 @@ class PrivateNotesPage extends React.Component { this.state = { privateNote: lodashGet(this.props.report, `privateNotes.${this.props.route.params.accountID}.note`, ''), + editMode: false, }; } @@ -56,6 +74,7 @@ class PrivateNotesPage extends React.Component { } savePrivateNote() { + Report.updatePrivateNotes(this.props.reportID, this.props.route.params.accountID, this.state.privateNote); Keyboard.dismiss(); } @@ -72,38 +91,38 @@ class PrivateNotesPage extends React.Component { } render() { - return ( Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST)} > Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack()} />
- {this.props.translate('workspace.inviteMessage.inviteMessagePrompt')} + {this.props.translate(Str.extractEmailDomain(lodashGet(this.props.personalDetailsList, [this.props.route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN ? 'privateNotes.sharedNoteMessage' : 'privateNotes.personalNoteMessage')} - + { + this.state.editMode ? (this.welcomeMessageInputRef = el)} accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} - inputID="welcomeMessage" - label={this.props.translate('workspace.inviteMessage.personalMessagePrompt')} - accessibilityLabel={this.props.translate('workspace.inviteMessage.personalMessagePrompt')} + inputID="privateNotes" + label={this.props.translate('privateNotes.composerLabel')} + accessibilityLabel={this.props.translate('privateNotes.title')} autoCompleteType="off" autoCorrect={false} autoGrowHeight @@ -113,7 +132,10 @@ class PrivateNotesPage extends React.Component { value={this.state.privateNote} onChangeText={(text) => this.setState({privateNote: text})} /> - + : + + + }
@@ -130,5 +152,11 @@ export default compose( report: { key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, }, + session: { + key: ONYXKEYS.SESSION, + }, + personalDetailsList: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, }), )(PrivateNotesPage); From f9033dcb7f4732e48f95cb9a0ee67c29b9d00746 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Sun, 27 Aug 2023 05:38:39 +0530 Subject: [PATCH 08/29] refactored to functional code and testing compose page --- .../PrivateNotes/PrivateNotesListPage.js | 44 ++--- src/pages/PrivateNotes/PrivateNotesPage.js | 172 +++++++++--------- 2 files changed, 111 insertions(+), 105 deletions(-) diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.js b/src/pages/PrivateNotes/PrivateNotesListPage.js index f0fec127a083..41f9cb8cc558 100644 --- a/src/pages/PrivateNotes/PrivateNotesListPage.js +++ b/src/pages/PrivateNotes/PrivateNotesListPage.js @@ -1,5 +1,5 @@ -import React, {useMemo, useEffect} from 'react'; -import {withOnyx} from 'react-native-onyx'; +import React, { useMemo, useEffect } from 'react'; +import { withOnyx } from 'react-native-onyx'; import PropTypes from 'prop-types'; import _ from 'underscore'; import lodashGet from 'lodash/get'; @@ -18,7 +18,7 @@ import personalDetailsPropType from '../personalDetailsPropType'; import * as UserUtils from '../../libs/UserUtils'; import reportPropTypes from '../reportPropTypes'; import ScreenWrapper from '../../components/ScreenWrapper'; -import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; +import withLocalize, { withLocalizePropTypes } from '../../components/withLocalize'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; import { withNetwork } from '../../components/OnyxProvider'; @@ -38,16 +38,16 @@ const propTypes = { /** Session info for the currently logged in user. */ session: PropTypes.shape({ - /** Currently logged in user email */ + /** Currently logged in user accountID */ accountID: PropTypes.number, }), - /** All of the personal details for everyone */ - personalDetailsList: PropTypes.objectOf(personalDetailsPropType), + /** All of the personal details for everyone */ + personalDetailsList: PropTypes.objectOf(personalDetailsPropType), - /** Information about the network */ + /** Information about the network */ network: networkPropTypes.isRequired, - ...withLocalizePropTypes, + ...withLocalizePropTypes, }; const defaultProps = { @@ -58,8 +58,8 @@ const defaultProps = { personalDetailsList: {}, }; -function PrivateNotesListPage({report, personalDetailsList, network, session}) { - const {translate} = useLocalize(); +function PrivateNotesListPage({ report, personalDetailsList, network, session }) { + const { translate } = useLocalize(); useEffect(() => { if (network.isOffline) { @@ -107,12 +107,12 @@ function PrivateNotesListPage({report, personalDetailsList, network, session}) { const privateNoteBrickRoadIndicator = (accountID) => !_.isEmpty(lodashGet(report, `privateNotes.${accountID}.errors`, '')) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; return _.chain(lodashGet(report, 'privateNotes')) .map((privateNote, accountID) => ({ - title: Number(lodashGet(session, 'accountID', null)) === accountID ? 'Your note' : lodashGet(personalDetailsList, `${accountID}.login`, ''), - icon: UserUtils.getAvatar(lodashGet(personalDetailsList, `${accountID}.avatar`, UserUtils.getDefaultAvatar(accountID)), accountID), - iconType: CONST.ICON_TYPE_AVATAR, - action: () => ReportUtils.navigateToPrivateNotesPage(report, accountID), - brickRoadIndicator: privateNoteBrickRoadIndicator(accountID), - })) + title: Number(lodashGet(session, 'accountID', null)) === accountID ? 'Your note' : lodashGet(personalDetailsList, `${accountID}.login`, ''), + icon: UserUtils.getAvatar(lodashGet(personalDetailsList, `${accountID}.avatar`, UserUtils.getDefaultAvatar(accountID)), accountID), + iconType: CONST.ICON_TYPE_AVATAR, + action: () => ReportUtils.navigateToPrivateNotesPage(report, accountID), + brickRoadIndicator: privateNoteBrickRoadIndicator(accountID), + })) .value(); }, [report, personalDetailsList, session]); @@ -123,12 +123,12 @@ function PrivateNotesListPage({report, personalDetailsList, network, session}) { onBackButtonPress={() => Navigation.goBack()} > Navigation.dismissModal()} - onBackButtonPress={() => Navigation.goBack()} + title={translate('privateNotes.title')} + shouldShowBackButton + onCloseButtonPress={() => Navigation.dismissModal()} + onBackButtonPress={() => Navigation.goBack()} /> - {report.isLoadingPrivateNotes && _.isEmpty(lodashGet(report, 'privateNotes', [])) ? : _.map(privateNotes, (item, index) => getMenuItem(item, index))} + {report.isLoadingPrivateNotes && _.isEmpty(lodashGet(report, 'privateNotes', [])) ? : _.map(privateNotes, (item, index) => getMenuItem(item, index))} ); @@ -141,7 +141,7 @@ export default compose( withLocalize, withOnyx({ report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, + key: ({ route }) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, }, session: { key: ONYXKEYS.SESSION, diff --git a/src/pages/PrivateNotes/PrivateNotesPage.js b/src/pages/PrivateNotes/PrivateNotesPage.js index 62a190590b0d..46e21d1d2ead 100644 --- a/src/pages/PrivateNotes/PrivateNotesPage.js +++ b/src/pages/PrivateNotes/PrivateNotesPage.js @@ -1,9 +1,9 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; -import {View, Keyboard} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import { View, Keyboard } from 'react-native'; +import { withOnyx } from 'react-native-onyx'; import lodashGet from 'lodash/get'; +import Str from 'expensify-common/lib/str'; import withLocalize from '../../components/withLocalize'; import ScreenWrapper from '../../components/ScreenWrapper'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; @@ -19,14 +19,13 @@ import Form from '../../components/Form'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import reportPropTypes from '../reportPropTypes'; import personalDetailsPropType from '../personalDetailsPropType'; -import Str from 'expensify-common/lib/str'; import RenderHTML from '../../components/RenderHTML'; -import { PressableWithoutFeedback } from '../../components/Pressable'; +import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback'; import * as Report from '../../libs/actions/Report'; const propTypes = { - /** All of the personal details for everyone */ - personalDetailsList: PropTypes.objectOf(personalDetailsPropType), + /** All of the personal details for everyone */ + personalDetailsList: PropTypes.objectOf(personalDetailsPropType), /** The report currently being looked at */ report: reportPropTypes, @@ -41,8 +40,8 @@ const propTypes = { /** Session of currently logged in user */ session: PropTypes.shape({ - /** Currently logged in user authToken */ - authToken: PropTypes.string, + /** Currently logged in user accountID */ + accountID: PropTypes.number, }), /** Returns translated string for given locale and phrase */ @@ -53,95 +52,102 @@ const defaultProps = { report: {}, session: { accountID: null, - } + }, + personalDetailsList: {}, }; -class PrivateNotesPage extends React.Component { - constructor(props) { - super(props); - - this.state = { - privateNote: lodashGet(this.props.report, `privateNotes.${this.props.route.params.accountID}.note`, ''), - editMode: false, - }; - } - - componentWillUnmount() { - if (!this.focusTimeout) { - return; - } - clearTimeout(this.focusTimeout); - } +const PrivateNotesPage = ({route, personalDetailsList, session, report}) => { + const [privateNote, setPrivateNote] = useState( + lodashGet(report, `privateNotes.${route.params.accountID}.note`, ''), + ); + const [editMode, setEditMode] = useState(false); + const isCurrentUser = Number(session.accountID) === Number(route.params.accountID); - savePrivateNote() { - Report.updatePrivateNotes(this.props.reportID, this.props.route.params.accountID, this.state.privateNote); + const savePrivateNote = () => { + Report.updatePrivateNotes(report.reportID, route.params.accountID, privateNote); Keyboard.dismiss(); - - } + }; - focusWelcomeMessageInput() { - this.focusTimeout = setTimeout(() => { - this.welcomeMessageInputRef.focus(); - // Below condition is needed for web, desktop and mweb only, for native cursor is set at end by default. - if (this.welcomeMessageInputRef.value && this.welcomeMessageInputRef.setSelectionRange) { - const length = this.welcomeMessageInputRef.value.length; - this.welcomeMessageInputRef.setSelectionRange(length, length); - } - }, CONST.ANIMATED_TRANSITION); - } + const switchToEditMode = () => { + if (isCurrentUser) { + setEditMode(true); + } + }; - render() { - return ( - - Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST)} + return ( + + Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST)} + > + Navigation.dismissModal()} + onBackButtonPress={() => Navigation.goBack()} + /> +
- Navigation.dismissModal()} - onBackButtonPress={() => Navigation.goBack()} - /> - - - {this.props.translate(Str.extractEmailDomain(lodashGet(this.props.personalDetailsList, [this.props.route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN ? 'privateNotes.sharedNoteMessage' : 'privateNotes.personalNoteMessage')} - - { - this.state.editMode ? + + + {translate( + Str.extractEmailDomain( + lodashGet( + personalDetailsList, + [route.params.accountID, 'login'], + '', + ), + ) === CONST.EMAIL.GUIDES_DOMAIN + ? 'privateNotes.sharedNoteMessage' + : 'privateNotes.personalNoteMessage', + )} + + + {editMode ? ( + (this.welcomeMessageInputRef = el)} + ref={(el) => { + this.welcomeMessageInputRef = el; + }} accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} inputID="privateNotes" - label={this.props.translate('privateNotes.composerLabel')} - accessibilityLabel={this.props.translate('privateNotes.title')} + label={translate('privateNotes.composerLabel')} + accessibilityLabel={translate('privateNotes.title')} autoCompleteType="off" autoCorrect={false} autoGrowHeight textAlignVertical="top" containerStyles={[styles.autoGrowHeightMultilineInput]} - defaultValue={this.state.privateNote} - value={this.state.privateNote} - onChangeText={(text) => this.setState({privateNote: text})} + defaultValue={privateNote} + value={privateNote} + onChangeText={(text) => setPrivateNote(text)} /> - : - + + ) : ( + + - } - -
-
- ); - } -} + )} + +
+
+ ); +}; PrivateNotesPage.propTypes = propTypes; PrivateNotesPage.defaultProps = defaultProps; @@ -150,7 +156,7 @@ export default compose( withLocalize, withOnyx({ report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, + key: ({ route }) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, }, session: { key: ONYXKEYS.SESSION, From 2e436e4122ec01e0d3dde6bc5c9b106281e4d3f2 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Sun, 27 Aug 2023 06:04:14 +0530 Subject: [PATCH 09/29] updated param name --- src/libs/actions/Report.js | 2 +- src/pages/PrivateNotes/PrivateNotesPage.js | 62 +++++++++++----------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 4cd84ad0e85b..25ba6a4dfe5a 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1984,7 +1984,7 @@ const updatePrivateNotes = (reportID, accountID, note) => { 'UpdateReportPrivateNote', { reportID, - note, + 'note-html': note, }, {optimisticData, successData, failureData}, ); diff --git a/src/pages/PrivateNotes/PrivateNotesPage.js b/src/pages/PrivateNotes/PrivateNotesPage.js index 46e21d1d2ead..d9724da0bf33 100644 --- a/src/pages/PrivateNotes/PrivateNotesPage.js +++ b/src/pages/PrivateNotes/PrivateNotesPage.js @@ -22,6 +22,7 @@ import personalDetailsPropType from '../personalDetailsPropType'; import RenderHTML from '../../components/RenderHTML'; import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback'; import * as Report from '../../libs/actions/Report'; +import useLocalize from '../../hooks/useLocalize'; const propTypes = { /** All of the personal details for everyone */ @@ -56,7 +57,8 @@ const defaultProps = { personalDetailsList: {}, }; -const PrivateNotesPage = ({route, personalDetailsList, session, report}) => { +const PrivateNotesPage = ({ route, personalDetailsList, session, report }) => { + const { translate } = useLocalize(); const [privateNote, setPrivateNote] = useState( lodashGet(report, `privateNotes.${route.params.accountID}.note`, ''), ); @@ -64,6 +66,7 @@ const PrivateNotesPage = ({route, personalDetailsList, session, report}) => { const isCurrentUser = Number(session.accountID) === Number(route.params.accountID); const savePrivateNote = () => { + debugger; Report.updatePrivateNotes(report.reportID, route.params.accountID, privateNote); Keyboard.dismiss(); }; @@ -77,14 +80,14 @@ const PrivateNotesPage = ({route, personalDetailsList, session, report}) => { return ( Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST)} > { onCloseButtonPress={() => Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack()} /> -
+ {translate( @@ -114,26 +111,31 @@ const PrivateNotesPage = ({route, personalDetailsList, session, report}) => { )} + {editMode ? ( - - { - this.welcomeMessageInputRef = el; - }} - accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} - inputID="privateNotes" - label={translate('privateNotes.composerLabel')} - accessibilityLabel={translate('privateNotes.title')} - autoCompleteType="off" - autoCorrect={false} - autoGrowHeight - textAlignVertical="top" - containerStyles={[styles.autoGrowHeightMultilineInput]} - defaultValue={privateNote} - value={privateNote} - onChangeText={(text) => setPrivateNote(text)} - /> - + + + setPrivateNote(text)} + /> + + ) : ( { )} - +
); From 0e6fd58acf28e01602c5c42d7199efd51998b1fd Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Mon, 28 Aug 2023 23:05:32 +0530 Subject: [PATCH 10/29] updated param name and fixed Your note case --- src/libs/actions/Report.js | 2 +- src/pages/PrivateNotes/PrivateNotesListPage.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 25ba6a4dfe5a..7938e08a3011 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1984,7 +1984,7 @@ const updatePrivateNotes = (reportID, accountID, note) => { 'UpdateReportPrivateNote', { reportID, - 'note-html': note, + 'privateNotes': note, }, {optimisticData, successData, failureData}, ); diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.js b/src/pages/PrivateNotes/PrivateNotesListPage.js index 41f9cb8cc558..b5a9095d5003 100644 --- a/src/pages/PrivateNotes/PrivateNotesListPage.js +++ b/src/pages/PrivateNotes/PrivateNotesListPage.js @@ -107,7 +107,7 @@ function PrivateNotesListPage({ report, personalDetailsList, network, session }) const privateNoteBrickRoadIndicator = (accountID) => !_.isEmpty(lodashGet(report, `privateNotes.${accountID}.errors`, '')) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; return _.chain(lodashGet(report, 'privateNotes')) .map((privateNote, accountID) => ({ - title: Number(lodashGet(session, 'accountID', null)) === accountID ? 'Your note' : lodashGet(personalDetailsList, `${accountID}.login`, ''), + title: Number(lodashGet(session, 'accountID', null)) === Number(accountID) ? 'Your note' : lodashGet(personalDetailsList, `${accountID}.login`, ''), icon: UserUtils.getAvatar(lodashGet(personalDetailsList, `${accountID}.avatar`, UserUtils.getDefaultAvatar(accountID)), accountID), iconType: CONST.ICON_TYPE_AVATAR, action: () => ReportUtils.navigateToPrivateNotesPage(report, accountID), From d5d3bff69c441c82ec924d3ecb3d2da9ef4d08f7 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Tue, 29 Aug 2023 19:27:44 +0530 Subject: [PATCH 11/29] added private notes menu option on all the required pages --- src/CONST.js | 1 + src/pages/PrivateNotes/PrivateNotesListPage.js | 2 +- src/pages/PrivateNotes/PrivateNotesPage.js | 2 +- src/pages/ProfilePage.js | 15 +++++++++++++++ src/pages/ReportDetailsPage.js | 9 +++++++++ 5 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/CONST.js b/src/CONST.js index 4234ed8a5ab7..8bfa60eb9343 100755 --- a/src/CONST.js +++ b/src/CONST.js @@ -1300,6 +1300,7 @@ const CONST = { SETTINGS: 'settings', LEAVE_ROOM: 'leaveRoom', WELCOME_MESSAGE: 'welcomeMessage', + PRIVATE_NOTES: 'privateNotes', }, EDIT_REQUEST_FIELD: { AMOUNT: 'amount', diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.js b/src/pages/PrivateNotes/PrivateNotesListPage.js index b5a9095d5003..b6056537866c 100644 --- a/src/pages/PrivateNotes/PrivateNotesListPage.js +++ b/src/pages/PrivateNotes/PrivateNotesListPage.js @@ -107,7 +107,7 @@ function PrivateNotesListPage({ report, personalDetailsList, network, session }) const privateNoteBrickRoadIndicator = (accountID) => !_.isEmpty(lodashGet(report, `privateNotes.${accountID}.errors`, '')) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; return _.chain(lodashGet(report, 'privateNotes')) .map((privateNote, accountID) => ({ - title: Number(lodashGet(session, 'accountID', null)) === Number(accountID) ? 'Your note' : lodashGet(personalDetailsList, `${accountID}.login`, ''), + title: Number(lodashGet(session, 'accountID', null)) === Number(accountID) ? 'My note' : lodashGet(personalDetailsList, `${accountID}.login`, ''), icon: UserUtils.getAvatar(lodashGet(personalDetailsList, `${accountID}.avatar`, UserUtils.getDefaultAvatar(accountID)), accountID), iconType: CONST.ICON_TYPE_AVATAR, action: () => ReportUtils.navigateToPrivateNotesPage(report, accountID), diff --git a/src/pages/PrivateNotes/PrivateNotesPage.js b/src/pages/PrivateNotes/PrivateNotesPage.js index d9724da0bf33..4240d1423341 100644 --- a/src/pages/PrivateNotes/PrivateNotesPage.js +++ b/src/pages/PrivateNotes/PrivateNotesPage.js @@ -88,7 +88,7 @@ const PrivateNotesPage = ({ route, personalDetailsList, session, report }) => { title={translate('privateNotes.title')} subtitle={ isCurrentUser - ? 'Your note' + ? 'My note' : `${lodashGet(personalDetailsList, `${route.params.accountID}.login`, '')} note` } shouldShowBackButton diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index f45f94704f86..b52001341c94 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -139,6 +139,8 @@ function ProfilePage(props) { const hasStatus = !!statusEmojiCode && Permissions.canUseCustomStatus(props.betas); const statusContent = `${statusEmojiCode} ${statusText}`; + const chatReportIDWithCurrentUser = !isCurrentUser && !Session.isAnonymousUser() ? ReportUtils.getChatByParticipants([accountID, Number(lodashGet(props.session, 'accountID'))]) : 0; + return ( )} + {chatReportIDWithCurrentUser && ( + ReportUtils.navigateToPrivateNotesPage([accountID])} + wrapperStyle={styles.breakAll} + shouldShowRightIcon + /> + )} )} {!hasMinimumDetails && isLoading && } @@ -271,5 +283,8 @@ export default compose( betas: { key: ONYXKEYS.BETAS, }, + session: { + key: ONYXKEYS.SESSION, + } }), )(ProfilePage); diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 361f0198402e..78388ac7eac0 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -111,6 +111,15 @@ function ReportDetailsPage(props) { }, }); } + + // We display private notes option for all the chat reports + items.push({ + key: CONST.REPORT_DETAILS_MENU_ITEM.PRIVATE_NOTES, + translationKey: 'privateNotes.title', + icon: Expensicons.Pencil, + isAnonymousAction: false, + action: () => ReportUtils.navigateToPrivateNotesPage(props.report.reportID), + }); if (isUserCreatedPolicyRoom || canLeaveRoom || isThread) { items.push({ From 673a4fb2cee80fad83fcd864021620ab295e8e87 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Wed, 30 Aug 2023 02:29:43 +0530 Subject: [PATCH 12/29] add offline feedback integration --- src/libs/ReportUtils.js | 5 +-- src/libs/actions/Report.js | 34 +++++++++++++++++++ .../PrivateNotes/PrivateNotesListPage.js | 2 +- src/pages/PrivateNotes/PrivateNotesPage.js | 32 ++++++++++++++--- src/pages/ProfilePage.js | 9 +++-- src/pages/ReportDetailsPage.js | 8 +++-- 6 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index fdad29113e4e..76dbebebeb2e 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3267,7 +3267,7 @@ function getTaskAssigneeChatOnyxData(accountID, assigneeEmail, assigneeAccountID * Redirect the user to either privateNotesLisPage if the report has multiple notes otherwise, it redirects the user to their own private note on the report * * @param {Object} report - * @param {Object} accountID + * @param {Number} accountID */ function navigateToPrivateNotesPage(report, accountID) { if (_.isEmpty(report)) { @@ -3277,8 +3277,9 @@ function navigateToPrivateNotesPage(report, accountID) { const privateNotes = lodashGet(report, 'privateNotes', {}); // Redirect the user to privateNotesList page in case there are multiple notes and accountID is not set - if (_.isEmpty(accountID) && _.keys(privateNotes) > 1) { + if ((_.isEmpty(accountID) && _.keys(privateNotes) > 1) || _.isEmpty(privateNotes)) { Navigation.navigate(ROUTES.getPrivateNotesListRoute(report.reportID)); + return; } // Default the accountID to current user's accountID in case it is empty diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 7938e08a3011..9caa5783c1cb 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1934,6 +1934,13 @@ function flagComment(reportID, reportAction, severity) { API.write('FlagComment', parameters, {optimisticData, successData, failureData}); } +/** + * Updates a given user's private notes on a report + * + * @param {String} reportID + * @param {Number} accountID + * @param {String} note + */ const updatePrivateNotes = (reportID, accountID, note) => { const optimisticData = [ { @@ -1990,6 +1997,11 @@ const updatePrivateNotes = (reportID, accountID, note) => { ); }; +/** + * Fetches all the private notes for a given report + * + * @param {String} reportID + */ function getReportPrivateNote(reportID) { if (_.isEmpty(reportID)) { return; @@ -2031,6 +2043,26 @@ function getReportPrivateNote(reportID) { ); } +/** + * Checks if there are any errors in the private notes for a given report + * + * @param {Object} report + **/ +function hasErrorInPrivateNotes(report) { + const privateNotes = lodashGet(report, 'privateNotes', {}); + return _.some(privateNotes, (privateNote) => !_.isEmpty(privateNote.errors)); +} + +/** + * Clears all errors associated with a given private note + * + * @param {String} reportID + * @param {Number} accountID + **/ +function clearPrivateNotesError(reportID, accountID) { + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {privateNotes: {[accountID]: {errors: null}}}); +} + export { addComment, addAttachment, @@ -2080,4 +2112,6 @@ export { openLastOpenedPublicRoom, updatePrivateNotes, getReportPrivateNote, + clearPrivateNotesError, + hasErrorInPrivateNotes, }; diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.js b/src/pages/PrivateNotes/PrivateNotesListPage.js index b6056537866c..b9045d3245da 100644 --- a/src/pages/PrivateNotes/PrivateNotesListPage.js +++ b/src/pages/PrivateNotes/PrivateNotesListPage.js @@ -119,7 +119,7 @@ function PrivateNotesListPage({ report, personalDetailsList, network, session }) return ( Navigation.goBack()} > { const [privateNote, setPrivateNote] = useState( lodashGet(report, `privateNotes.${route.params.accountID}.note`, ''), ); - const [editMode, setEditMode] = useState(false); + const [editMode, setEditMode] = useState(_.isEmpty(privateNote)); const isCurrentUser = Number(session.accountID) === Number(route.params.accountID); const savePrivateNote = () => { - debugger; - Report.updatePrivateNotes(report.reportID, route.params.accountID, privateNote); + if (_.isEmpty(privateNote)) { + return; + } + const parser = new ExpensiMark(); + const editedNote = parser.replace(privateNote); + Report.updatePrivateNotes(report.reportID, route.params.accountID, editedNote); Keyboard.dismiss(); + + // Enable the view mode once we have saved the updated note + setPrivateNote(editedNote); + setEditMode(false); }; const switchToEditMode = () => { + // Every time we switch to edit mode we want to render the content in the markdown format + const parser = new ExpensiMark(); + setPrivateNote(parser.htmlToMarkdown(privateNote).trim()); + if (isCurrentUser) { setEditMode(true); } @@ -142,7 +156,17 @@ const PrivateNotesPage = ({ route, personalDetailsList, session, report }) => { accessibilityRole={CONST.ACCESSIBILITY_ROLE.Button} onPress={switchToEditMode} > - + + Report.clearPrivateNotesError(report.reportID, route.params.accountID) + } + > + + )} diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index b52001341c94..b01006c96d6d 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -139,7 +139,7 @@ function ProfilePage(props) { const hasStatus = !!statusEmojiCode && Permissions.canUseCustomStatus(props.betas); const statusContent = `${statusEmojiCode} ${statusText}`; - const chatReportIDWithCurrentUser = !isCurrentUser && !Session.isAnonymousUser() ? ReportUtils.getChatByParticipants([accountID, Number(lodashGet(props.session, 'accountID'))]) : 0; + const chatReportWithCurrentUser = !isCurrentUser && !Session.isAnonymousUser() ? ReportUtils.getChatByParticipants([accountID]) : 0; return ( @@ -236,14 +236,17 @@ function ProfilePage(props) { shouldShowRightIcon /> )} - {chatReportIDWithCurrentUser && ( + {!_.isEmpty(chatReportWithCurrentUser) && ( ReportUtils.navigateToPrivateNotesPage([accountID])} + onPress={() => { + ReportUtils.navigateToPrivateNotesPage(chatReportWithCurrentUser, Number(lodashGet(props.session, 'accountID'))); + }} wrapperStyle={styles.breakAll} shouldShowRightIcon + brickRoadIndicator={Report.hasErrorInPrivateNotes(chatReportWithCurrentUser) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} /> )} diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 78388ac7eac0..21bda5393585 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -118,7 +118,10 @@ function ReportDetailsPage(props) { translationKey: 'privateNotes.title', icon: Expensicons.Pencil, isAnonymousAction: false, - action: () => ReportUtils.navigateToPrivateNotesPage(props.report.reportID), + action: () => { + ReportUtils.navigateToPrivateNotesPage(props.report); + }, + brickRoadIndicator: Report.hasErrorInPrivateNotes(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '', }); if (isUserCreatedPolicyRoom || canLeaveRoom || isThread) { @@ -185,6 +188,7 @@ function ReportDetailsPage(props) { {_.map(menuItems, (item) => { + console.log(item) const brickRoadIndicator = ReportUtils.hasReportNameError(props.report) && item.key === CONST.REPORT_DETAILS_MENU_ITEM.SETTINGS ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; return ( @@ -196,7 +200,7 @@ function ReportDetailsPage(props) { onPress={item.action} isAnonymousAction={item.isAnonymousAction} shouldShowRightIcon - brickRoadIndicator={brickRoadIndicator} + brickRoadIndicator={brickRoadIndicator || item.brickRoadIndicator} /> ); })} From c5dbac8c034294ed869301e794a4b58edfa95b11 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Wed, 30 Aug 2023 02:39:43 +0530 Subject: [PATCH 13/29] linter error --- src/libs/actions/Report.js | 5 +++-- src/pages/PrivateNotes/PrivateNotesPage.js | 11 +++++------ src/pages/ReportDetailsPage.js | 3 +-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 9caa5783c1cb..4a286256d8a1 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -2047,7 +2047,8 @@ function getReportPrivateNote(reportID) { * Checks if there are any errors in the private notes for a given report * * @param {Object} report - **/ + * @returns {Boolean} Returns true if there are errors in any of the private notes on the report + */ function hasErrorInPrivateNotes(report) { const privateNotes = lodashGet(report, 'privateNotes', {}); return _.some(privateNotes, (privateNote) => !_.isEmpty(privateNote.errors)); @@ -2058,7 +2059,7 @@ function hasErrorInPrivateNotes(report) { * * @param {String} reportID * @param {Number} accountID - **/ + */ function clearPrivateNotesError(reportID, accountID) { Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {privateNotes: {[accountID]: {errors: null}}}); } diff --git a/src/pages/PrivateNotes/PrivateNotesPage.js b/src/pages/PrivateNotes/PrivateNotesPage.js index 81abcfb8c951..71c458e78269 100644 --- a/src/pages/PrivateNotes/PrivateNotesPage.js +++ b/src/pages/PrivateNotes/PrivateNotesPage.js @@ -1,9 +1,11 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { View, Keyboard } from 'react-native'; import { withOnyx } from 'react-native-onyx'; import lodashGet from 'lodash/get'; import Str from 'expensify-common/lib/str'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; +import _ from 'underscore'; import withLocalize from '../../components/withLocalize'; import ScreenWrapper from '../../components/ScreenWrapper'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; @@ -23,7 +25,6 @@ import RenderHTML from '../../components/RenderHTML'; import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback'; import * as Report from '../../libs/actions/Report'; import useLocalize from '../../hooks/useLocalize'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; const propTypes = { @@ -46,9 +47,6 @@ const propTypes = { /** Currently logged in user accountID */ accountID: PropTypes.number, }), - - /** Returns translated string for given locale and phrase */ - translate: PropTypes.func.isRequired, }; const defaultProps = { @@ -59,7 +57,7 @@ const defaultProps = { personalDetailsList: {}, }; -const PrivateNotesPage = ({ route, personalDetailsList, session, report }) => { +function PrivateNotesPage({ route, personalDetailsList, session, report }) { const { translate } = useLocalize(); const [privateNote, setPrivateNote] = useState( lodashGet(report, `privateNotes.${route.params.accountID}.note`, ''), @@ -175,6 +173,7 @@ const PrivateNotesPage = ({ route, personalDetailsList, session, report }) => { ); }; +PrivateNotesPage.displayName = 'PrivateNotesPage'; PrivateNotesPage.propTypes = propTypes; PrivateNotesPage.defaultProps = defaultProps; diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 21bda5393585..1d25bcf61e7a 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -135,7 +135,7 @@ function ReportDetailsPage(props) { } return items; - }, [props.report.reportID, participants, isArchivedRoom, shouldDisableSettings, isThread, isUserCreatedPolicyRoom, canLeaveRoom]); + }, [props.report, participants, isArchivedRoom, shouldDisableSettings, isThread, isUserCreatedPolicyRoom, canLeaveRoom]); const displayNamesWithTooltips = useMemo(() => { const hasMultipleParticipants = participants.length > 1; @@ -188,7 +188,6 @@ function ReportDetailsPage(props) { {_.map(menuItems, (item) => { - console.log(item) const brickRoadIndicator = ReportUtils.hasReportNameError(props.report) && item.key === CONST.REPORT_DETAILS_MENU_ITEM.SETTINGS ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; return ( From f8793660b7e78dbc349d4a9918d367b3e6e8f040 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Wed, 30 Aug 2023 10:11:18 +0530 Subject: [PATCH 14/29] fix style --- src/libs/Navigation/linkingConfig.js | 2 +- src/libs/ReportUtils.js | 4 +- src/libs/actions/Report.js | 24 +++++----- .../PrivateNotes/PrivateNotesListPage.js | 22 +++++---- src/pages/PrivateNotes/PrivateNotesPage.js | 48 +++++++------------ src/pages/ProfilePage.js | 4 +- src/pages/ReportDetailsPage.js | 4 +- 7 files changed, 49 insertions(+), 59 deletions(-) diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 493ea0678ace..5adb30d99ca7 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -223,7 +223,7 @@ export default { PrivateNotes_Root: ROUTES.PRIVATE_NOTES, PrivateNotes_List: ROUTES.PRIVATE_NOTES_LIST, }, - }, + }, Report_Details: { screens: { Report_Details_Root: ROUTES.REPORT_WITH_ID_DETAILS, diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 7559e4196ea8..cde5873ebcf5 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3432,12 +3432,12 @@ function navigateToPrivateNotesPage(report, accountID) { const privateNotes = lodashGet(report, 'privateNotes', {}); - // Redirect the user to privateNotesList page in case there are multiple notes and accountID is not set + // Redirect the user to privateNotesList page in case there are multiple notes and accountID is not set if ((_.isEmpty(accountID) && _.keys(privateNotes) > 1) || _.isEmpty(privateNotes)) { Navigation.navigate(ROUTES.getPrivateNotesListRoute(report.reportID)); return; } - + // Default the accountID to current user's accountID in case it is empty Navigation.navigate(ROUTES.getPrivateNotesRoute(report.reportID, _.isEmpty(accountID) ? currentUserAccountID : accountID)); } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index b5a6656fa62a..6eff7261a2f1 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1949,11 +1949,11 @@ const updatePrivateNotes = (reportID, accountID, note) => { value: { privateNotes: { [accountID]: { - pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, - errors: null, - note + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + errors: null, + note, }, - } + }, }, }, ]; @@ -1968,7 +1968,7 @@ const updatePrivateNotes = (reportID, accountID, note) => { pendingAction: null, errors: null, }, - } + }, }, }, ]; @@ -1980,9 +1980,9 @@ const updatePrivateNotes = (reportID, accountID, note) => { value: { privateNotes: { [accountID]: { - errors: ErrorUtils.getMicroSecondOnyxError('Private notes couldn\'t be saved'), + errors: ErrorUtils.getMicroSecondOnyxError("Private notes couldn't be saved"), }, - } + }, }, }, ]; @@ -1991,7 +1991,7 @@ const updatePrivateNotes = (reportID, accountID, note) => { 'UpdateReportPrivateNote', { reportID, - 'privateNotes': note, + privateNotes: note, }, {optimisticData, successData, failureData}, ); @@ -2045,23 +2045,23 @@ function getReportPrivateNote(reportID) { /** * Checks if there are any errors in the private notes for a given report - * + * * @param {Object} report * @returns {Boolean} Returns true if there are errors in any of the private notes on the report */ function hasErrorInPrivateNotes(report) { - const privateNotes = lodashGet(report, 'privateNotes', {}); + const privateNotes = lodashGet(report, 'privateNotes', {}); return _.some(privateNotes, (privateNote) => !_.isEmpty(privateNote.errors)); } /** * Clears all errors associated with a given private note - * + * * @param {String} reportID * @param {Number} accountID */ function clearPrivateNotesError(reportID, accountID) { - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {privateNotes: {[accountID]: {errors: null}}}); + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {privateNotes: {[accountID]: {errors: null}}}); } export { diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.js b/src/pages/PrivateNotes/PrivateNotesListPage.js index b9045d3245da..92b6b5f12a65 100644 --- a/src/pages/PrivateNotes/PrivateNotesListPage.js +++ b/src/pages/PrivateNotes/PrivateNotesListPage.js @@ -1,5 +1,5 @@ -import React, { useMemo, useEffect } from 'react'; -import { withOnyx } from 'react-native-onyx'; +import React, {useMemo, useEffect} from 'react'; +import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; import _ from 'underscore'; import lodashGet from 'lodash/get'; @@ -18,10 +18,10 @@ import personalDetailsPropType from '../personalDetailsPropType'; import * as UserUtils from '../../libs/UserUtils'; import reportPropTypes from '../reportPropTypes'; import ScreenWrapper from '../../components/ScreenWrapper'; -import withLocalize, { withLocalizePropTypes } from '../../components/withLocalize'; +import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '../../components/HeaderWithBackButton'; -import { withNetwork } from '../../components/OnyxProvider'; +import {withNetwork} from '../../components/OnyxProvider'; import networkPropTypes from '../../components/networkPropTypes'; const propTypes = { @@ -58,8 +58,8 @@ const defaultProps = { personalDetailsList: {}, }; -function PrivateNotesListPage({ report, personalDetailsList, network, session }) { - const { translate } = useLocalize(); +function PrivateNotesListPage({report, personalDetailsList, network, session}) { + const {translate} = useLocalize(); useEffect(() => { if (network.isOffline) { @@ -104,7 +104,7 @@ function PrivateNotesListPage({ report, personalDetailsList, network, session }) * @returns {Array} the menu item list */ const privateNotes = useMemo(() => { - const privateNoteBrickRoadIndicator = (accountID) => !_.isEmpty(lodashGet(report, `privateNotes.${accountID}.errors`, '')) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; + const privateNoteBrickRoadIndicator = (accountID) => (!_.isEmpty(lodashGet(report, `privateNotes.${accountID}.errors`, '')) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''); return _.chain(lodashGet(report, 'privateNotes')) .map((privateNote, accountID) => ({ title: Number(lodashGet(session, 'accountID', null)) === Number(accountID) ? 'My note' : lodashGet(personalDetailsList, `${accountID}.login`, ''), @@ -128,7 +128,11 @@ function PrivateNotesListPage({ report, personalDetailsList, network, session }) onCloseButtonPress={() => Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack()} /> - {report.isLoadingPrivateNotes && _.isEmpty(lodashGet(report, 'privateNotes', [])) ? : _.map(privateNotes, (item, index) => getMenuItem(item, index))} + {report.isLoadingPrivateNotes && _.isEmpty(lodashGet(report, 'privateNotes', [])) ? ( + + ) : ( + _.map(privateNotes, (item, index) => getMenuItem(item, index)) + )} ); @@ -141,7 +145,7 @@ export default compose( withLocalize, withOnyx({ report: { - key: ({ route }) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, }, session: { key: ONYXKEYS.SESSION, diff --git a/src/pages/PrivateNotes/PrivateNotesPage.js b/src/pages/PrivateNotes/PrivateNotesPage.js index 71c458e78269..0942879a23ba 100644 --- a/src/pages/PrivateNotes/PrivateNotesPage.js +++ b/src/pages/PrivateNotes/PrivateNotesPage.js @@ -1,7 +1,7 @@ -import React, { useState } from 'react'; +import React, {useState} from 'react'; import PropTypes from 'prop-types'; -import { View, Keyboard } from 'react-native'; -import { withOnyx } from 'react-native-onyx'; +import {View, Keyboard} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import Str from 'expensify-common/lib/str'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; @@ -57,11 +57,9 @@ const defaultProps = { personalDetailsList: {}, }; -function PrivateNotesPage({ route, personalDetailsList, session, report }) { - const { translate } = useLocalize(); - const [privateNote, setPrivateNote] = useState( - lodashGet(report, `privateNotes.${route.params.accountID}.note`, ''), - ); +function PrivateNotesPage({route, personalDetailsList, session, report}) { + const {translate} = useLocalize(); + const [privateNote, setPrivateNote] = useState(lodashGet(report, `privateNotes.${route.params.accountID}.note`, '')); const [editMode, setEditMode] = useState(_.isEmpty(privateNote)); const isCurrentUser = Number(session.accountID) === Number(route.params.accountID); @@ -73,7 +71,7 @@ function PrivateNotesPage({ route, personalDetailsList, session, report }) { const editedNote = parser.replace(privateNote); Report.updatePrivateNotes(report.reportID, route.params.accountID, editedNote); Keyboard.dismiss(); - + // Enable the view mode once we have saved the updated note setPrivateNote(editedNote); setEditMode(false); @@ -83,7 +81,7 @@ function PrivateNotesPage({ route, personalDetailsList, session, report }) { // Every time we switch to edit mode we want to render the content in the markdown format const parser = new ExpensiMark(); setPrivateNote(parser.htmlToMarkdown(privateNote).trim()); - + if (isCurrentUser) { setEditMode(true); } @@ -98,11 +96,7 @@ function PrivateNotesPage({ route, personalDetailsList, session, report }) { > Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack()} @@ -111,13 +105,7 @@ function PrivateNotesPage({ route, personalDetailsList, session, report }) { {translate( - Str.extractEmailDomain( - lodashGet( - personalDetailsList, - [route.params.accountID, 'login'], - '', - ), - ) === CONST.EMAIL.GUIDES_DOMAIN + Str.extractEmailDomain(lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN ? 'privateNotes.sharedNoteMessage' : 'privateNotes.personalNoteMessage', )} @@ -155,13 +143,11 @@ function PrivateNotesPage({ route, personalDetailsList, session, report }) { onPress={switchToEditMode} > - Report.clearPrivateNotesError(report.reportID, route.params.accountID) - } + errors={{ + ...lodashGet(report, `privateNotes.${route.params.accountID}.errors`, ''), + }} + pendingAction={lodashGet(report, `privateNotes.${route.params.accountID}.pendingAction`, '')} + onClose={() => Report.clearPrivateNotesError(report.reportID, route.params.accountID)} > @@ -171,7 +157,7 @@ function PrivateNotesPage({ route, personalDetailsList, session, report }) { ); -}; +} PrivateNotesPage.displayName = 'PrivateNotesPage'; PrivateNotesPage.propTypes = propTypes; @@ -181,7 +167,7 @@ export default compose( withLocalize, withOnyx({ report: { - key: ({ route }) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, }, session: { key: ONYXKEYS.SESSION, diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 084ef2822aa6..76af3b0add1f 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -137,7 +137,7 @@ function ProfilePage(props) { const statusText = lodashGet(details, 'status.text', ''); const hasStatus = !!statusEmojiCode && Permissions.canUseCustomStatus(props.betas); const statusContent = `${statusEmojiCode} ${statusText}`; - + const navigateBackTo = lodashGet(props.route, 'params.backTo', ''); const chatReportWithCurrentUser = !isCurrentUser && !Session.isAnonymousUser() ? ReportUtils.getChatByParticipants([accountID]) : 0; @@ -289,6 +289,6 @@ export default compose( }, session: { key: ONYXKEYS.SESSION, - } + }, }), )(ProfilePage); diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 1d25bcf61e7a..4cc251cd7923 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -111,7 +111,7 @@ function ReportDetailsPage(props) { }, }); } - + // We display private notes option for all the chat reports items.push({ key: CONST.REPORT_DETAILS_MENU_ITEM.PRIVATE_NOTES, @@ -119,7 +119,7 @@ function ReportDetailsPage(props) { icon: Expensicons.Pencil, isAnonymousAction: false, action: () => { - ReportUtils.navigateToPrivateNotesPage(props.report); + ReportUtils.navigateToPrivateNotesPage(props.report); }, brickRoadIndicator: Report.hasErrorInPrivateNotes(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '', }); From 4bd31435154c2b20ef0ae1808ab2d5e13335b427 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Wed, 30 Aug 2023 11:51:42 +0530 Subject: [PATCH 15/29] fix translation --- src/languages/es.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/languages/es.js b/src/languages/es.js index 5c7d6c7cc5ed..d3fa6132d135 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -646,9 +646,11 @@ export default { allSet: 'Todo está listo. Guarda tu contraseña en un lugar seguro.', }, privateNotes: { - title: 'Private notes', - personalNoteMessage: 'Keep notes about this chat here. You are the only person who can add, edit or view these notes.', - sharedNoteMessage: 'Keep notes about this chat here. Expensify employee and other users on team.expensify.com domain can view these notes.', + title: 'Notas privadas', + personalNoteMessage: 'Guarda notas sobre este chat aquí. Usted es la única persona que puede añadir, editar o ver estas notas.', + sharedNoteMessage: 'Guarda notas sobre este chat aquí. Los empleados de Expensify y otros usuarios del dominio team.expensify.com pueden ver estas notas.', + notesUnavailable: 'No se han encontrado notas para el usuario', + composerLabel: 'Notas', }, addPayPalMePage: { enterYourUsernameToGetPaidViaPayPal: 'Recibe pagos vía PayPal.', From 4c40d89c2d75171ba8f57710374f35bf14c7cf0b Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Wed, 30 Aug 2023 13:46:43 +0530 Subject: [PATCH 16/29] Fix lodashGet usasge --- src/pages/PrivateNotes/PrivateNotesListPage.js | 12 ++++++------ src/pages/PrivateNotes/PrivateNotesPage.js | 11 +++++------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.js b/src/pages/PrivateNotes/PrivateNotesListPage.js index 92b6b5f12a65..754d1aebfb19 100644 --- a/src/pages/PrivateNotes/PrivateNotesListPage.js +++ b/src/pages/PrivateNotes/PrivateNotesListPage.js @@ -104,11 +104,11 @@ function PrivateNotesListPage({report, personalDetailsList, network, session}) { * @returns {Array} the menu item list */ const privateNotes = useMemo(() => { - const privateNoteBrickRoadIndicator = (accountID) => (!_.isEmpty(lodashGet(report, `privateNotes.${accountID}.errors`, '')) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''); - return _.chain(lodashGet(report, 'privateNotes')) + const privateNoteBrickRoadIndicator = (accountID) => (!_.isEmpty(lodashGet(report, ['privateNotes', accountID, 'errors'], '')) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''); + return _.chain(lodashGet(report, 'privateNotes', {})) .map((privateNote, accountID) => ({ - title: Number(lodashGet(session, 'accountID', null)) === Number(accountID) ? 'My note' : lodashGet(personalDetailsList, `${accountID}.login`, ''), - icon: UserUtils.getAvatar(lodashGet(personalDetailsList, `${accountID}.avatar`, UserUtils.getDefaultAvatar(accountID)), accountID), + title: Number(lodashGet(session, 'accountID', null)) === Number(accountID) ? 'My note' : lodashGet(personalDetailsList, [accountID, 'login'], ''), + icon: UserUtils.getAvatar(lodashGet(personalDetailsList, [accountID, 'avatar'], UserUtils.getDefaultAvatar(accountID)), accountID), iconType: CONST.ICON_TYPE_AVATAR, action: () => ReportUtils.navigateToPrivateNotesPage(report, accountID), brickRoadIndicator: privateNoteBrickRoadIndicator(accountID), @@ -119,7 +119,7 @@ function PrivateNotesListPage({report, personalDetailsList, network, session}) { return ( Navigation.goBack()} > Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack()} /> - {report.isLoadingPrivateNotes && _.isEmpty(lodashGet(report, 'privateNotes', [])) ? ( + {report.isLoadingPrivateNotes && _.isEmpty(lodashGet(report, 'privateNotes', {})) ? ( ) : ( _.map(privateNotes, (item, index) => getMenuItem(item, index)) diff --git a/src/pages/PrivateNotes/PrivateNotesPage.js b/src/pages/PrivateNotes/PrivateNotesPage.js index 0942879a23ba..79c70ca224c4 100644 --- a/src/pages/PrivateNotes/PrivateNotesPage.js +++ b/src/pages/PrivateNotes/PrivateNotesPage.js @@ -59,7 +59,7 @@ const defaultProps = { function PrivateNotesPage({route, personalDetailsList, session, report}) { const {translate} = useLocalize(); - const [privateNote, setPrivateNote] = useState(lodashGet(report, `privateNotes.${route.params.accountID}.note`, '')); + const [privateNote, setPrivateNote] = useState(lodashGet(report, ['privateNotes', route.params.accountID, 'note'], '')); const [editMode, setEditMode] = useState(_.isEmpty(privateNote)); const isCurrentUser = Number(session.accountID) === Number(route.params.accountID); @@ -86,17 +86,16 @@ function PrivateNotesPage({route, personalDetailsList, session, report}) { setEditMode(true); } }; - return ( Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST)} > Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack()} @@ -144,9 +143,9 @@ function PrivateNotesPage({route, personalDetailsList, session, report}) { > Report.clearPrivateNotesError(report.reportID, route.params.accountID)} > From 6cf1da842f97d8edae4942b082dfd02f1c205353 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Wed, 30 Aug 2023 15:21:23 +0530 Subject: [PATCH 17/29] use length and donot allow to edit if not current user note --- src/libs/ReportUtils.js | 2 +- src/pages/PrivateNotes/PrivateNotesPage.js | 12 ++++++------ src/pages/ReportDetailsPage.js | 4 +--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 04d80e6d9497..4ef31bd93922 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3434,7 +3434,7 @@ function navigateToPrivateNotesPage(report, accountID) { const privateNotes = lodashGet(report, 'privateNotes', {}); // Redirect the user to privateNotesList page in case there are multiple notes and accountID is not set - if ((_.isEmpty(accountID) && _.keys(privateNotes) > 1) || _.isEmpty(privateNotes)) { + if ((_.isEmpty(accountID) && _.keys(privateNotes).length > 1) || _.isEmpty(privateNotes)) { Navigation.navigate(ROUTES.getPrivateNotesListRoute(report.reportID)); return; } diff --git a/src/pages/PrivateNotes/PrivateNotesPage.js b/src/pages/PrivateNotes/PrivateNotesPage.js index 79c70ca224c4..614f4427cd73 100644 --- a/src/pages/PrivateNotes/PrivateNotesPage.js +++ b/src/pages/PrivateNotes/PrivateNotesPage.js @@ -61,7 +61,7 @@ function PrivateNotesPage({route, personalDetailsList, session, report}) { const {translate} = useLocalize(); const [privateNote, setPrivateNote] = useState(lodashGet(report, ['privateNotes', route.params.accountID, 'note'], '')); const [editMode, setEditMode] = useState(_.isEmpty(privateNote)); - const isCurrentUser = Number(session.accountID) === Number(route.params.accountID); + const isCurrentUserNote = Number(session.accountID) === Number(route.params.accountID); const savePrivateNote = () => { if (_.isEmpty(privateNote)) { @@ -78,13 +78,13 @@ function PrivateNotesPage({route, personalDetailsList, session, report}) { }; const switchToEditMode = () => { + if (!isCurrentUserNote) { + return; + } // Every time we switch to edit mode we want to render the content in the markdown format const parser = new ExpensiMark(); setPrivateNote(parser.htmlToMarkdown(privateNote).trim()); - - if (isCurrentUser) { - setEditMode(true); - } + setEditMode(true); }; return ( @@ -95,7 +95,7 @@ function PrivateNotesPage({route, personalDetailsList, session, report}) { > Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack()} diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 4cc251cd7923..b15c5e55c940 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -118,9 +118,7 @@ function ReportDetailsPage(props) { translationKey: 'privateNotes.title', icon: Expensicons.Pencil, isAnonymousAction: false, - action: () => { - ReportUtils.navigateToPrivateNotesPage(props.report); - }, + action: () => ReportUtils.navigateToPrivateNotesPage(props.report), brickRoadIndicator: Report.hasErrorInPrivateNotes(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '', }); From 7b23352fd2cce150299e444cf4c3c9dcafeb39bd Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Tue, 12 Sep 2023 11:58:26 +0530 Subject: [PATCH 18/29] added new page --- src/ROUTES.js | 7 +- src/components/MenuItem.js | 1 + .../AppNavigator/ModalStackNavigators.js | 4 +- src/libs/Navigation/linkingConfig.js | 3 +- src/libs/ReportUtils.js | 2 +- .../PrivateNotes/PrivateNotesEditPage.js | 149 ++++++++++++++++++ ...teNotesPage.js => PrivateNotesViewPage.js} | 12 +- 7 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 src/pages/PrivateNotes/PrivateNotesEditPage.js rename src/pages/PrivateNotes/{PrivateNotesPage.js => PrivateNotesViewPage.js} (95%) diff --git a/src/ROUTES.js b/src/ROUTES.js index 091957b908d7..d0a28e50917a 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -165,11 +165,12 @@ export default { DESKTOP_SIGN_IN_REDIRECT: 'desktop-signin-redirect', // Routes related to private notes added to the report - PRIVATE_NOTES: 'r/:reportID/notes/:accountID', - getPrivateNotesRoute: (reportID, accountID) => `r/${reportID}/notes/${accountID}`, - + PRIVATE_NOTES_VIEW: 'r/:reportID/notes/:accountID', + getPrivateNotesViewRoute: (reportID, accountID) => `r/${reportID}/notes/${accountID}`, PRIVATE_NOTES_LIST: 'r/:reportID/notes', getPrivateNotesListRoute: (reportID) => `r/${reportID}/notes`, + PRIVATE_NOTES_EDIT: 'r/:reportID/notes/:accountID/edit', + getPrivateNotesEditRoute: (reportID, accountID) => `r/${reportID}/notes/${accountID}/edit`, // This is a special validation URL that will take the user to /workspace/new after validation. This is used // when linking users from e.com in order to share a session in this app. diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js index adf3fa0cdd80..4031c05e7edb 100644 --- a/src/components/MenuItem.js +++ b/src/components/MenuItem.js @@ -74,6 +74,7 @@ const defaultProps = { title: '', numberOfLinesTitle: 1, shouldGreyOutWhenDisabled: true, + shouldRenderAsHTML: false, }; const MenuItem = React.forwardRef((props, ref) => { diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index e208c27f2212..fb8449810a43 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -766,10 +766,10 @@ const EditRequestStackNavigator = createModalStackNavigator([ const PrivateNotesModalStackNavigator = createModalStackNavigator([ { getComponent: () => { - const PrivateNotesPage = require('../../../pages/PrivateNotes/PrivateNotesPage').default; + const PrivateNotesPage = require('../../../pages/PrivateNotes/PrivateNotesViewPage').default; return PrivateNotesPage; }, - name: 'PrivateNotes_Root', + name: 'PrivateNotes_View', }, { getComponent: () => { diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index ce77bd2333a8..dd9f721d1d12 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -218,8 +218,9 @@ export default { }, Private_Notes: { screens: { - PrivateNotes_Root: ROUTES.PRIVATE_NOTES, + PrivateNotes_View: ROUTES.PRIVATE_NOTES_VIEW, PrivateNotes_List: ROUTES.PRIVATE_NOTES_LIST, + PrivateNotes_Edit: ROUTES.PRIVATE_NOTES_EDIT, }, }, Report_Details: { diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 2b23e31f4e6f..5cb00b3597a0 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3481,7 +3481,7 @@ function navigateToPrivateNotesPage(report, accountID) { } // Default the accountID to current user's accountID in case it is empty - Navigation.navigate(ROUTES.getPrivateNotesRoute(report.reportID, _.isEmpty(accountID) ? currentUserAccountID : accountID)); + Navigation.navigate(ROUTES.getPrivateNotesViewRoute(report.reportID, _.isEmpty(accountID) ? currentUserAccountID : accountID)); } /** diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js new file mode 100644 index 000000000000..64cd46987143 --- /dev/null +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -0,0 +1,149 @@ +import React, {useState} from 'react'; +import PropTypes from 'prop-types'; +import {View, Keyboard} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import lodashGet from 'lodash/get'; +import Str from 'expensify-common/lib/str'; +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; +import _ from 'underscore'; +import withLocalize from '../../components/withLocalize'; +import ScreenWrapper from '../../components/ScreenWrapper'; +import HeaderWithBackButton from '../../components/HeaderWithBackButton'; +import Navigation from '../../libs/Navigation/Navigation'; +import styles from '../../styles/styles'; +import compose from '../../libs/compose'; +import ONYXKEYS from '../../ONYXKEYS'; +import TextInput from '../../components/TextInput'; +import CONST from '../../CONST'; +import Text from '../../components/Text'; +import ROUTES from '../../ROUTES'; +import Form from '../../components/Form'; +import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; +import reportPropTypes from '../reportPropTypes'; +import personalDetailsPropType from '../personalDetailsPropType'; +import RenderHTML from '../../components/RenderHTML'; +import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback'; +import * as Report from '../../libs/actions/Report'; +import useLocalize from '../../hooks/useLocalize'; +import OfflineWithFeedback from '../../components/OfflineWithFeedback'; + +const propTypes = { + /** 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, + + /** Session of currently logged in user */ + session: PropTypes.shape({ + /** Currently logged in user accountID */ + accountID: PropTypes.number, + }), +}; + +const defaultProps = { + report: {}, + session: { + accountID: null, + }, + personalDetailsList: {}, +}; + +function PrivateNotesEditPage({route, personalDetailsList, session, report}) { + const {translate} = useLocalize(); + const [privateNote, setPrivateNote] = useState(lodashGet(report, ['privateNotes', route.params.accountID, 'note'], '')); + const isCurrentUserNote = Number(session.accountID) === Number(route.params.accountID); + + const savePrivateNote = () => { + if (_.isEmpty(privateNote)) { + return; + } + const parser = new ExpensiMark(); + const editedNote = parser.replace(privateNote); + Report.updatePrivateNotes(report.reportID, route.params.accountID, editedNote); + Keyboard.dismiss(); + + // Enable the view mode once we have saved the updated note + setPrivateNote(editedNote); + setEditMode(false); + }; + + return ( + + Navigation.goBack(ROUTES.PRIVATE_NOTES_VIEW)} + > + Navigation.dismissModal()} + onBackButtonPress={() => Navigation.goBack(ROUTES.PRIVATE_NOTES_VIEW)} + /> + + + + {translate( + Str.extractEmailDomain(lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN + ? 'privateNotes.sharedNoteMessage' + : 'privateNotes.personalNoteMessage', + )} + + +
+ + setPrivateNote(text)} + /> + +
+
+
+
+ ); +} + +PrivateNotesEditPage.displayName = 'PrivateNotesEditPage'; +PrivateNotesEditPage.propTypes = propTypes; +PrivateNotesEditPage.defaultProps = defaultProps; + +export default compose( + withLocalize, + withOnyx({ + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.reportID.toString()}`, + }, + session: { + key: ONYXKEYS.SESSION, + }, + personalDetailsList: { + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + }, + }), +)(PrivateNotesEditPage); diff --git a/src/pages/PrivateNotes/PrivateNotesPage.js b/src/pages/PrivateNotes/PrivateNotesViewPage.js similarity index 95% rename from src/pages/PrivateNotes/PrivateNotesPage.js rename to src/pages/PrivateNotes/PrivateNotesViewPage.js index 614f4427cd73..67012319ea78 100644 --- a/src/pages/PrivateNotes/PrivateNotesPage.js +++ b/src/pages/PrivateNotes/PrivateNotesViewPage.js @@ -57,7 +57,7 @@ const defaultProps = { personalDetailsList: {}, }; -function PrivateNotesPage({route, personalDetailsList, session, report}) { +function PrivateNotesViewPage({route, personalDetailsList, session, report}) { const {translate} = useLocalize(); const [privateNote, setPrivateNote] = useState(lodashGet(report, ['privateNotes', route.params.accountID, 'note'], '')); const [editMode, setEditMode] = useState(_.isEmpty(privateNote)); @@ -98,7 +98,7 @@ function PrivateNotesPage({route, personalDetailsList, session, report}) { subtitle={isCurrentUserNote ? 'My note' : `${lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')} note`} shouldShowBackButton onCloseButtonPress={() => Navigation.dismissModal()} - onBackButtonPress={() => Navigation.goBack()} + onBackButtonPress={() => Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST)} /> @@ -158,9 +158,9 @@ function PrivateNotesPage({route, personalDetailsList, session, report}) { ); } -PrivateNotesPage.displayName = 'PrivateNotesPage'; -PrivateNotesPage.propTypes = propTypes; -PrivateNotesPage.defaultProps = defaultProps; +PrivateNotesViewPage.displayName = 'PrivateNotesViewPage'; +PrivateNotesViewPage.propTypes = propTypes; +PrivateNotesViewPage.defaultProps = defaultProps; export default compose( withLocalize, @@ -175,4 +175,4 @@ export default compose( key: ONYXKEYS.PERSONAL_DETAILS_LIST, }, }), -)(PrivateNotesPage); +)(PrivateNotesViewPage); From 87b9626311551bcae8806fda761803dd260ef31b Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Tue, 12 Sep 2023 16:15:26 +0530 Subject: [PATCH 19/29] added logic for edit and render content separately --- ios/Podfile.lock | 2 +- src/components/menuItemPropTypes.js | 3 + .../AppNavigator/ModalStackNavigators.js | 7 ++ .../PrivateNotes/PrivateNotesEditPage.js | 26 ++--- .../PrivateNotes/PrivateNotesViewPage.js | 107 +++--------------- 5 files changed, 41 insertions(+), 104 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2bea672171fe..0929b541af07 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1311,4 +1311,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 845537d35601574adcd0794e17003ba7dbccdbfd -COCOAPODS: 1.12.1 +COCOAPODS: 1.11.3 diff --git a/src/components/menuItemPropTypes.js b/src/components/menuItemPropTypes.js index cc89f9a7b80b..53216ab7cdc7 100644 --- a/src/components/menuItemPropTypes.js +++ b/src/components/menuItemPropTypes.js @@ -144,6 +144,9 @@ const propTypes = { /** Should we grey out the menu item when it is disabled? */ shouldGreyOutWhenDisabled: PropTypes.bool, + + /** Should render the content in HTML format */ + shouldRenderAsHTML: PropTypes.bool, }; export default propTypes; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index fb8449810a43..d30362a20085 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -778,6 +778,13 @@ const PrivateNotesModalStackNavigator = createModalStackNavigator([ }, name: 'PrivateNotes_List', }, + { + getComponent: () => { + const PrivateNotesEditPage = require('../../../pages/PrivateNotes/PrivateNotesEditPage').default; + return PrivateNotesEditPage; + }, + name: 'PrivateNotes_Edit', + }, ]); const SignInModalStackNavigator = createModalStackNavigator([ diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 64cd46987143..149090f82f57 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -21,8 +21,6 @@ import Form from '../../components/Form'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import reportPropTypes from '../reportPropTypes'; import personalDetailsPropType from '../personalDetailsPropType'; -import RenderHTML from '../../components/RenderHTML'; -import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback'; import * as Report from '../../libs/actions/Report'; import useLocalize from '../../hooks/useLocalize'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; @@ -63,32 +61,25 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { const isCurrentUserNote = Number(session.accountID) === Number(route.params.accountID); const savePrivateNote = () => { - if (_.isEmpty(privateNote)) { - return; - } const parser = new ExpensiMark(); const editedNote = parser.replace(privateNote); Report.updatePrivateNotes(report.reportID, route.params.accountID, editedNote); Keyboard.dismiss(); - - // Enable the view mode once we have saved the updated note - setPrivateNote(editedNote); - setEditMode(false); }; return ( Navigation.goBack(ROUTES.PRIVATE_NOTES_VIEW)} + onBackButtonPress={() => Navigation.goBack(ROUTES.getPrivateNotesViewRoute(report.reportID, route.params.accountID))} > Navigation.dismissModal()} - onBackButtonPress={() => Navigation.goBack(ROUTES.PRIVATE_NOTES_VIEW)} + onBackButtonPress={() => Navigation.goBack(ROUTES.getPrivateNotesViewRoute(report.reportID, route.params.accountID))} /> @@ -106,7 +97,14 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { submitButtonText={translate('common.save')} enabledWhenOffline > - + Report.clearPrivateNotesError(report.reportID, route.params.accountID)} + style={[styles.mb3]} + > setPrivateNote(text)} /> - +
diff --git a/src/pages/PrivateNotes/PrivateNotesViewPage.js b/src/pages/PrivateNotes/PrivateNotesViewPage.js index 67012319ea78..156cd8073972 100644 --- a/src/pages/PrivateNotes/PrivateNotesViewPage.js +++ b/src/pages/PrivateNotes/PrivateNotesViewPage.js @@ -1,10 +1,8 @@ -import React, {useState} from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; -import {View, Keyboard} from 'react-native'; +import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; -import Str from 'expensify-common/lib/str'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import _ from 'underscore'; import withLocalize from '../../components/withLocalize'; import ScreenWrapper from '../../components/ScreenWrapper'; @@ -13,19 +11,14 @@ import Navigation from '../../libs/Navigation/Navigation'; import styles from '../../styles/styles'; import compose from '../../libs/compose'; import ONYXKEYS from '../../ONYXKEYS'; -import TextInput from '../../components/TextInput'; -import CONST from '../../CONST'; -import Text from '../../components/Text'; import ROUTES from '../../ROUTES'; -import Form from '../../components/Form'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import reportPropTypes from '../reportPropTypes'; import personalDetailsPropType from '../personalDetailsPropType'; -import RenderHTML from '../../components/RenderHTML'; -import PressableWithoutFeedback from '../../components/Pressable/PressableWithoutFeedback'; -import * as Report from '../../libs/actions/Report'; import useLocalize from '../../hooks/useLocalize'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; +import MenuItemWithTopDescription from '../../components/MenuItemWithTopDescription'; +import CONST from '../../CONST'; const propTypes = { /** All of the personal details for everyone */ @@ -59,99 +52,35 @@ const defaultProps = { function PrivateNotesViewPage({route, personalDetailsList, session, report}) { const {translate} = useLocalize(); - const [privateNote, setPrivateNote] = useState(lodashGet(report, ['privateNotes', route.params.accountID, 'note'], '')); - const [editMode, setEditMode] = useState(_.isEmpty(privateNote)); const isCurrentUserNote = Number(session.accountID) === Number(route.params.accountID); + const privateNote = lodashGet(report, ['privateNotes', route.params.accountID, 'note'], ''); - const savePrivateNote = () => { - if (_.isEmpty(privateNote)) { - return; - } - const parser = new ExpensiMark(); - const editedNote = parser.replace(privateNote); - Report.updatePrivateNotes(report.reportID, route.params.accountID, editedNote); - Keyboard.dismiss(); - - // Enable the view mode once we have saved the updated note - setPrivateNote(editedNote); - setEditMode(false); - }; - - const switchToEditMode = () => { - if (!isCurrentUserNote) { - return; - } - // Every time we switch to edit mode we want to render the content in the markdown format - const parser = new ExpensiMark(); - setPrivateNote(parser.htmlToMarkdown(privateNote).trim()); - setEditMode(true); - }; return ( Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST)} + onBackButtonPress={() => Navigation.goBack(ROUTES.getPrivateNotesListRoute(report.reportID))} > Navigation.dismissModal()} - onBackButtonPress={() => Navigation.goBack(ROUTES.PRIVATE_NOTES_LIST)} + onBackButtonPress={() => Navigation.goBack(ROUTES.getPrivateNotesListRoute(report.reportID))} /> - - - {translate( - Str.extractEmailDomain(lodashGet(personalDetailsList, [route.params.accountID, 'login'], '')) === CONST.EMAIL.GUIDES_DOMAIN - ? 'privateNotes.sharedNoteMessage' - : 'privateNotes.personalNoteMessage', - )} - - - - {editMode ? ( -
- - setPrivateNote(text)} - /> - -
- ) : ( - - Report.clearPrivateNotesError(report.reportID, route.params.accountID)} - > - - - - )} + + isCurrentUserNote && Navigation.navigate(ROUTES.getPrivateNotesEditRoute(report.reportID, route.params.accountID))} + shouldShowRightIcon={isCurrentUserNote} + numberOfLinesTitle={0} + shouldRenderAsHTML + brickRoadIndicator={!_.isEmpty(lodashGet(report, ['privateNotes', route.params.accountID, 'errors'], '')) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} + /> +
From 3835e9c0c4a98fc4d07ec444278a22b59c6772ad Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Tue, 12 Sep 2023 18:20:10 +0530 Subject: [PATCH 20/29] revert back podfile changes --- ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 0929b541af07..2bea672171fe 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1311,4 +1311,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 845537d35601574adcd0794e17003ba7dbccdbfd -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 From 90eaae940ef2614ce1529c6fdd65098a06b54eb5 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Tue, 12 Sep 2023 18:36:23 +0530 Subject: [PATCH 21/29] testing and few fixes --- src/components/MenuItem.js | 5 ++++- src/libs/ReportUtils.js | 7 +++++++ src/pages/PrivateNotes/PrivateNotesEditPage.js | 11 ++++++++--- src/pages/PrivateNotes/PrivateNotesViewPage.js | 10 ++++++---- src/pages/ProfilePage.js | 2 +- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/components/MenuItem.js b/src/components/MenuItem.js index 4031c05e7edb..2138779bcba4 100644 --- a/src/components/MenuItem.js +++ b/src/components/MenuItem.js @@ -24,6 +24,7 @@ import variables from '../styles/variables'; import * as Session from '../libs/actions/Session'; import Hoverable from './Hoverable'; import useWindowDimensions from '../hooks/useWindowDimensions'; +import RenderHTML from './RenderHTML'; const propTypes = menuItemPropTypes; @@ -222,7 +223,9 @@ const MenuItem = React.forwardRef((props, ref) => { )} - {Boolean(props.title) && ( + {Boolean(props.title) && Boolean(props.shouldRenderAsHTML) && } + + {Boolean(props.title) && !props.shouldRenderAsHTML && ( 1) || _.isEmpty(privateNotes)) { Navigation.navigate(ROUTES.getPrivateNotesListRoute(report.reportID)); return; } + // If the privateNotes is empty, then we will redirect the user to the privateNotesEdit page + if (_.isEmpty(lodashGet(privateNotes[_.isEmpty(accountID) ? currentUserAccountID : accountID], 'note', ''))) { + Navigation.navigate(ROUTES.getPrivateNotesEditRoute(report.reportID, _.isEmpty(accountID) ? currentUserAccountID : accountID)); + return; + } + // Default the accountID to current user's accountID in case it is empty Navigation.navigate(ROUTES.getPrivateNotesViewRoute(report.reportID, _.isEmpty(accountID) ? currentUserAccountID : accountID)); } diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 149090f82f57..3dd1b5ce9ca8 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -57,14 +57,19 @@ const defaultProps = { function PrivateNotesEditPage({route, personalDetailsList, session, report}) { const {translate} = useLocalize(); - const [privateNote, setPrivateNote] = useState(lodashGet(report, ['privateNotes', route.params.accountID, 'note'], '')); + + // We need to edit the note in markdown format, but display it in HTML format + const parser = new ExpensiMark(); + const [privateNote, setPrivateNote] = useState(parser.htmlToMarkdown(lodashGet(report, ['privateNotes', route.params.accountID, 'note'], '')).trim()); const isCurrentUserNote = Number(session.accountID) === Number(route.params.accountID); const savePrivateNote = () => { - const parser = new ExpensiMark(); const editedNote = parser.replace(privateNote); Report.updatePrivateNotes(report.reportID, route.params.accountID, editedNote); Keyboard.dismiss(); + + // Navigate back to the private notes view page + Navigation.goBack(ROUTES.getPrivateNotesViewRoute(report.reportID, route.params.accountID)); }; return ( @@ -101,7 +106,7 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { errors={{ ...lodashGet(report, ['privateNotes', route.params.accountID, 'errors'], ''), }} - pendingAction={lodashGet(report, ['privateNotes', route.params.accountID, 'pendingAction'], '')} + pendingAction={lodashGet(report, ['privateNotes', route.params.accountID, 'pendingAction'], null)} onClose={() => Report.clearPrivateNotesError(report.reportID, route.params.accountID)} style={[styles.mb3]} > diff --git a/src/pages/PrivateNotes/PrivateNotesViewPage.js b/src/pages/PrivateNotes/PrivateNotesViewPage.js index 156cd8073972..267ae0b4947d 100644 --- a/src/pages/PrivateNotes/PrivateNotesViewPage.js +++ b/src/pages/PrivateNotes/PrivateNotesViewPage.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {View} from 'react-native'; +import {ScrollView} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import lodashGet from 'lodash/get'; import _ from 'underscore'; @@ -69,8 +69,8 @@ function PrivateNotesViewPage({route, personalDetailsList, session, report}) { onCloseButtonPress={() => Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack(ROUTES.getPrivateNotesListRoute(report.reportID))} /> - - + + - +
); diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 76af3b0add1f..7785e1282fbe 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -243,7 +243,7 @@ function ProfilePage(props) { titleStyle={styles.flex1} icon={Expensicons.Pencil} onPress={() => { - ReportUtils.navigateToPrivateNotesPage(chatReportWithCurrentUser, Number(lodashGet(props.session, 'accountID'))); + ReportUtils.navigateToPrivateNotesPage(chatReportWithCurrentUser); }} wrapperStyle={styles.breakAll} shouldShowRightIcon From 15e9f77660d319af07752e89216322a35fefc034 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Tue, 12 Sep 2023 19:58:48 +0530 Subject: [PATCH 22/29] some cleanups --- src/pages/ProfilePage.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 7785e1282fbe..98964931e92f 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -287,8 +287,5 @@ export default compose( betas: { key: ONYXKEYS.BETAS, }, - session: { - key: ONYXKEYS.SESSION, - }, }), )(ProfilePage); From 0518f1502aead6a5a25dd3a9940a67afd365b662 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Tue, 12 Sep 2023 20:09:16 +0530 Subject: [PATCH 23/29] update Route code to ts --- src/ROUTES.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 8ee3d8bfa6bb..13fe2ad59d3c 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -172,11 +172,11 @@ export default { // Routes related to private notes added to the report PRIVATE_NOTES_VIEW: 'r/:reportID/notes/:accountID', - getPrivateNotesViewRoute: (reportID, accountID) => `r/${reportID}/notes/${accountID}`, + getPrivateNotesViewRoute: (reportID: string, accountID: string | number) => `r/${reportID}/notes/${accountID}`, PRIVATE_NOTES_LIST: 'r/:reportID/notes', - getPrivateNotesListRoute: (reportID) => `r/${reportID}/notes`, + getPrivateNotesListRoute: (reportID: string) => `r/${reportID}/notes`, PRIVATE_NOTES_EDIT: 'r/:reportID/notes/:accountID/edit', - getPrivateNotesEditRoute: (reportID, accountID) => `r/${reportID}/notes/${accountID}/edit`, + getPrivateNotesEditRoute: (reportID: string, accountID: string | number) => `r/${reportID}/notes/${accountID}/edit`, // This is a special validation URL that will take the user to /workspace/new after validation. This is used // when linking users from e.com in order to share a session in this app. From 6c95fad7be765121ee01ec79ae7cc4b37c8906f4 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Tue, 12 Sep 2023 23:09:29 +0530 Subject: [PATCH 24/29] fix the horizontal padding for push to page --- src/pages/PrivateNotes/PrivateNotesEditPage.js | 1 - src/pages/PrivateNotes/PrivateNotesViewPage.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 3dd1b5ce9ca8..156022154955 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -106,7 +106,6 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { errors={{ ...lodashGet(report, ['privateNotes', route.params.accountID, 'errors'], ''), }} - pendingAction={lodashGet(report, ['privateNotes', route.params.accountID, 'pendingAction'], null)} onClose={() => Report.clearPrivateNotesError(report.reportID, route.params.accountID)} style={[styles.mb3]} > diff --git a/src/pages/PrivateNotes/PrivateNotesViewPage.js b/src/pages/PrivateNotes/PrivateNotesViewPage.js index 267ae0b4947d..f0a89f0c629d 100644 --- a/src/pages/PrivateNotes/PrivateNotesViewPage.js +++ b/src/pages/PrivateNotes/PrivateNotesViewPage.js @@ -69,7 +69,7 @@ function PrivateNotesViewPage({route, personalDetailsList, session, report}) { onCloseButtonPress={() => Navigation.dismissModal()} onBackButtonPress={() => Navigation.goBack(ROUTES.getPrivateNotesListRoute(report.reportID))} /> - + Date: Wed, 13 Sep 2023 19:09:35 +0530 Subject: [PATCH 25/29] donot show private notes option for thread and tasks --- src/pages/ReportDetailsPage.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 3ce86d214fb9..348fc5e6ec51 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -112,15 +112,17 @@ function ReportDetailsPage(props) { }); } - // We display private notes option for all the chat reports - items.push({ - key: CONST.REPORT_DETAILS_MENU_ITEM.PRIVATE_NOTES, - translationKey: 'privateNotes.title', - icon: Expensicons.Pencil, - isAnonymousAction: false, - action: () => ReportUtils.navigateToPrivateNotesPage(props.report), - brickRoadIndicator: Report.hasErrorInPrivateNotes(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '', - }); + // Prevent displaying private notes option for threads and task reports + if (!isThread && !ReportUtils.isTaskReport(props.report)) { + items.push({ + key: CONST.REPORT_DETAILS_MENU_ITEM.PRIVATE_NOTES, + translationKey: 'privateNotes.title', + icon: Expensicons.Pencil, + isAnonymousAction: false, + action: () => ReportUtils.navigateToPrivateNotesPage(props.report), + brickRoadIndicator: Report.hasErrorInPrivateNotes(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '', + }); + } if (isUserCreatedPolicyRoom || canLeaveRoom || isThread) { items.push({ From 8b6ac1d4f5a844e3746af517b5d7d9efda370dbb Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Fri, 15 Sep 2023 07:40:30 +0530 Subject: [PATCH 26/29] fix the navigation behaviour --- src/libs/ReportUtils.js | 31 ------------------- .../PrivateNotes/PrivateNotesEditPage.js | 9 +++--- .../PrivateNotes/PrivateNotesListPage.js | 4 +-- .../PrivateNotes/PrivateNotesViewPage.js | 4 +-- src/pages/ProfilePage.js | 5 ++- src/pages/ReportDetailsPage.js | 2 +- 6 files changed, 11 insertions(+), 44 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index b01ceef5ca50..8b84555ca2a9 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3462,36 +3462,6 @@ function getTaskAssigneeChatOnyxData(accountID, assigneeEmail, assigneeAccountID }; } -/** - * Redirect the user to either privateNotesLisPage if the report has multiple notes otherwise, it redirects the user to their own private note on the report - * - * @param {Object} report - * @param {Number} accountID - */ -function navigateToPrivateNotesPage(report, accountID) { - if (_.isEmpty(report)) { - return; - } - - const privateNotes = lodashGet(report, 'privateNotes', {}); - - // Redirect the user to privateNotesList page in case there are multiple notes and accountID is not set - // We will also redirect to the privateNotesList page if privateNotes is empty since we load the private notes on that page. - if ((_.isEmpty(accountID) && _.keys(privateNotes).length > 1) || _.isEmpty(privateNotes)) { - Navigation.navigate(ROUTES.getPrivateNotesListRoute(report.reportID)); - return; - } - - // If the privateNotes is empty, then we will redirect the user to the privateNotesEdit page - if (_.isEmpty(lodashGet(privateNotes[_.isEmpty(accountID) ? currentUserAccountID : accountID], 'note', ''))) { - Navigation.navigate(ROUTES.getPrivateNotesEditRoute(report.reportID, _.isEmpty(accountID) ? currentUserAccountID : accountID)); - return; - } - - // Default the accountID to current user's accountID in case it is empty - Navigation.navigate(ROUTES.getPrivateNotesViewRoute(report.reportID, _.isEmpty(accountID) ? currentUserAccountID : accountID)); -} - /** * Get the last 3 transactions with receipts of an IOU report that will be displayed on the report preview * @@ -3651,7 +3621,6 @@ export { buildTransactionThread, areAllRequestsBeingSmartScanned, getReportPreviewDisplayTransactions, - navigateToPrivateNotesPage, getTransactionsWithReceipts, hasMissingSmartscanFields, }; diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 156022154955..023cc38afa38 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -16,7 +16,6 @@ import ONYXKEYS from '../../ONYXKEYS'; import TextInput from '../../components/TextInput'; import CONST from '../../CONST'; import Text from '../../components/Text'; -import ROUTES from '../../ROUTES'; import Form from '../../components/Form'; import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoundView'; import reportPropTypes from '../reportPropTypes'; @@ -68,8 +67,8 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { Report.updatePrivateNotes(report.reportID, route.params.accountID, editedNote); Keyboard.dismiss(); - // Navigate back to the private notes view page - Navigation.goBack(ROUTES.getPrivateNotesViewRoute(report.reportID, route.params.accountID)); + // Take user back to the PrivateNotesView page + Navigation.goBack(); }; return ( @@ -77,14 +76,14 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { Navigation.goBack(ROUTES.getPrivateNotesViewRoute(report.reportID, route.params.accountID))} + onBackButtonPress={() => Navigation.goBack()} > Navigation.dismissModal()} - onBackButtonPress={() => Navigation.goBack(ROUTES.getPrivateNotesViewRoute(report.reportID, route.params.accountID))} + onBackButtonPress={() => Navigation.goBack()} /> diff --git a/src/pages/PrivateNotes/PrivateNotesListPage.js b/src/pages/PrivateNotes/PrivateNotesListPage.js index 754d1aebfb19..5ea081a12f25 100644 --- a/src/pages/PrivateNotes/PrivateNotesListPage.js +++ b/src/pages/PrivateNotes/PrivateNotesListPage.js @@ -10,7 +10,6 @@ import styles from '../../styles/styles'; import compose from '../../libs/compose'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; import MenuItem from '../../components/MenuItem'; -import * as ReportUtils from '../../libs/ReportUtils'; import useLocalize from '../../hooks/useLocalize'; import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator'; import * as Report from '../../libs/actions/Report'; @@ -23,6 +22,7 @@ import FullPageNotFoundView from '../../components/BlockingViews/FullPageNotFoun import HeaderWithBackButton from '../../components/HeaderWithBackButton'; import {withNetwork} from '../../components/OnyxProvider'; import networkPropTypes from '../../components/networkPropTypes'; +import ROUTES from '../../ROUTES'; const propTypes = { /** The report currently being looked at */ @@ -110,7 +110,7 @@ function PrivateNotesListPage({report, personalDetailsList, network, session}) { title: Number(lodashGet(session, 'accountID', null)) === Number(accountID) ? 'My note' : lodashGet(personalDetailsList, [accountID, 'login'], ''), icon: UserUtils.getAvatar(lodashGet(personalDetailsList, [accountID, 'avatar'], UserUtils.getDefaultAvatar(accountID)), accountID), iconType: CONST.ICON_TYPE_AVATAR, - action: () => ReportUtils.navigateToPrivateNotesPage(report, accountID), + action: () => Navigation.navigate(ROUTES.getPrivateNotesViewRoute(report.reportID, accountID)), brickRoadIndicator: privateNoteBrickRoadIndicator(accountID), })) .value(); diff --git a/src/pages/PrivateNotes/PrivateNotesViewPage.js b/src/pages/PrivateNotes/PrivateNotesViewPage.js index f0a89f0c629d..86814ed4dc92 100644 --- a/src/pages/PrivateNotes/PrivateNotesViewPage.js +++ b/src/pages/PrivateNotes/PrivateNotesViewPage.js @@ -60,14 +60,14 @@ function PrivateNotesViewPage({route, personalDetailsList, session, report}) { Navigation.goBack(ROUTES.getPrivateNotesListRoute(report.reportID))} + onBackButtonPress={() => Navigation.goBack()} > Navigation.dismissModal()} - onBackButtonPress={() => Navigation.goBack(ROUTES.getPrivateNotesListRoute(report.reportID))} + onBackButtonPress={() => Navigation.goBack()} /> diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 98964931e92f..19f2b1fdc0c6 100755 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -36,6 +36,7 @@ import * as Illustrations from '../components/Icon/Illustrations'; import variables from '../styles/variables'; import * as ValidationUtils from '../libs/ValidationUtils'; import Permissions from '../libs/Permissions'; +import ROUTES from '../ROUTES'; const matchType = PropTypes.shape({ params: PropTypes.shape({ @@ -242,9 +243,7 @@ function ProfilePage(props) { title={`${props.translate('privateNotes.title')}`} titleStyle={styles.flex1} icon={Expensicons.Pencil} - onPress={() => { - ReportUtils.navigateToPrivateNotesPage(chatReportWithCurrentUser); - }} + onPress={() => Navigation.navigate(ROUTES.getPrivateNotesListRoute(chatReportWithCurrentUser.reportID))} wrapperStyle={styles.breakAll} shouldShowRightIcon brickRoadIndicator={Report.hasErrorInPrivateNotes(chatReportWithCurrentUser) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''} diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 348fc5e6ec51..3a9e0f5c2eb8 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -119,7 +119,7 @@ function ReportDetailsPage(props) { translationKey: 'privateNotes.title', icon: Expensicons.Pencil, isAnonymousAction: false, - action: () => ReportUtils.navigateToPrivateNotesPage(props.report), + action: () => Navigation.navigate(ROUTES.getPrivateNotesListRoute(props.report.reportID)), brickRoadIndicator: Report.hasErrorInPrivateNotes(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '', }); } From 3a0725876b3cf19f4201a1c43954124e695ddcbd Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Fri, 15 Sep 2023 15:43:10 +0530 Subject: [PATCH 27/29] add auto-focus --- src/pages/PrivateNotes/PrivateNotesEditPage.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 023cc38afa38..f909239a2b48 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, {useState, useRef} from 'react'; import PropTypes from 'prop-types'; import {View, Keyboard} from 'react-native'; import {withOnyx} from 'react-native-onyx'; @@ -62,6 +62,9 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { const [privateNote, setPrivateNote] = useState(parser.htmlToMarkdown(lodashGet(report, ['privateNotes', route.params.accountID, 'note'], '')).trim()); const isCurrentUserNote = Number(session.accountID) === Number(route.params.accountID); + // To focus on the input field when the page loads + const privateNotesInput = useRef(null); + const savePrivateNote = () => { const editedNote = parser.replace(privateNote); Report.updatePrivateNotes(report.reportID, route.params.accountID, editedNote); @@ -72,7 +75,16 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { }; return ( - + { + if (!privateNotesInput.current) { + return; + } + + privateNotesInput.current.focus(); + }} + > setPrivateNote(text)} + ref={(el) => (privateNotesInput.current = el)} /> From 9cfa85ff753c5d1cbf432ccef59e2f8eb63ef46b Mon Sep 17 00:00:00 2001 From: Vivek Kumar Date: Fri, 15 Sep 2023 15:52:40 +0530 Subject: [PATCH 28/29] set the cursor at end for web, desktop and mweb --- src/pages/PrivateNotes/PrivateNotesEditPage.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index f909239a2b48..70afebb3d5bf 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -83,6 +83,12 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { } privateNotesInput.current.focus(); + + // Below condition is needed for web, desktop and mweb only, for native cursor to position at end by default. + if (privateNotesInput.current.value && privateNotesInput.current.setSelectionRange) { + const length = privateNotesInput.current.value.length; + privateNotesInput.current.setSelectionRange(length, length); + } }} > Date: Fri, 15 Sep 2023 16:14:59 +0530 Subject: [PATCH 29/29] use focusAndUpdateMultilineInputRange for auto-focus --- src/pages/PrivateNotes/PrivateNotesEditPage.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index 70afebb3d5bf..4cada83941ac 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -23,6 +23,7 @@ import personalDetailsPropType from '../personalDetailsPropType'; import * as Report from '../../libs/actions/Report'; import useLocalize from '../../hooks/useLocalize'; import OfflineWithFeedback from '../../components/OfflineWithFeedback'; +import focusAndUpdateMultilineInputRange from '../../libs/focusAndUpdateMultilineInputRange'; const propTypes = { /** All of the personal details for everyone */ @@ -77,19 +78,7 @@ function PrivateNotesEditPage({route, personalDetailsList, session, report}) { return ( { - if (!privateNotesInput.current) { - return; - } - - privateNotesInput.current.focus(); - - // Below condition is needed for web, desktop and mweb only, for native cursor to position at end by default. - if (privateNotesInput.current.value && privateNotesInput.current.setSelectionRange) { - const length = privateNotesInput.current.value.length; - privateNotesInput.current.setSelectionRange(length, length); - } - }} + onEntryTransitionEnd={() => focusAndUpdateMultilineInputRange(privateNotesInput.current)} >