From 467e9815601469434c93827e8946ac32e95fca18 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 4 Apr 2023 16:14:24 +0200 Subject: [PATCH 001/349] Navigate back after changing the name --- src/pages/ReportSettingsPage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/ReportSettingsPage.js b/src/pages/ReportSettingsPage.js index f3af110f686..56951be5cde 100644 --- a/src/pages/ReportSettingsPage.js +++ b/src/pages/ReportSettingsPage.js @@ -25,6 +25,7 @@ import reportPropTypes from './reportPropTypes'; import withReportOrNotFound from './home/report/withReportOrNotFound'; import Form from '../components/Form'; import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundView'; +import ROUTES from "../ROUTES"; const propTypes = { /** Route params */ @@ -111,6 +112,7 @@ class ReportSettingsPage extends Component { return; } Report.updatePolicyRoomName(this.props.report, values.newRoomName); + Navigation.goBack(); } /** From f9a98c78dc4b6fb04782be8a15e5817bf080d3fd Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 4 Apr 2023 16:14:41 +0200 Subject: [PATCH 002/349] remove unneded import --- src/pages/ReportSettingsPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/ReportSettingsPage.js b/src/pages/ReportSettingsPage.js index 56951be5cde..e7d9b0a0079 100644 --- a/src/pages/ReportSettingsPage.js +++ b/src/pages/ReportSettingsPage.js @@ -25,7 +25,6 @@ import reportPropTypes from './reportPropTypes'; import withReportOrNotFound from './home/report/withReportOrNotFound'; import Form from '../components/Form'; import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundView'; -import ROUTES from "../ROUTES"; const propTypes = { /** Route params */ From 09a03b586a882bd87084bb16afedcbc1e1d7df4c Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 5 Apr 2023 14:13:47 +0200 Subject: [PATCH 003/349] remove navigation back --- src/pages/ReportSettingsPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/ReportSettingsPage.js b/src/pages/ReportSettingsPage.js index e7d9b0a0079..f3af110f686 100644 --- a/src/pages/ReportSettingsPage.js +++ b/src/pages/ReportSettingsPage.js @@ -111,7 +111,6 @@ class ReportSettingsPage extends Component { return; } Report.updatePolicyRoomName(this.props.report, values.newRoomName); - Navigation.goBack(); } /** From 2cb94247786d356ac4a97df2ac5a7d6c6ffdaaae Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 5 Apr 2023 14:51:07 +0200 Subject: [PATCH 004/349] create new RoomNamePage component --- .../AppNavigator/ModalStackNavigators.js | 2 +- src/pages/ReportSettingsPage.js | 266 ------------------ 2 files changed, 1 insertion(+), 267 deletions(-) delete mode 100644 src/pages/ReportSettingsPage.js diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 7b46354e939..9f8b9ea0480 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -148,7 +148,7 @@ const ReportDetailsModalStackNavigator = createModalStackNavigator([{ const ReportSettingsModalStackNavigator = createModalStackNavigator([{ getComponent: () => { - const ReportSettingsPage = require('../../../pages/ReportSettingsPage').default; + const ReportSettingsPage = require('../../../pages/settings/Report/ReportSettingsPage').default; return ReportSettingsPage; }, name: 'Report_Settings_Root', diff --git a/src/pages/ReportSettingsPage.js b/src/pages/ReportSettingsPage.js deleted file mode 100644 index f3af110f686..00000000000 --- a/src/pages/ReportSettingsPage.js +++ /dev/null @@ -1,266 +0,0 @@ -import React, {Component} 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 CONST from '../CONST'; -import ONYXKEYS from '../ONYXKEYS'; -import styles from '../styles/styles'; -import compose from '../libs/compose'; -import Navigation from '../libs/Navigation/Navigation'; -import * as Report from '../libs/actions/Report'; -import * as Policy from '../libs/actions/Policy'; -import * as ReportUtils from '../libs/ReportUtils'; -import HeaderWithCloseButton from '../components/HeaderWithCloseButton'; -import ScreenWrapper from '../components/ScreenWrapper'; -import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; -import Text from '../components/Text'; -import RoomNameInput from '../components/RoomNameInput'; -import Picker from '../components/Picker'; -import * as ValidationUtils from '../libs/ValidationUtils'; -import * as ErrorUtils from '../libs/ErrorUtils'; -import OfflineWithFeedback from '../components/OfflineWithFeedback'; -import reportPropTypes from './reportPropTypes'; -import withReportOrNotFound from './home/report/withReportOrNotFound'; -import Form from '../components/Form'; -import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundView'; - -const propTypes = { - /** Route params */ - route: PropTypes.shape({ - params: PropTypes.shape({ - /** Report ID passed via route r/:reportID/settings */ - reportID: PropTypes.string, - }), - }).isRequired, - - ...withLocalizePropTypes, - - /* Onyx Props */ - - /** The active report */ - report: reportPropTypes.isRequired, - - /** The policies which the user has access to and which the report could be tied to */ - policies: PropTypes.shape({ - /** The policy name */ - name: PropTypes.string, - - /** ID of the policy */ - id: PropTypes.string, - }), - - /** All reports shared with the user */ - reports: PropTypes.objectOf(reportPropTypes), -}; - -const defaultProps = { - policies: {}, - reports: {}, -}; - -class ReportSettingsPage extends Component { - constructor(props) { - super(props); - - this.validate = this.validate.bind(this); - } - - getNotificationPreferenceOptions() { - return [ - {value: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, label: this.props.translate('notificationPreferences.immediately')}, - {value: CONST.REPORT.NOTIFICATION_PREFERENCE.DAILY, label: this.props.translate('notificationPreferences.daily')}, - {value: CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE, label: this.props.translate('notificationPreferences.mute')}, - ]; - } - - /** - * @param {Object|null} linkedWorkspace - the workspace the report is on, null if the user isn't a member of the workspace - * @returns {Boolean} - */ - shouldDisableRename(linkedWorkspace) { - if (ReportUtils.isDefaultRoom(this.props.report) || ReportUtils.isArchivedRoom(this.props.report)) { - return true; - } - - // The remaining checks only apply to public rooms - if (!ReportUtils.isPublicRoom(this.props.report)) { - return false; - } - - // if the linked workspace is null, that means the person isn't a member of the workspace the report is in - // which means this has to be a public room we want to disable renaming for - if (!linkedWorkspace) { - return true; - } - - // If there is a linked workspace, that means the user is a member of the workspace the report is in. - // Still, we only want policy owners and admins to be able to modify the name. - return !Policy.isPolicyOwner(linkedWorkspace) && linkedWorkspace.role !== CONST.POLICY.ROLE.ADMIN; - } - - /** - * @param {Object} values - form input values passed by the Form component - */ - updatePolicyRoomName(values) { - Keyboard.dismiss(); - - // When the room name has not changed, skip the Form submission - if (values.newRoomName === this.props.report.reportName) { - return; - } - Report.updatePolicyRoomName(this.props.report, values.newRoomName); - } - - /** - * @param {Object} values - form input values passed by the Form component - * @returns {Boolean} - */ - validate(values) { - const errors = {}; - - // We should skip validation hence we return an empty errors and we skip Form submission on the onSubmit method - if (values.newRoomName === this.props.report.reportName) { - return errors; - } - - if (!values.newRoomName || values.newRoomName === CONST.POLICY.ROOM_PREFIX) { - // We error if the user doesn't enter a room name or left blank - ErrorUtils.addErrorMessage(errors, 'newRoomName', this.props.translate('newRoomPage.pleaseEnterRoomName')); - } else if (values.newRoomName !== CONST.POLICY.ROOM_PREFIX && !ValidationUtils.isValidRoomName(values.newRoomName)) { - // We error if the room name has invalid characters - ErrorUtils.addErrorMessage(errors, 'newRoomName', this.props.translate('newRoomPage.roomNameInvalidError')); - } else if (ValidationUtils.isReservedRoomName(values.newRoomName)) { - // Certain names are reserved for default rooms and should not be used for policy rooms. - ErrorUtils.addErrorMessage(errors, 'newRoomName', this.props.translate('newRoomPage.roomNameReservedError', {reservedName: values.newRoomName})); - } else if (ValidationUtils.isExistingRoomName(values.newRoomName, this.props.reports, this.props.report.policyID)) { - // Certain names are reserved for default rooms and should not be used for policy rooms. - ErrorUtils.addErrorMessage(errors, 'newRoomName', this.props.translate('newRoomPage.roomAlreadyExistsError')); - } - - return errors; - } - - render() { - const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(this.props.report); - const linkedWorkspace = _.find(this.props.policies, policy => policy && policy.id === this.props.report.policyID); - const shouldDisableRename = this.shouldDisableRename(linkedWorkspace); - - return ( - - - -
!shouldDisableRename && this.updatePolicyRoomName(values)} - scrollContextEnabled - isSubmitButtonVisible={shouldShowRoomName && !shouldDisableRename} - enabledWhenOffline - > - - - { - if (this.props.report.notificationPreference === notificationPreference) { - return; - } - - Report.updateNotificationPreference( - this.props.report.reportID, - this.props.report.notificationPreference, - notificationPreference, - ); - }} - items={this.getNotificationPreferenceOptions()} - value={this.props.report.notificationPreference} - /> - - - {shouldShowRoomName && ( - - Report.clearPolicyRoomNameErrors(this.props.report.reportID)} - > - - - {shouldDisableRename ? ( - - - {this.props.translate('newRoomPage.roomName')} - - - {this.props.report.reportName} - - - ) - : ( - - )} - - - - - )} - {linkedWorkspace && ( - - - {this.props.translate('workspace.common.workspace')} - - - {linkedWorkspace.name} - - - )} - {this.props.report.visibility && ( - - - {this.props.translate('newRoomPage.visibility')} - - - {this.props.translate(`newRoomPage.visibilityOptions.${this.props.report.visibility}`)} - - - { - this.props.report.visibility === CONST.REPORT.VISIBILITY.RESTRICTED - ? this.props.translate('newRoomPage.restrictedDescription') - : this.props.translate('newRoomPage.privateDescription') - } - - - )} -
-
-
- ); - } -} - -ReportSettingsPage.propTypes = propTypes; -ReportSettingsPage.defaultProps = defaultProps; -export default compose( - withLocalize, - withReportOrNotFound, - withOnyx({ - policies: { - key: ONYXKEYS.COLLECTION.POLICY, - }, - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, - }), -)(ReportSettingsPage); From fd706e6a6a72fc2bb6d498250d498784dd05c181 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 5 Apr 2023 14:52:01 +0200 Subject: [PATCH 005/349] actually add the page --- src/ONYXKEYS.js | 1 + src/components/MemoryGame/MemoryGame.js | 0 .../settings/Report/ReportSettingsPage.js | 267 ++++++++++++++++++ src/pages/settings/Report/RoomNamePage.js | 86 ++++++ 4 files changed, 354 insertions(+) create mode 100644 src/components/MemoryGame/MemoryGame.js create mode 100644 src/pages/settings/Report/ReportSettingsPage.js create mode 100644 src/pages/settings/Report/RoomNamePage.js diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js index 6fe8a0625cd..4f1a2062f09 100755 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -182,6 +182,7 @@ export default { CLOSE_ACCOUNT_FORM: 'closeAccount', PROFILE_SETTINGS_FORM: 'profileSettingsForm', DISPLAY_NAME_FORM: 'displayNameForm', + ROOM_NAME_FORM: 'roomNameForm', LEGAL_NAME_FORM: 'legalNameForm', DATE_OF_BIRTH_FORM: 'dateOfBirthForm', HOME_ADDRESS_FORM: 'homeAddressForm', diff --git a/src/components/MemoryGame/MemoryGame.js b/src/components/MemoryGame/MemoryGame.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js new file mode 100644 index 00000000000..58d7f1c1206 --- /dev/null +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -0,0 +1,267 @@ +import React, {Component} 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 CONST from '../../../CONST'; +import ONYXKEYS from '../../../ONYXKEYS'; +import styles from '../../../styles/styles'; +import compose from '../../../libs/compose'; +import Navigation from '../../../libs/Navigation/Navigation'; +import * as Report from '../../../libs/actions/Report'; +import * as Policy from '../../../libs/actions/Policy'; +import * as ReportUtils from '../../../libs/ReportUtils'; +import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; +import ScreenWrapper from '../../../components/ScreenWrapper'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; +import Text from '../../../components/Text'; +import RoomNameInput from '../../../components/RoomNameInput'; +import Picker from '../../../components/Picker'; +import * as ValidationUtils from '../../../libs/ValidationUtils'; +import * as ErrorUtils from '../../../libs/ErrorUtils'; +import OfflineWithFeedback from '../../../components/OfflineWithFeedback'; +import reportPropTypes from '../../reportPropTypes'; +import withReportOrNotFound from '../../home/report/withReportOrNotFound'; +import Form from '../../../components/Form'; +import FullPageNotFoundView from '../../../components/BlockingViews/FullPageNotFoundView'; + +const propTypes = { + /** Route params */ + route: PropTypes.shape({ + params: PropTypes.shape({ + /** Report ID passed via route r/:reportID/settings */ + reportID: PropTypes.string, + }), + }).isRequired, + + ...withLocalizePropTypes, + + /* Onyx Props */ + + /** The active report */ + report: reportPropTypes.isRequired, + + /** The policies which the user has access to and which the report could be tied to */ + policies: PropTypes.shape({ + /** The policy name */ + name: PropTypes.string, + + /** ID of the policy */ + id: PropTypes.string, + }), + + /** All reports shared with the user */ + reports: PropTypes.objectOf(reportPropTypes), +}; + +const defaultProps = { + policies: {}, + reports: {}, +}; + + +class ReportSettingsPage extends Component { + constructor(props) { + super(props); + + this.validate = this.validate.bind(this); + } + + getNotificationPreferenceOptions() { + return [ + {value: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, label: this.props.translate('notificationPreferences.immediately')}, + {value: CONST.REPORT.NOTIFICATION_PREFERENCE.DAILY, label: this.props.translate('notificationPreferences.daily')}, + {value: CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE, label: this.props.translate('notificationPreferences.mute')}, + ]; + } + + /** + * @param {Object|null} linkedWorkspace - the workspace the report is on, null if the user isn't a member of the workspace + * @returns {Boolean} + */ + shouldDisableRename(linkedWorkspace) { + if (ReportUtils.isDefaultRoom(this.props.report) || ReportUtils.isArchivedRoom(this.props.report)) { + return true; + } + + // The remaining checks only apply to public rooms + if (!ReportUtils.isPublicRoom(this.props.report)) { + return false; + } + + // if the linked workspace is null, that means the person isn't a member of the workspace the report is in + // which means this has to be a public room we want to disable renaming for + if (!linkedWorkspace) { + return true; + } + + // If there is a linked workspace, that means the user is a member of the workspace the report is in. + // Still, we only want policy owners and admins to be able to modify the name. + return !Policy.isPolicyOwner(linkedWorkspace) && linkedWorkspace.role !== CONST.POLICY.ROLE.ADMIN; + } + + /** + * @param {Object} values - form input values passed by the Form component + */ + updatePolicyRoomName(values) { + Keyboard.dismiss(); + + // When the room name has not changed, skip the Form submission + if (values.newRoomName === this.props.report.reportName) { + return; + } + Report.updatePolicyRoomName(this.props.report, values.newRoomName); + } + + /** + * @param {Object} values - form input values passed by the Form component + * @returns {Boolean} + */ + validate(values) { + const errors = {}; + + // We should skip validation hence we return an empty errors and we skip Form submission on the onSubmit method + if (values.newRoomName === this.props.report.reportName) { + return errors; + } + + if (!values.newRoomName || values.newRoomName === CONST.POLICY.ROOM_PREFIX) { + // We error if the user doesn't enter a room name or left blank + ErrorUtils.addErrorMessage(errors, 'newRoomName', this.props.translate('newRoomPage.pleaseEnterRoomName')); + } else if (values.newRoomName !== CONST.POLICY.ROOM_PREFIX && !ValidationUtils.isValidRoomName(values.newRoomName)) { + // We error if the room name has invalid characters + ErrorUtils.addErrorMessage(errors, 'newRoomName', this.props.translate('newRoomPage.roomNameInvalidError')); + } else if (ValidationUtils.isReservedRoomName(values.newRoomName)) { + // Certain names are reserved for default rooms and should not be used for policy rooms. + ErrorUtils.addErrorMessage(errors, 'newRoomName', this.props.translate('newRoomPage.roomNameReservedError', {reservedName: values.newRoomName})); + } else if (ValidationUtils.isExistingRoomName(values.newRoomName, this.props.reports, this.props.report.policyID)) { + // Certain names are reserved for default rooms and should not be used for policy rooms. + ErrorUtils.addErrorMessage(errors, 'newRoomName', this.props.translate('newRoomPage.roomAlreadyExistsError')); + } + + return errors; + } + + render() { + const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(this.props.report); + const linkedWorkspace = _.find(this.props.policies, policy => policy && policy.id === this.props.report.policyID); + const shouldDisableRename = this.shouldDisableRename(linkedWorkspace); + + return ( + + + +
!shouldDisableRename && this.updatePolicyRoomName(values)} + scrollContextEnabled + isSubmitButtonVisible={shouldShowRoomName && !shouldDisableRename} + enabledWhenOffline + > + + + { + if (this.props.report.notificationPreference === notificationPreference) { + return; + } + + Report.updateNotificationPreference( + this.props.report.reportID, + this.props.report.notificationPreference, + notificationPreference, + ); + }} + items={this.getNotificationPreferenceOptions()} + value={this.props.report.notificationPreference} + /> + + + {shouldShowRoomName && ( + + Report.clearPolicyRoomNameErrors(this.props.report.reportID)} + > + + + {shouldDisableRename ? ( + + + {this.props.translate('newRoomPage.roomName')} + + + {this.props.report.reportName} + + + ) + : ( + + )} + + + + + )} + {linkedWorkspace && ( + + + {this.props.translate('workspace.common.workspace')} + + + {linkedWorkspace.name} + + + )} + {this.props.report.visibility && ( + + + {this.props.translate('newRoomPage.visibility')} + + + {this.props.translate(`newRoomPage.visibilityOptions.${this.props.report.visibility}`)} + + + { + this.props.report.visibility === CONST.REPORT.VISIBILITY.RESTRICTED + ? this.props.translate('newRoomPage.restrictedDescription') + : this.props.translate('newRoomPage.privateDescription') + } + + + )} +
+
+
+ ); + } +} + +ReportSettingsPage.propTypes = propTypes; +ReportSettingsPage.defaultProps = defaultProps; +export default compose( + withLocalize, + withReportOrNotFound, + withOnyx({ + policies: { + key: ONYXKEYS.COLLECTION.POLICY, + }, + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + }), +)(ReportSettingsPage); diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js new file mode 100644 index 00000000000..af53f9671eb --- /dev/null +++ b/src/pages/settings/Report/RoomNamePage.js @@ -0,0 +1,86 @@ +import lodashGet from 'lodash/get'; +import React, {Component} from 'react'; +import {View} from 'react-native'; +import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../components/withCurrentUserPersonalDetails'; +import ScreenWrapper from '../../../components/ScreenWrapper'; +import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; +import ROUTES from '../../../ROUTES'; +import Form from '../../../components/Form'; +import ONYXKEYS from '../../../ONYXKEYS'; +import CONST from '../../../CONST'; +import * as ValidationUtils from '../../../libs/ValidationUtils'; +import TextInput from '../../../components/TextInput'; +import Text from '../../../components/Text'; +import styles from '../../../styles/styles'; +import Navigation from '../../../libs/Navigation/Navigation'; +import * as PersonalDetails from '../../../libs/actions/PersonalDetails'; +import compose from '../../../libs/compose'; +import * as ErrorUtils from '../../../libs/ErrorUtils'; +import * as RoomNameInputUtils from "../../../libs/RoomNameInputUtils"; + +const propTypes = { + ...withLocalizePropTypes, + ...withCurrentUserPersonalDetailsPropTypes, +}; + +const defaultProps = { + ...withCurrentUserPersonalDetailsDefaultProps, +}; + +class RoomNamePage extends Component { + constructor(props) { + super(props); + + this.validate = this.validate.bind(this); + this.updateRoomName = this.updateRoomName.bind(this); + } + + /** + * Submit form to update room's name + * @param {Object} values + * @param {String} values.roomName + */ + updateRoomName(values) { + RoomNameInputUtils.modifyRoomName(values.roomName); + } + + render() { + return ( + + Navigation.navigate(ROUTES.SETTINGS_PROFILE)} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> +
+ + + +
+
+ ); + } +} + +RoomNamePage.propTypes = propTypes; +RoomNamePage.defaultProps = defaultProps; + +export default compose( + withLocalize, + withCurrentUserPersonalDetails, +)(RoomNamePage); From e5b37e7b4b788f89ca340cadbcc09d74f1bf4d3b Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 5 Apr 2023 14:53:18 +0200 Subject: [PATCH 006/349] actually add them --- src/components/MemoryGame/MemoryGame.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/components/MemoryGame/MemoryGame.js diff --git a/src/components/MemoryGame/MemoryGame.js b/src/components/MemoryGame/MemoryGame.js deleted file mode 100644 index e69de29bb2d..00000000000 From 512e83950b317334c68f059a8292d92c6d5cca18 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 5 Apr 2023 14:54:03 +0200 Subject: [PATCH 007/349] remove unneeded imports --- src/pages/settings/Report/RoomNamePage.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index af53f9671eb..02bc450b0fa 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -1,4 +1,3 @@ -import lodashGet from 'lodash/get'; import React, {Component} from 'react'; import {View} from 'react-native'; import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../components/withCurrentUserPersonalDetails'; @@ -9,14 +8,10 @@ import ROUTES from '../../../ROUTES'; import Form from '../../../components/Form'; import ONYXKEYS from '../../../ONYXKEYS'; import CONST from '../../../CONST'; -import * as ValidationUtils from '../../../libs/ValidationUtils'; import TextInput from '../../../components/TextInput'; -import Text from '../../../components/Text'; import styles from '../../../styles/styles'; import Navigation from '../../../libs/Navigation/Navigation'; -import * as PersonalDetails from '../../../libs/actions/PersonalDetails'; import compose from '../../../libs/compose'; -import * as ErrorUtils from '../../../libs/ErrorUtils'; import * as RoomNameInputUtils from "../../../libs/RoomNameInputUtils"; const propTypes = { From 642b69844a1c58f758dd7f9dff519642e9576068 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 5 Apr 2023 14:58:19 +0200 Subject: [PATCH 008/349] cleanup component --- src/pages/settings/Report/RoomNamePage.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index 02bc450b0fa..6bf36cf7774 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -15,12 +15,12 @@ import compose from '../../../libs/compose'; import * as RoomNameInputUtils from "../../../libs/RoomNameInputUtils"; const propTypes = { + reportID: PropTypes.string.isRequired, + roomName: PropTypes.string.isRequired, ...withLocalizePropTypes, - ...withCurrentUserPersonalDetailsPropTypes, }; const defaultProps = { - ...withCurrentUserPersonalDetailsDefaultProps, }; class RoomNamePage extends Component { @@ -46,7 +46,7 @@ class RoomNamePage extends Component { Navigation.navigate(ROUTES.SETTINGS_PROFILE)} + onBackButtonPress={() => Navigation.getReportSettingsRoute(this.props.reportID)} onCloseButtonPress={() => Navigation.dismissModal(true)} />
From 05ca854445b506e7bb0aa96d4b7d92966e596bf2 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 5 Apr 2023 15:04:05 +0200 Subject: [PATCH 009/349] more cleanup --- src/pages/settings/Report/ReportSettingsPage.js | 6 ++++-- src/pages/settings/Report/RoomNamePage.js | 13 ++----------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index 58d7f1c1206..5fc8f40723b 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -25,6 +25,7 @@ import reportPropTypes from '../../reportPropTypes'; import withReportOrNotFound from '../../home/report/withReportOrNotFound'; import Form from '../../../components/Form'; import FullPageNotFoundView from '../../../components/BlockingViews/FullPageNotFoundView'; +import RoomNamePage from "./RoomNamePage"; const propTypes = { /** Route params */ @@ -207,9 +208,10 @@ class ReportSettingsPage extends Component { ) : ( - )} diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index 6bf36cf7774..ea46795ca00 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -1,10 +1,9 @@ import React, {Component} from 'react'; +import PropTypes from 'prop-types'; import {View} from 'react-native'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../../components/withCurrentUserPersonalDetails'; import ScreenWrapper from '../../../components/ScreenWrapper'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; -import ROUTES from '../../../ROUTES'; import Form from '../../../components/Form'; import ONYXKEYS from '../../../ONYXKEYS'; import CONST from '../../../CONST'; @@ -12,7 +11,7 @@ import TextInput from '../../../components/TextInput'; import styles from '../../../styles/styles'; import Navigation from '../../../libs/Navigation/Navigation'; import compose from '../../../libs/compose'; -import * as RoomNameInputUtils from "../../../libs/RoomNameInputUtils"; +import * as RoomNameInputUtils from '../../../libs/RoomNameInputUtils'; const propTypes = { reportID: PropTypes.string.isRequired, @@ -20,14 +19,9 @@ const propTypes = { ...withLocalizePropTypes, }; -const defaultProps = { -}; - class RoomNamePage extends Component { constructor(props) { super(props); - - this.validate = this.validate.bind(this); this.updateRoomName = this.updateRoomName.bind(this); } @@ -52,7 +46,6 @@ class RoomNamePage extends Component { Date: Wed, 5 Apr 2023 15:24:05 +0200 Subject: [PATCH 010/349] add route --- src/ROUTES.js | 2 ++ .../Navigation/AppNavigator/ModalStackNavigators.js | 10 +++++++++- src/libs/Navigation/linkingConfig.js | 7 ++++++- src/pages/settings/Report/ReportSettingsPage.js | 10 ++++++---- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/ROUTES.js b/src/ROUTES.js index d5ba2406e32..35570607b1f 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -102,7 +102,9 @@ export default { REPORT_WITH_ID_DETAILS: 'r/:reportID/details', getReportDetailsRoute: reportID => `r/${reportID}/details`, REPORT_SETTINGS: 'r/:reportID/settings', + REPORT_SETTINGS_ROOM_NAME: 'r/:reportID/settings/room-name', getReportSettingsRoute: reportID => `r/${reportID}/settings`, + getReportSettingsRoomNameRoute: reportID => `r/${reportID}/settings/room-name`, TRANSITION_FROM_OLD_DOT: 'transition', VALIDATE_LOGIN: 'v/:accountID/:validateCode', GET_ASSISTANCE: 'get-assistance/:taskID', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 9f8b9ea0480..8b0ac182565 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -152,7 +152,15 @@ const ReportSettingsModalStackNavigator = createModalStackNavigator([{ return ReportSettingsPage; }, name: 'Report_Settings_Root', -}]); +}, +{ + getComponent: () => { + const RoomNamePage = require('../../../pages/settings/Report/RoomNamePage').default; + return RoomNamePage; + }, + name: 'Report_Settings_Room_Name', +}, +]); const ReportParticipantsModalStackNavigator = createModalStackNavigator([ { diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index a096a742186..50e4f61472e 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -192,7 +192,12 @@ export default { }, Report_Settings: { screens: { - Report_Settings_Root: ROUTES.REPORT_SETTINGS, + Report_Settings_Root: { + path: ROUTES.REPORT_SETTINGS, + }, + Report_Settings_Room_Name: { + path: ROUTES.REPORT_SETTINGS_ROOM_NAME, + }, }, }, NewGroup: { diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index 5fc8f40723b..d9e7695e603 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -26,6 +26,8 @@ import withReportOrNotFound from '../../home/report/withReportOrNotFound'; import Form from '../../../components/Form'; import FullPageNotFoundView from '../../../components/BlockingViews/FullPageNotFoundView'; import RoomNamePage from "./RoomNamePage"; +import MenuItemWithTopDescription from "../../../components/MenuItemWithTopDescription"; +import ROUTES from "../../../ROUTES"; const propTypes = { /** Route params */ @@ -208,10 +210,10 @@ class ReportSettingsPage extends Component { ) : ( - Navigation.navigate(ROUTES.getReportSettingsRoute(this.props.report.reportID))} /> )} From 891b87fc8e0aaa47b3e080500aa8731adb94a063 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 5 Apr 2023 15:55:24 +0200 Subject: [PATCH 011/349] get right props --- src/pages/settings/Report/ReportSettingsPage.js | 5 +++++ src/pages/settings/Report/RoomNamePage.js | 13 +++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index d9e7695e603..a4307b6bd60 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -160,6 +160,11 @@ class ReportSettingsPage extends Component { onBackButtonPress={Navigation.goBack} onCloseButtonPress={Navigation.dismissModal} /> + Navigation.navigate(ROUTES.getReportSettingsRoomNameRoute(this.props.report.reportID))} + /> Navigation.getReportSettingsRoute(this.props.reportID)} + onBackButtonPress={() => Navigation.getReportSettingsRoute(this.props.report.reportID)} onCloseButtonPress={() => Navigation.dismissModal(true)} /> @@ -69,4 +73,5 @@ RoomNamePage.propTypes = propTypes; export default compose( withLocalize, + withReportOrNotFound, )(RoomNamePage); From 6ec699cacf5c0c893e2555f587548b2482e790f2 Mon Sep 17 00:00:00 2001 From: Alberto Date: Wed, 5 Apr 2023 16:01:58 +0200 Subject: [PATCH 012/349] add validation --- src/pages/settings/Report/RoomNamePage.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index 93670aa22c0..25c46593b80 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -14,6 +14,9 @@ import compose from '../../../libs/compose'; import * as RoomNameInputUtils from '../../../libs/RoomNameInputUtils'; import withReportOrNotFound from '../../home/report/withReportOrNotFound'; import reportPropTypes from '../../reportPropTypes'; +import ROUTES from "../../../ROUTES"; +import * as ValidationUtils from "../../../libs/ValidationUtils"; +import _ from "underscore/underscore-node.mjs"; const propTypes = { ...withLocalizePropTypes, @@ -27,6 +30,7 @@ class RoomNamePage extends Component { constructor(props) { super(props); this.updateRoomName = this.updateRoomName.bind(this); + this.validate = this.validate.bind(this); } /** @@ -38,19 +42,34 @@ class RoomNamePage extends Component { RoomNameInputUtils.modifyRoomName(values.roomName); } + /** + * @param {Object} values + * @param {String} values.roomName + * @returns {Object} - An object containing the errors for each inputID + */ + validate(values) { + const errors = {}; + if (_.isEmpty(values.roomName)) { + errors.roomName = this.props.translate('common.error.fieldRequired'); + } + + return errors; + } + render() { return ( Navigation.getReportSettingsRoute(this.props.report.reportID)} + onBackButtonPress={() => Navigation.navigate(ROUTES.getReportSettingsRoute(this.props.report.reportID))} onCloseButtonPress={() => Navigation.dismissModal(true)} /> From a7f795d9239d4f5563c92ac4ac20f98a323d07ef Mon Sep 17 00:00:00 2001 From: Alberto Date: Mon, 10 Apr 2023 15:29:49 +0200 Subject: [PATCH 013/349] changing branch --- src/pages/settings/Report/ReportSettingsPage.js | 6 ++---- src/pages/settings/Report/RoomNamePage.js | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index a4307b6bd60..4bae3b2a64a 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -16,7 +16,6 @@ import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; import ScreenWrapper from '../../../components/ScreenWrapper'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import Text from '../../../components/Text'; -import RoomNameInput from '../../../components/RoomNameInput'; import Picker from '../../../components/Picker'; import * as ValidationUtils from '../../../libs/ValidationUtils'; import * as ErrorUtils from '../../../libs/ErrorUtils'; @@ -25,9 +24,8 @@ import reportPropTypes from '../../reportPropTypes'; import withReportOrNotFound from '../../home/report/withReportOrNotFound'; import Form from '../../../components/Form'; import FullPageNotFoundView from '../../../components/BlockingViews/FullPageNotFoundView'; -import RoomNamePage from "./RoomNamePage"; -import MenuItemWithTopDescription from "../../../components/MenuItemWithTopDescription"; -import ROUTES from "../../../ROUTES"; +import MenuItemWithTopDescription from '../../../components/MenuItemWithTopDescription'; +import ROUTES from '../../../ROUTES'; const propTypes = { /** Route params */ diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index 25c46593b80..30da0962c06 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -1,5 +1,5 @@ import React, {Component} from 'react'; -import PropTypes from 'prop-types'; +import _ from 'underscore'; import {View} from 'react-native'; import ScreenWrapper from '../../../components/ScreenWrapper'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; @@ -14,9 +14,7 @@ import compose from '../../../libs/compose'; import * as RoomNameInputUtils from '../../../libs/RoomNameInputUtils'; import withReportOrNotFound from '../../home/report/withReportOrNotFound'; import reportPropTypes from '../../reportPropTypes'; -import ROUTES from "../../../ROUTES"; -import * as ValidationUtils from "../../../libs/ValidationUtils"; -import _ from "underscore/underscore-node.mjs"; +import ROUTES from '../../../ROUTES'; const propTypes = { ...withLocalizePropTypes, From 362a51c7e374876e86dce8a0cc5475a51ee0c162 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 11 Apr 2023 10:35:45 +0200 Subject: [PATCH 014/349] Add prefix handling --- src/pages/settings/Report/RoomNamePage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index 30da0962c06..4c1a7d41828 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -75,8 +75,9 @@ class RoomNamePage extends Component { From a4d6f8bedebdcfe78374412c89697fc3243eb934 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 11 Apr 2023 10:55:44 +0200 Subject: [PATCH 015/349] save and return correctly for name --- src/pages/settings/Report/RoomNamePage.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index 4c1a7d41828..a97145f192d 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -15,6 +15,7 @@ import * as RoomNameInputUtils from '../../../libs/RoomNameInputUtils'; import withReportOrNotFound from '../../home/report/withReportOrNotFound'; import reportPropTypes from '../../reportPropTypes'; import ROUTES from '../../../ROUTES'; +import * as Report from '../../../libs/actions/Report'; const propTypes = { ...withLocalizePropTypes, @@ -37,7 +38,8 @@ class RoomNamePage extends Component { * @param {String} values.roomName */ updateRoomName(values) { - RoomNameInputUtils.modifyRoomName(values.roomName); + Report.updatePolicyRoomName(this.props.report, RoomNameInputUtils.modifyRoomName(values.roomName)); + Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(this.props.report.reportID)); } /** @@ -77,7 +79,7 @@ class RoomNamePage extends Component { name="name" prefixCharacter={CONST.POLICY.ROOM_PREFIX} label={this.props.translate('newRoomPage.roomName')} - defaultValue={this.props.report.reportName.substring(1)} // Since the room name always starts with a prefix, we omit the first character to avoid displaying it twice. + defaultValue={this.props.report.reportName.substring(1)} // Since the room name always starts with a prefix, we omit the first character. maxLength={CONST.REPORT.MAX_ROOM_NAME_LENGTH} /> From 7cc104cb44a822e52835b72b92eec78fe45f24a3 Mon Sep 17 00:00:00 2001 From: Alberto Date: Tue, 11 Apr 2023 11:40:54 +0200 Subject: [PATCH 016/349] Create NotificationPreferencesPage --- src/ONYXKEYS.js | 1 + src/languages/en.js | 1 + src/languages/es.js | 1 + .../Report/NotificationPreferencePage.js | 85 +++++++++++++++++++ src/pages/settings/Report/RoomNamePage.js | 2 +- 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/pages/settings/Report/NotificationPreferencePage.js diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js index 4f1a2062f09..9bd5c78fec5 100755 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -183,6 +183,7 @@ export default { PROFILE_SETTINGS_FORM: 'profileSettingsForm', DISPLAY_NAME_FORM: 'displayNameForm', ROOM_NAME_FORM: 'roomNameForm', + NOTIFICATION_PREFERENCES_FORM: 'notificationPreferencesForm', LEGAL_NAME_FORM: 'legalNameForm', DATE_OF_BIRTH_FORM: 'dateOfBirthForm', HOME_ADDRESS_FORM: 'homeAddressForm', diff --git a/src/languages/en.js b/src/languages/en.js index c1229dcf979..32ee284e8cb 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -321,6 +321,7 @@ export default { }, }, notificationPreferences: { + header: 'Notification Preferences', label: 'Notify me about new messages', immediately: 'Immediately', daily: 'Daily', diff --git a/src/languages/es.js b/src/languages/es.js index ef5fe94a360..def26e8633a 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -320,6 +320,7 @@ export default { }, }, notificationPreferences: { + header: 'Preferencias de Avisos', label: 'Avisar sobre nuevos mensajes', immediately: 'Inmediatamente', daily: 'Cada día', diff --git a/src/pages/settings/Report/NotificationPreferencePage.js b/src/pages/settings/Report/NotificationPreferencePage.js new file mode 100644 index 00000000000..27580387434 --- /dev/null +++ b/src/pages/settings/Report/NotificationPreferencePage.js @@ -0,0 +1,85 @@ +import React, {Component} from 'react'; +import _ from 'underscore'; +import {View} from 'react-native'; +import ScreenWrapper from '../../../components/ScreenWrapper'; +import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; +import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; +import Form from '../../../components/Form'; +import ONYXKEYS from '../../../ONYXKEYS'; +import CONST from '../../../CONST'; +import TextInput from '../../../components/TextInput'; +import styles from '../../../styles/styles'; +import Navigation from '../../../libs/Navigation/Navigation'; +import compose from '../../../libs/compose'; +import * as RoomNameInputUtils from '../../../libs/RoomNameInputUtils'; +import withReportOrNotFound from '../../home/report/withReportOrNotFound'; +import reportPropTypes from '../../reportPropTypes'; +import ROUTES from '../../../ROUTES'; +import * as Report from '../../../libs/actions/Report'; + +const propTypes = { + ...withLocalizePropTypes, + + /* Onyx Props */ + /** The active report */ + report: reportPropTypes.isRequired, +}; + +class NotificationPreferencePage extends Component { + constructor(props) { + super(props); + this.updateNotificationPreference = this.updateNotificationPreference.bind(this); + this.validate = this.validate.bind(this); + } + + /** + * Submit form to update room's name + * @param {Object} values + * @param {String} values.notificationPreference + */ + updateNotificationPreference(values) { + Report.updateNotificationPreference( + this.props.report.reportID, + this.props.report.notificationPreference, + values.notificationPreference, + ); + Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(this.props.report.reportID)); + } + + render() { + return ( + + Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(this.props.report.reportID))} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> + + + + + + + ); + } +} + +NotificationPreferencePage.propTypes = propTypes; + +export default compose( + withLocalize, + withReportOrNotFound, +)(NotificationPreferencePage); diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index a97145f192d..67f5a3f742d 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -62,7 +62,7 @@ class RoomNamePage extends Component { Navigation.navigate(ROUTES.getReportSettingsRoute(this.props.report.reportID))} + onBackButtonPress={() => Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(this.props.report.reportID))} onCloseButtonPress={() => Navigation.dismissModal(true)} />
Date: Tue, 11 Apr 2023 12:42:33 +0200 Subject: [PATCH 017/349] add route --- src/ROUTES.js | 2 ++ .../AppNavigator/ModalStackNavigators.js | 7 +++++++ src/libs/Navigation/linkingConfig.js | 3 +++ .../Report/NotificationPreferencePage.js | 19 +++++++++++++++++++ .../settings/Report/ReportSettingsPage.js | 5 +++++ 5 files changed, 36 insertions(+) diff --git a/src/ROUTES.js b/src/ROUTES.js index 35570607b1f..c74996a8b2e 100644 --- a/src/ROUTES.js +++ b/src/ROUTES.js @@ -103,8 +103,10 @@ export default { getReportDetailsRoute: reportID => `r/${reportID}/details`, REPORT_SETTINGS: 'r/:reportID/settings', REPORT_SETTINGS_ROOM_NAME: 'r/:reportID/settings/room-name', + REPORT_SETTINGS_NOTIFICATION_PREFERENCES: 'r/:reportID/settings/notification-preferences', getReportSettingsRoute: reportID => `r/${reportID}/settings`, getReportSettingsRoomNameRoute: reportID => `r/${reportID}/settings/room-name`, + getReportSettingsNotificationPreferencesRoute: reportID => `r/${reportID}/settings/notification-preferences`, TRANSITION_FROM_OLD_DOT: 'transition', VALIDATE_LOGIN: 'v/:accountID/:validateCode', GET_ASSISTANCE: 'get-assistance/:taskID', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 8b0ac182565..7f130ebdebe 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -160,6 +160,13 @@ const ReportSettingsModalStackNavigator = createModalStackNavigator([{ }, name: 'Report_Settings_Room_Name', }, +{ + getComponent: () => { + const NotificationPreferencesPage = require('../../../pages/settings/Report/NotificationPreferencePage').default; + return NotificationPreferencesPage; + }, + name: 'Report_Settings_Room_Name', +}, ]); const ReportParticipantsModalStackNavigator = createModalStackNavigator([ diff --git a/src/libs/Navigation/linkingConfig.js b/src/libs/Navigation/linkingConfig.js index 50e4f61472e..09c3b5d1125 100644 --- a/src/libs/Navigation/linkingConfig.js +++ b/src/libs/Navigation/linkingConfig.js @@ -198,6 +198,9 @@ export default { Report_Settings_Room_Name: { path: ROUTES.REPORT_SETTINGS_ROOM_NAME, }, + Report_Settings_Notification_Preferences: { + path: ROUTES.REPORT_SETTINGS_NOTIFICATION_PREFERENCES, + }, }, }, NewGroup: { diff --git a/src/pages/settings/Report/NotificationPreferencePage.js b/src/pages/settings/Report/NotificationPreferencePage.js index 27580387434..e99fb32c5b6 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.js +++ b/src/pages/settings/Report/NotificationPreferencePage.js @@ -16,6 +16,7 @@ import withReportOrNotFound from '../../home/report/withReportOrNotFound'; import reportPropTypes from '../../reportPropTypes'; import ROUTES from '../../../ROUTES'; import * as Report from '../../../libs/actions/Report'; +import OptionsSelector from "../../../components/OptionsSelector"; const propTypes = { ...withLocalizePropTypes, @@ -29,6 +30,7 @@ class NotificationPreferencePage extends Component { constructor(props) { super(props); this.updateNotificationPreference = this.updateNotificationPreference.bind(this); + this.getNotificationPreferenceOptions = this.getNotificationPreferenceOptions.bind(this); this.validate = this.validate.bind(this); } @@ -46,6 +48,14 @@ class NotificationPreferencePage extends Component { Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(this.props.report.reportID)); } + getNotificationPreferenceOptions() { + return [ + {value: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, label: this.props.translate('notificationPreferences.immediately')}, + {value: CONST.REPORT.NOTIFICATION_PREFERENCE.DAILY, label: this.props.translate('notificationPreferences.daily')}, + {value: CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE, label: this.props.translate('notificationPreferences.mute')}, + ]; + } + render() { return ( @@ -64,6 +74,15 @@ class NotificationPreferencePage extends Component { enabledWhenOffline > + Navigation.navigate(ROUTES.getReportSettingsRoomNameRoute(this.props.report.reportID))} /> + Navigation.navigate(ROUTES.getReportSettingsRoomNameRoute(this.props.report.reportID))} + /> Date: Wed, 12 Apr 2023 12:30:02 +0200 Subject: [PATCH 018/349] changing branch --- .../AppNavigator/ModalStackNavigators.js | 2 +- .../Report/NotificationPreferencePage.js | 37 +++++-------------- .../settings/Report/ReportSettingsPage.js | 2 +- 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js index 7f130ebdebe..ec314427b4f 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.js +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.js @@ -165,7 +165,7 @@ const ReportSettingsModalStackNavigator = createModalStackNavigator([{ const NotificationPreferencesPage = require('../../../pages/settings/Report/NotificationPreferencePage').default; return NotificationPreferencesPage; }, - name: 'Report_Settings_Room_Name', + name: 'Report_Settings_Notification_Preferences', }, ]); diff --git a/src/pages/settings/Report/NotificationPreferencePage.js b/src/pages/settings/Report/NotificationPreferencePage.js index e99fb32c5b6..2d5ad5864f3 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.js +++ b/src/pages/settings/Report/NotificationPreferencePage.js @@ -11,12 +11,11 @@ import TextInput from '../../../components/TextInput'; import styles from '../../../styles/styles'; import Navigation from '../../../libs/Navigation/Navigation'; import compose from '../../../libs/compose'; -import * as RoomNameInputUtils from '../../../libs/RoomNameInputUtils'; import withReportOrNotFound from '../../home/report/withReportOrNotFound'; import reportPropTypes from '../../reportPropTypes'; import ROUTES from '../../../ROUTES'; import * as Report from '../../../libs/actions/Report'; -import OptionsSelector from "../../../components/OptionsSelector"; +import OptionsSelector from '../../../components/OptionsSelector'; const propTypes = { ...withLocalizePropTypes, @@ -31,7 +30,14 @@ class NotificationPreferencePage extends Component { super(props); this.updateNotificationPreference = this.updateNotificationPreference.bind(this); this.getNotificationPreferenceOptions = this.getNotificationPreferenceOptions.bind(this); - this.validate = this.validate.bind(this); + } + + getNotificationPreferenceOptions() { + return [ + this.props.translate('notificationPreferences.immediately'), + this.props.translate('notificationPreferences.daily'), + this.props.translate('notificationPreferences.mute'), + ]; } /** @@ -48,14 +54,6 @@ class NotificationPreferencePage extends Component { Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(this.props.report.reportID)); } - getNotificationPreferenceOptions() { - return [ - {value: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, label: this.props.translate('notificationPreferences.immediately')}, - {value: CONST.REPORT.NOTIFICATION_PREFERENCE.DAILY, label: this.props.translate('notificationPreferences.daily')}, - {value: CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE, label: this.props.translate('notificationPreferences.mute')}, - ]; - } - render() { return ( @@ -65,32 +63,17 @@ class NotificationPreferencePage extends Component { onBackButtonPress={() => Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(this.props.report.reportID))} onCloseButtonPress={() => Navigation.dismissModal(true)} /> - + - - ); } diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index 8551fe777ae..8b35e604e15 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -166,7 +166,7 @@ class ReportSettingsPage extends Component { Navigation.navigate(ROUTES.getReportSettingsRoomNameRoute(this.props.report.reportID))} + onPress={() => Navigation.navigate(ROUTES.getReportSettingsNotificationPreferencesRoute(this.props.report.reportID))} />
Date: Wed, 12 Apr 2023 16:51:16 +0300 Subject: [PATCH 019/349] Revert "Fix/large images android" --- android/app/src/main/AndroidManifest.xml | 2 - package-lock.json | 8 +- package.json | 2 +- patches/react-native-fast-image+8.6.3.patch | 300 ++++++++++++++++++++ src/components/ImageView/index.native.js | 1 - 5 files changed, 303 insertions(+), 10 deletions(-) create mode 100644 patches/react-native-fast-image+8.6.3.patch diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 07214d27c47..514f471bf8e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -10,8 +10,6 @@ { ++ ++ @Override ++ public boolean handles(@NonNull InputStream source, @NonNull Options options) throws IOException { ++ return true; ++ } ++ ++ @Nullable ++ @Override ++ public Resource decode(@NonNull InputStream source, int width, int height, @NonNull Options options) throws IOException { ++ BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); ++ bitmapOptions.inJustDecodeBounds = true; ++ BitmapFactory.decodeStream(source, null, bitmapOptions); ++ ++ // BitmapFactory#decodeStream leaves stream's position where ever it was after reading the encoded data ++ // https://developer.android.com/reference/android/graphics/BitmapFactory#decodeStream(java.io.InputStream) ++ // so we need to rewind the stream to be able to read image header with exif values ++ source.reset(); ++ ++ int orientation = new ExifInterface(source).getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED); ++ if (orientation == ExifInterface.ORIENTATION_ROTATE_90 || orientation == ExifInterface.ORIENTATION_ROTATE_270) { ++ int tmpWidth = bitmapOptions.outWidth; ++ bitmapOptions.outWidth = bitmapOptions.outHeight; ++ bitmapOptions.outHeight = tmpWidth; ++ } ++ return new SimpleResource(bitmapOptions); ++ } ++} +\ No newline at end of file +diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/BitmapSizeTranscoder.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/BitmapSizeTranscoder.java +new file mode 100644 +index 0000000..7d208d1 +--- /dev/null ++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/BitmapSizeTranscoder.java +@@ -0,0 +1,23 @@ ++package com.dylanvann.fastimage; ++ ++import android.graphics.BitmapFactory; ++ ++import androidx.annotation.NonNull; ++import androidx.annotation.Nullable; ++ ++import com.bumptech.glide.load.Options; ++import com.bumptech.glide.load.engine.Resource; ++import com.bumptech.glide.load.resource.SimpleResource; ++import com.bumptech.glide.load.resource.transcode.ResourceTranscoder; ++ ++public class BitmapSizeTranscoder implements ResourceTranscoder { ++ @Nullable ++ @Override ++ public Resource transcode(@NonNull Resource toTranscode, @NonNull Options options) { ++ BitmapFactory.Options bitmap = toTranscode.get(); ++ Size size = new Size(); ++ size.width = bitmap.outWidth; ++ size.height = bitmap.outHeight; ++ return new SimpleResource(size); ++ } ++} +\ No newline at end of file +diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java +index 811292a..f60b87c 100644 +--- a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java ++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java +@@ -2,6 +2,7 @@ package com.dylanvann.fastimage; + + import android.content.Context; + import androidx.annotation.NonNull; ++import android.graphics.BitmapFactory; + + import com.bumptech.glide.Glide; + import com.bumptech.glide.Registry; +@@ -47,6 +48,9 @@ public class FastImageOkHttpProgressGlideModule extends LibraryGlideModule { + .build(); + OkHttpUrlLoader.Factory factory = new OkHttpUrlLoader.Factory(client); + registry.replace(GlideUrl.class, InputStream.class, factory); ++ // Decoder + Transcoder pair for InputStream -> Size ++ registry.prepend(InputStream.class, BitmapFactory.Options.class, new BitmapSizeDecoder()); ++ registry.register(BitmapFactory.Options.class, Size.class, new BitmapSizeTranscoder()); + } + + private static Interceptor createInterceptor(final ResponseProgressListener listener) { +diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java +index dbeb813..bf8f21c 100644 +--- a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java ++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageRequestListener.java +@@ -22,13 +22,6 @@ public class FastImageRequestListener implements RequestListener { + this.key = key; + } + +- private static WritableMap mapFromResource(Drawable resource) { +- WritableMap resourceData = new WritableNativeMap(); +- resourceData.putInt("width", resource.getIntrinsicWidth()); +- resourceData.putInt("height", resource.getIntrinsicHeight()); +- return resourceData; +- } +- + @Override + public boolean onLoadFailed(@androidx.annotation.Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + FastImageOkHttpProgressGlideModule.forget(key); +@@ -53,7 +46,6 @@ public class FastImageRequestListener implements RequestListener { + ThemedReactContext context = (ThemedReactContext) view.getContext(); + RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class); + int viewId = view.getId(); +- eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_EVENT, mapFromResource(resource)); + eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap()); + return false; + } +diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +index 34fcf89..1339f5c 100644 +--- a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java ++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +@@ -2,6 +2,7 @@ package com.dylanvann.fastimage; + + import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_ERROR_EVENT; + ++import androidx.annotation.NonNull; + import android.annotation.SuppressLint; + import android.content.Context; + import android.graphics.drawable.Drawable; +@@ -9,16 +10,24 @@ import android.graphics.drawable.Drawable; + import androidx.annotation.Nullable; + import androidx.appcompat.widget.AppCompatImageView; + ++import com.bumptech.glide.Glide; + import com.bumptech.glide.RequestBuilder; + import com.bumptech.glide.RequestManager; ++import com.bumptech.glide.load.DataSource; ++import com.bumptech.glide.load.engine.GlideException; + import com.bumptech.glide.load.model.GlideUrl; + import com.bumptech.glide.request.Request; ++import com.bumptech.glide.request.RequestListener; ++import com.bumptech.glide.request.target.SimpleTarget; ++import com.bumptech.glide.request.target.Target; ++import com.bumptech.glide.request.transition.Transition; + import com.facebook.react.bridge.ReadableMap; + import com.facebook.react.bridge.WritableMap; + import com.facebook.react.bridge.WritableNativeMap; + import com.facebook.react.uimanager.ThemedReactContext; + import com.facebook.react.uimanager.events.RCTEventEmitter; + ++import java.io.File; + import java.util.ArrayList; + import java.util.Collections; + import java.util.List; +@@ -124,9 +133,34 @@ class FastImageViewWithUrl extends AppCompatImageView { + RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class); + int viewId = this.getId(); + +- eventEmitter.receiveEvent(viewId, +- FastImageViewManager.REACT_ON_LOAD_START_EVENT, +- new WritableNativeMap()); ++ // Request the URL from cache to see if it exists there and if so pass the cache ++ // path as an argument in the onLoadStart event ++ requestManager ++ .asFile() ++ .load(glideUrl) ++ .onlyRetrieveFromCache(true) ++ .listener(new RequestListener() { ++ @Override ++ public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { ++ WritableNativeMap result = new WritableNativeMap(); ++ result.putNull("cachePath"); ++ eventEmitter.receiveEvent(viewId, ++ FastImageViewManager.REACT_ON_LOAD_START_EVENT, ++ result); ++ return false; ++ } ++ ++ @Override ++ public boolean onResourceReady(File resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { ++ WritableNativeMap result = new WritableNativeMap(); ++ result.putString("cachePath", resource.getAbsolutePath()); ++ eventEmitter.receiveEvent(viewId, ++ FastImageViewManager.REACT_ON_LOAD_START_EVENT, ++ result); ++ return false; ++ } ++ }) ++ .submit(); + } + + if (requestManager != null) { +@@ -148,6 +182,25 @@ class FastImageViewWithUrl extends AppCompatImageView { + builder.listener(new FastImageRequestListener(key)); + + builder.into(this); ++ ++ // Used specifically to handle the `onLoad` event for the image ++ RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class); ++ int viewId = this.getId(); ++ requestManager ++ .as(Size.class) ++ .load(imageSource == null ? null : imageSource.getSourceForLoad()) ++ .into(new SimpleTarget() { ++ @Override ++ public void onResourceReady(@NonNull Size resource, @Nullable Transition transition) { ++ WritableMap resourceData = new WritableNativeMap(); ++ resourceData.putInt("width", resource.width); ++ resourceData.putInt("height", resource.height); ++ eventEmitter.receiveEvent(viewId, ++ "onFastImageLoad", ++ resourceData ++ ); ++ } ++ }); + } + } + +diff --git a/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/Size.java b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/Size.java +new file mode 100644 +index 0000000..2fe8a47 +--- /dev/null ++++ b/node_modules/react-native-fast-image/android/src/main/java/com/dylanvann/fastimage/Size.java +@@ -0,0 +1,6 @@ ++package com.dylanvann.fastimage; ++ ++public class Size { ++ int width; ++ int height; ++} +\ No newline at end of file +diff --git a/node_modules/react-native-fast-image/dist/index.d.ts b/node_modules/react-native-fast-image/dist/index.d.ts +index 5abb7c9..a2672c6 100644 +--- a/node_modules/react-native-fast-image/dist/index.d.ts ++++ b/node_modules/react-native-fast-image/dist/index.d.ts +@@ -27,6 +27,11 @@ export declare type Source = { + priority?: Priority; + cache?: Cache; + }; ++export interface OnLoadStartEvent { ++ nativeEvent: { ++ cachePath: string | null; ++ }; ++} + export interface OnLoadEvent { + nativeEvent: { + width: number; +@@ -57,7 +62,7 @@ export interface FastImageProps extends AccessibilityProps, ViewProps { + defaultSource?: ImageRequireSource; + resizeMode?: ResizeMode; + fallback?: boolean; +- onLoadStart?(): void; ++ onLoadStart?(event: OnLoadStartEvent): void; + onProgress?(event: OnProgressEvent): void; + onLoad?(event: OnLoadEvent): void; + onError?(): void; +diff --git a/node_modules/react-native-fast-image/ios/FastImage/FFFastImageView.m b/node_modules/react-native-fast-image/ios/FastImage/FFFastImageView.m +index f710081..391ef92 100644 +--- a/node_modules/react-native-fast-image/ios/FastImage/FFFastImageView.m ++++ b/node_modules/react-native-fast-image/ios/FastImage/FFFastImageView.m +@@ -54,7 +54,6 @@ - (void) setOnFastImageError: (RCTDirectEventBlock)onFastImageError { + - (void) setOnFastImageLoadStart: (RCTDirectEventBlock)onFastImageLoadStart { + if (_source && !self.hasSentOnLoadStart) { + _onFastImageLoadStart = onFastImageLoadStart; +- onFastImageLoadStart(@{}); + self.hasSentOnLoadStart = YES; + } else { + _onFastImageLoadStart = onFastImageLoadStart; +@@ -188,7 +187,18 @@ - (void) reloadImage { + } + + if (self.onFastImageLoadStart) { +- self.onFastImageLoadStart(@{}); ++ NSString* cachePath = [[SDImageCache sharedImageCache] cachePathForKey:url]; ++ BOOL isCached = [[SDImageCache sharedImageCache] diskImageDataExistsWithKey:url]; ++ if (isCached) { ++ self.onFastImageLoadStart(@{ ++ @"cachePath": cachePath ++ }); ++ } ++ else { ++ self.onFastImageLoadStart(@{ ++ @"cachePath": [NSNull null] ++ }); ++ } + self.hasSentOnLoadStart = YES; + } else { + self.hasSentOnLoadStart = NO; diff --git a/src/components/ImageView/index.native.js b/src/components/ImageView/index.native.js index 508187af2a8..11732715b94 100644 --- a/src/components/ImageView/index.native.js +++ b/src/components/ImageView/index.native.js @@ -221,7 +221,6 @@ class ImageView extends PureComponent { // due to ImageZoom shouldShowLoadingIndicator ? styles.opacity0 : styles.opacity1, ]} - disableTransformation source={{uri: this.props.url}} isAuthTokenRequired={this.props.isAuthTokenRequired} resizeMode={Image.resizeMode.contain} From d211ac545a6c3f35cb9ff954c2b8a06249d18a40 Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Thu, 13 Apr 2023 22:38:43 +0000 Subject: [PATCH 020/349] Sort participants in report participants page --- src/pages/ReportParticipantsPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index 34189f347ef..8dc40db0398 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -57,7 +57,7 @@ const defaultProps = { const getAllParticipants = (report, personalDetails) => { const {participants} = report; - return _.map(participants, (login) => { + return _.chain(participants).sort().map((login) => { const userLogin = Str.removeSMSDomain(login); const userPersonalDetail = lodashGet(personalDetails, login, {displayName: userLogin, avatar: ''}); @@ -74,7 +74,7 @@ const getAllParticipants = (report, personalDetails) => { text: userPersonalDetail.displayName, tooltipText: userLogin, participantsList: [{login, displayName: userPersonalDetail.displayName}], - }); + }).value(); }); }; From d8eeb4910de4c543c10672ba2095e563689a4e92 Mon Sep 17 00:00:00 2001 From: Alberto Date: Sat, 15 Apr 2023 16:36:25 +0200 Subject: [PATCH 021/349] add icons --- .../Report/NotificationPreferencePage.js | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/pages/settings/Report/NotificationPreferencePage.js b/src/pages/settings/Report/NotificationPreferencePage.js index 2d5ad5864f3..8465b9aee8b 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.js +++ b/src/pages/settings/Report/NotificationPreferencePage.js @@ -16,6 +16,8 @@ import reportPropTypes from '../../reportPropTypes'; import ROUTES from '../../../ROUTES'; import * as Report from '../../../libs/actions/Report'; import OptionsSelector from '../../../components/OptionsSelector'; +import * as Expensicons from "../../../components/Icon/Expensicons"; +import themeColors from "../../../styles/themes/default"; const propTypes = { ...withLocalizePropTypes, @@ -24,20 +26,41 @@ const propTypes = { /** The active report */ report: reportPropTypes.isRequired, }; +const greenCheckmark = {src: Expensicons.Checkmark, color: themeColors.success}; class NotificationPreferencePage extends Component { constructor(props) { super(props); this.updateNotificationPreference = this.updateNotificationPreference.bind(this); this.getNotificationPreferenceOptions = this.getNotificationPreferenceOptions.bind(this); + this.getPreferenceOption = this.getPreferenceOption.bind(this); } getNotificationPreferenceOptions() { - return [ + const frequencies = [ this.props.translate('notificationPreferences.immediately'), this.props.translate('notificationPreferences.daily'), this.props.translate('notificationPreferences.mute'), ]; + return _.map(frequencies, frequency => this.getPreferenceOption(frequency)); + } + + /** + * Get timezone option object for the list. + * @param {String} text + * @return {Object} Timezone list option + */ + getPreferenceOption(text) { + return { + text, + keyForList: text, + + // Include the green checkmark icon to indicate the currently selected value + customIcon: text === this.props.report.notificationPreference ? greenCheckmark : undefined, + + // This property will make the currently selected value have bold text + boldStyle: text === this.props.report.notificationPreference, + }; } /** From 77b9bcebeec0485473987eb1a3a582f4df8e78e7 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 17 Apr 2023 18:57:26 -0700 Subject: [PATCH 022/349] Remove form, push to page for report settings --- .../settings/Report/ReportSettingsPage.js | 97 ++++++------------- 1 file changed, 31 insertions(+), 66 deletions(-) diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index 8b35e604e15..3a970bab9f4 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -61,7 +61,6 @@ const defaultProps = { reports: {}, }; - class ReportSettingsPage extends Component { constructor(props) { super(props); @@ -159,76 +158,42 @@ class ReportSettingsPage extends Component { onCloseButtonPress={Navigation.dismissModal} /> Navigation.navigate(ROUTES.getReportSettingsRoomNameRoute(this.props.report.reportID))} - /> - Navigation.navigate(ROUTES.getReportSettingsNotificationPreferencesRoute(this.props.report.reportID))} /> - !shouldDisableRename && this.updatePolicyRoomName(values)} - scrollContextEnabled - isSubmitButtonVisible={shouldShowRoomName && !shouldDisableRename} - enabledWhenOffline - > - - - { - if (this.props.report.notificationPreference === notificationPreference) { - return; - } - - Report.updateNotificationPreference( - this.props.report.reportID, - this.props.report.notificationPreference, - notificationPreference, - ); - }} - items={this.getNotificationPreferenceOptions()} - value={this.props.report.notificationPreference} - /> - - - {shouldShowRoomName && ( - - Report.clearPolicyRoomNameErrors(this.props.report.reportID)} - > - - - {shouldDisableRename ? ( - - - {this.props.translate('newRoomPage.roomName')} - - - {this.props.report.reportName} - - - ) - : ( - Navigation.navigate(ROUTES.getReportSettingsRoute(this.props.report.reportID))} - /> - )} + {shouldShowRoomName && ( + Report.clearPolicyRoomNameErrors(this.props.report.reportID)} + > + + + {shouldDisableRename ? ( + + + {this.props.translate('newRoomPage.roomName')} + + + {this.props.report.reportName} + - - + ) + : ( + Navigation.navigate(ROUTES.getReportSettingsRoomNameRoute(this.props.report.reportID))} + /> + )} + - )} + + )} + {linkedWorkspace && ( @@ -256,7 +221,7 @@ class ReportSettingsPage extends Component { )} - +
); From 0773f90c271927854c5a37527195608b8871b2ed Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 17 Apr 2023 19:02:57 -0700 Subject: [PATCH 023/349] Remove unused code after refactor --- .../settings/Report/ReportSettingsPage.js | 69 +------------------ 1 file changed, 1 insertion(+), 68 deletions(-) diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index 3a970bab9f4..605d72e385c 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -1,6 +1,6 @@ import React, {Component} 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 _ from 'underscore'; import lodashGet from 'lodash/get'; @@ -16,13 +16,9 @@ import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; import ScreenWrapper from '../../../components/ScreenWrapper'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import Text from '../../../components/Text'; -import Picker from '../../../components/Picker'; -import * as ValidationUtils from '../../../libs/ValidationUtils'; -import * as ErrorUtils from '../../../libs/ErrorUtils'; import OfflineWithFeedback from '../../../components/OfflineWithFeedback'; import reportPropTypes from '../../reportPropTypes'; import withReportOrNotFound from '../../home/report/withReportOrNotFound'; -import Form from '../../../components/Form'; import FullPageNotFoundView from '../../../components/BlockingViews/FullPageNotFoundView'; import MenuItemWithTopDescription from '../../../components/MenuItemWithTopDescription'; import ROUTES from '../../../ROUTES'; @@ -51,31 +47,13 @@ const propTypes = { /** ID of the policy */ id: PropTypes.string, }), - - /** All reports shared with the user */ - reports: PropTypes.objectOf(reportPropTypes), }; const defaultProps = { policies: {}, - reports: {}, }; class ReportSettingsPage extends Component { - constructor(props) { - super(props); - - this.validate = this.validate.bind(this); - } - - getNotificationPreferenceOptions() { - return [ - {value: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS, label: this.props.translate('notificationPreferences.immediately')}, - {value: CONST.REPORT.NOTIFICATION_PREFERENCE.DAILY, label: this.props.translate('notificationPreferences.daily')}, - {value: CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE, label: this.props.translate('notificationPreferences.mute')}, - ]; - } - /** * @param {Object|null} linkedWorkspace - the workspace the report is on, null if the user isn't a member of the workspace * @returns {Boolean} @@ -101,48 +79,6 @@ class ReportSettingsPage extends Component { return !Policy.isPolicyOwner(linkedWorkspace) && linkedWorkspace.role !== CONST.POLICY.ROLE.ADMIN; } - /** - * @param {Object} values - form input values passed by the Form component - */ - updatePolicyRoomName(values) { - Keyboard.dismiss(); - - // When the room name has not changed, skip the Form submission - if (values.newRoomName === this.props.report.reportName) { - return; - } - Report.updatePolicyRoomName(this.props.report, values.newRoomName); - } - - /** - * @param {Object} values - form input values passed by the Form component - * @returns {Boolean} - */ - validate(values) { - const errors = {}; - - // We should skip validation hence we return an empty errors and we skip Form submission on the onSubmit method - if (values.newRoomName === this.props.report.reportName) { - return errors; - } - - if (!values.newRoomName || values.newRoomName === CONST.POLICY.ROOM_PREFIX) { - // We error if the user doesn't enter a room name or left blank - ErrorUtils.addErrorMessage(errors, 'newRoomName', this.props.translate('newRoomPage.pleaseEnterRoomName')); - } else if (values.newRoomName !== CONST.POLICY.ROOM_PREFIX && !ValidationUtils.isValidRoomName(values.newRoomName)) { - // We error if the room name has invalid characters - ErrorUtils.addErrorMessage(errors, 'newRoomName', this.props.translate('newRoomPage.roomNameInvalidError')); - } else if (ValidationUtils.isReservedRoomName(values.newRoomName)) { - // Certain names are reserved for default rooms and should not be used for policy rooms. - ErrorUtils.addErrorMessage(errors, 'newRoomName', this.props.translate('newRoomPage.roomNameReservedError', {reservedName: values.newRoomName})); - } else if (ValidationUtils.isExistingRoomName(values.newRoomName, this.props.reports, this.props.report.policyID)) { - // Certain names are reserved for default rooms and should not be used for policy rooms. - ErrorUtils.addErrorMessage(errors, 'newRoomName', this.props.translate('newRoomPage.roomAlreadyExistsError')); - } - - return errors; - } - render() { const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(this.props.report); const linkedWorkspace = _.find(this.props.policies, policy => policy && policy.id === this.props.report.policyID); @@ -237,8 +173,5 @@ export default compose( policies: { key: ONYXKEYS.COLLECTION.POLICY, }, - reports: { - key: ONYXKEYS.COLLECTION.REPORT, - }, }), )(ReportSettingsPage); From f31f1247558b89caa007e5833b531ff712c192d6 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 17 Apr 2023 19:08:35 -0700 Subject: [PATCH 024/349] Translate and capitalize notification preference --- src/pages/settings/Report/ReportSettingsPage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index 605d72e385c..c834be82b8d 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -83,6 +83,7 @@ class ReportSettingsPage extends Component { const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(this.props.report); const linkedWorkspace = _.find(this.props.policies, policy => policy && policy.id === this.props.report.policyID); const shouldDisableRename = this.shouldDisableRename(linkedWorkspace); + const notificationPreference = this.props.translate(`notificationPreferences.${this.props.report.notificationPreference}`); return ( @@ -95,7 +96,7 @@ class ReportSettingsPage extends Component { /> Navigation.navigate(ROUTES.getReportSettingsNotificationPreferencesRoute(this.props.report.reportID))} /> From 223d1fa388b5a3596e5509384aa49cd44bd657d6 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 25 Apr 2023 17:38:07 -0700 Subject: [PATCH 025/349] Refactor notif pref page to functional component --- .../Report/NotificationPreferencePage.js | 124 +++++++----------- 1 file changed, 45 insertions(+), 79 deletions(-) diff --git a/src/pages/settings/Report/NotificationPreferencePage.js b/src/pages/settings/Report/NotificationPreferencePage.js index 8465b9aee8b..6420ec2a35c 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.js +++ b/src/pages/settings/Report/NotificationPreferencePage.js @@ -1,23 +1,18 @@ -import React, {Component} from 'react'; +import React from 'react'; import _ from 'underscore'; -import {View} from 'react-native'; import ScreenWrapper from '../../../components/ScreenWrapper'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; -import Form from '../../../components/Form'; -import ONYXKEYS from '../../../ONYXKEYS'; -import CONST from '../../../CONST'; -import TextInput from '../../../components/TextInput'; import styles from '../../../styles/styles'; +import OptionsList from '../../../components/OptionsList'; import Navigation from '../../../libs/Navigation/Navigation'; import compose from '../../../libs/compose'; import withReportOrNotFound from '../../home/report/withReportOrNotFound'; import reportPropTypes from '../../reportPropTypes'; import ROUTES from '../../../ROUTES'; import * as Report from '../../../libs/actions/Report'; -import OptionsSelector from '../../../components/OptionsSelector'; -import * as Expensicons from "../../../components/Icon/Expensicons"; -import themeColors from "../../../styles/themes/default"; +import * as Expensicons from '../../../components/Icon/Expensicons'; +import themeColors from '../../../styles/themes/default'; const propTypes = { ...withLocalizePropTypes, @@ -28,80 +23,51 @@ const propTypes = { }; const greenCheckmark = {src: Expensicons.Checkmark, color: themeColors.success}; -class NotificationPreferencePage extends Component { - constructor(props) { - super(props); - this.updateNotificationPreference = this.updateNotificationPreference.bind(this); - this.getNotificationPreferenceOptions = this.getNotificationPreferenceOptions.bind(this); - this.getPreferenceOption = this.getPreferenceOption.bind(this); - } +const NotificationPreferencePage = (props) => { + const frequencies = [ + props.translate('notificationPreferences.immediately'), + props.translate('notificationPreferences.daily'), + props.translate('notificationPreferences.mute'), + ]; + const notificationPreferenceOptions = _.map(frequencies, frequency => ({ + text: frequency, + keyForList: frequency, - getNotificationPreferenceOptions() { - const frequencies = [ - this.props.translate('notificationPreferences.immediately'), - this.props.translate('notificationPreferences.daily'), - this.props.translate('notificationPreferences.mute'), - ]; - return _.map(frequencies, frequency => this.getPreferenceOption(frequency)); - } + // Include the green checkmark icon to indicate the currently selected value + customIcon: frequency === props.report.notificationPreference ? greenCheckmark : undefined, - /** - * Get timezone option object for the list. - * @param {String} text - * @return {Object} Timezone list option - */ - getPreferenceOption(text) { - return { - text, - keyForList: text, + // This property will make the currently selected value have bold text + boldStyle: frequency === props.report.notificationPreference, + })); - // Include the green checkmark icon to indicate the currently selected value - customIcon: text === this.props.report.notificationPreference ? greenCheckmark : undefined, - - // This property will make the currently selected value have bold text - boldStyle: text === this.props.report.notificationPreference, - }; - } - - /** - * Submit form to update room's name - * @param {Object} values - * @param {String} values.notificationPreference - */ - updateNotificationPreference(values) { - Report.updateNotificationPreference( - this.props.report.reportID, - this.props.report.notificationPreference, - values.notificationPreference, - ); - Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(this.props.report.reportID)); - } - - render() { - return ( - - Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(this.props.report.reportID))} - onCloseButtonPress={() => Navigation.dismissModal(true)} - /> - - - - - - ); - } -} + return ( + + Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(props.report.reportID))} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> + Report.updateNotificationPreference(props.reportID, props.report.notificationPreference, notificationPreference)} + hideSectionHeaders + optionHoveredStyle={ + { + ...styles.hoveredComponentBG, + ...styles.mhn5, + ...styles.ph5, + } + } + shouldHaveOptionSeparator + shouldDisableRowInnerPadding + contentContainerStyles={[styles.ph5]} + /> + + ); +}; +NotificationPreferencePage.displayName = 'NotificationPreferencePage'; NotificationPreferencePage.propTypes = propTypes; export default compose( From bd116ee37495d524eac1fcc776b32cf9c12c0323 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 25 Apr 2023 17:56:05 -0700 Subject: [PATCH 026/349] Store notificationPreferences separately for page --- src/languages/en.js | 10 ++++--- src/languages/es.js | 10 ++++--- .../Report/NotificationPreferencePage.js | 27 +++++++++---------- .../settings/Report/ReportSettingsPage.js | 4 +-- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 32ee284e8cb..081ffaca69b 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -320,12 +320,14 @@ export default { genericCancelFailureMessage: ({type}) => `Unexpected error ${type === 'decline' ? 'declining' : 'cancelling'} the money request, please try again later`, }, }, - notificationPreferences: { + notificationPreferencesPage: { header: 'Notification Preferences', label: 'Notify me about new messages', - immediately: 'Immediately', - daily: 'Daily', - mute: 'Mute', + notificationPreferences: { + immediately: 'Immediately', + daily: 'Daily', + mute: 'Mute', + }, }, loginField: { numberHasNotBeenValidated: 'The number has not yet been validated. Click the button to resend the validation link via text.', diff --git a/src/languages/es.js b/src/languages/es.js index def26e8633a..bf3b78ad213 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -319,12 +319,14 @@ export default { genericCancelFailureMessage: ({type}) => `Error inesperado al ${type === 'decline' ? 'rechazar' : 'cancelar'} la solicitud de dinero, por favor inténtalo más tarde`, }, }, - notificationPreferences: { + notificationPreferencesPage: { header: 'Preferencias de Avisos', label: 'Avisar sobre nuevos mensajes', - immediately: 'Inmediatamente', - daily: 'Cada día', - mute: 'Nunca', + notificationPreferences: { + immediately: 'Inmediatamente', + daily: 'Cada día', + mute: 'Nunca', + }, }, loginField: { numberHasNotBeenValidated: 'El número no está validado todavía. Haz click en el botón para reenviar el enlace de confirmación via SMS.', diff --git a/src/pages/settings/Report/NotificationPreferencePage.js b/src/pages/settings/Report/NotificationPreferencePage.js index 6420ec2a35c..12b1a1b5235 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.js +++ b/src/pages/settings/Report/NotificationPreferencePage.js @@ -24,26 +24,25 @@ const propTypes = { const greenCheckmark = {src: Expensicons.Checkmark, color: themeColors.success}; const NotificationPreferencePage = (props) => { - const frequencies = [ - props.translate('notificationPreferences.immediately'), - props.translate('notificationPreferences.daily'), - props.translate('notificationPreferences.mute'), - ]; - const notificationPreferenceOptions = _.map(frequencies, frequency => ({ - text: frequency, - keyForList: frequency, + const notificationPreferenceOptions = _.map(props.translate('notificationPreferencesPage.notificationPreferences'), + (preference, key) => ( + { + value: key, + text: preference, + keyForList: key, - // Include the green checkmark icon to indicate the currently selected value - customIcon: frequency === props.report.notificationPreference ? greenCheckmark : undefined, + // Include the green checkmark icon to indicate the currently selected value + customIcon: key === props.report.notificationPreference ? greenCheckmark : null, - // This property will make the currently selected value have bold text - boldStyle: frequency === props.report.notificationPreference, - })); + // This property will make the currently selected value have bold text + boldStyle: key === props.report.notificationPreference, + } + )); return ( Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(props.report.reportID))} onCloseButtonPress={() => Navigation.dismissModal(true)} diff --git a/src/pages/settings/Report/ReportSettingsPage.js b/src/pages/settings/Report/ReportSettingsPage.js index c834be82b8d..dedaab858f9 100644 --- a/src/pages/settings/Report/ReportSettingsPage.js +++ b/src/pages/settings/Report/ReportSettingsPage.js @@ -83,7 +83,7 @@ class ReportSettingsPage extends Component { const shouldShowRoomName = !ReportUtils.isPolicyExpenseChat(this.props.report); const linkedWorkspace = _.find(this.props.policies, policy => policy && policy.id === this.props.report.policyID); const shouldDisableRename = this.shouldDisableRename(linkedWorkspace); - const notificationPreference = this.props.translate(`notificationPreferences.${this.props.report.notificationPreference}`); + const notificationPreference = this.props.translate(`notificationPreferencesPage.notificationPreferences.${this.props.report.notificationPreference}`); return ( @@ -97,7 +97,7 @@ class ReportSettingsPage extends Component { Navigation.navigate(ROUTES.getReportSettingsNotificationPreferencesRoute(this.props.report.reportID))} /> {shouldShowRoomName && ( From 3c96c2ef757f1e7391778f80cdcdfe1d5b574db8 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 25 Apr 2023 17:56:43 -0700 Subject: [PATCH 027/349] Update notif pref from option selection --- src/pages/settings/Report/NotificationPreferencePage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Report/NotificationPreferencePage.js b/src/pages/settings/Report/NotificationPreferencePage.js index 12b1a1b5235..e4870615de8 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.js +++ b/src/pages/settings/Report/NotificationPreferencePage.js @@ -49,7 +49,7 @@ const NotificationPreferencePage = (props) => { /> Report.updateNotificationPreference(props.reportID, props.report.notificationPreference, notificationPreference)} + onSelectRow={option => Report.updateNotificationPreference(props.report.reportID, props.report.notificationPreference, option.value)} hideSectionHeaders optionHoveredStyle={ { From 75614a3cfe4f05be6ea0590db4e865fa6eeab93b Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 25 Apr 2023 18:29:50 -0700 Subject: [PATCH 028/349] Update prop type comment --- src/pages/settings/Report/NotificationPreferencePage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/settings/Report/NotificationPreferencePage.js b/src/pages/settings/Report/NotificationPreferencePage.js index e4870615de8..2fe6dd430e5 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.js +++ b/src/pages/settings/Report/NotificationPreferencePage.js @@ -17,8 +17,7 @@ import themeColors from '../../../styles/themes/default'; const propTypes = { ...withLocalizePropTypes, - /* Onyx Props */ - /** The active report */ + /** The report for which we are setting notification preferences */ report: reportPropTypes.isRequired, }; const greenCheckmark = {src: Expensicons.Checkmark, color: themeColors.success}; From 63e3b0436f0c430bf61ed1750baa0c78f2419f5c Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 25 Apr 2023 18:31:14 -0700 Subject: [PATCH 029/349] Update notification pref and navigate --- src/libs/actions/Report.js | 5 +++-- src/pages/settings/Report/NotificationPreferencePage.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 09349e10d4d..22a7e3dd966 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -968,7 +968,7 @@ function saveReportActionDraftNumberOfLines(reportID, reportActionID, numberOfLi * @param {String} previousValue * @param {String} newValue */ -function updateNotificationPreference(reportID, previousValue, newValue) { +function updateNotificationPreferenceAndNavigate(reportID, previousValue, newValue) { const optimisticData = [ { onyxMethod: CONST.ONYX.METHOD.MERGE, @@ -984,6 +984,7 @@ function updateNotificationPreference(reportID, previousValue, newValue) { }, ]; API.write('UpdateReportNotificationPreference', {reportID, notificationPreference: newValue}, {optimisticData, failureData}); + Navigation.navigate(ROUTES.getReportSettingsRoute(reportID)); } /** @@ -1420,7 +1421,7 @@ export { addComment, addAttachment, reconnect, - updateNotificationPreference, + updateNotificationPreferenceAndNavigate, subscribeToReportTypingEvents, subscribeToReportCommentPushNotifications, unsubscribeFromReportChannel, diff --git a/src/pages/settings/Report/NotificationPreferencePage.js b/src/pages/settings/Report/NotificationPreferencePage.js index 2fe6dd430e5..6b57d06f28e 100644 --- a/src/pages/settings/Report/NotificationPreferencePage.js +++ b/src/pages/settings/Report/NotificationPreferencePage.js @@ -48,7 +48,7 @@ const NotificationPreferencePage = (props) => { /> Report.updateNotificationPreference(props.report.reportID, props.report.notificationPreference, option.value)} + onSelectRow={option => Report.updateNotificationPreferenceAndNavigate(props.report.reportID, props.report.notificationPreference, option.value)} hideSectionHeaders optionHoveredStyle={ { From df4947c40e2cc11e3987907564e2df3b5c6594f1 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Tue, 25 Apr 2023 18:31:31 -0700 Subject: [PATCH 030/349] Immediately key is always --- src/languages/en.js | 2 +- src/languages/es.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 081ffaca69b..9b1b5694629 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -324,7 +324,7 @@ export default { header: 'Notification Preferences', label: 'Notify me about new messages', notificationPreferences: { - immediately: 'Immediately', + always: 'Immediately', daily: 'Daily', mute: 'Mute', }, diff --git a/src/languages/es.js b/src/languages/es.js index bf3b78ad213..52c286fa1fa 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -323,7 +323,7 @@ export default { header: 'Preferencias de Avisos', label: 'Avisar sobre nuevos mensajes', notificationPreferences: { - immediately: 'Inmediatamente', + always: 'Inmediatamente', daily: 'Cada día', mute: 'Nunca', }, From 2b12c2051467927afd65f903d77ee7a8b86caded Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 27 Apr 2023 14:20:46 -0700 Subject: [PATCH 031/349] Convert RoomNamePage to functional component --- src/pages/settings/Report/RoomNamePage.js | 103 +++++++++------------- 1 file changed, 43 insertions(+), 60 deletions(-) diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index 67f5a3f742d..38f70c2c2e1 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -1,4 +1,4 @@ -import React, {Component} from 'react'; +import React, {useCallback} from 'react'; import _ from 'underscore'; import {View} from 'react-native'; import ScreenWrapper from '../../../components/ScreenWrapper'; @@ -20,76 +20,59 @@ import * as Report from '../../../libs/actions/Report'; const propTypes = { ...withLocalizePropTypes, - /* Onyx Props */ - /** The active report */ + /** The room report for which the name is being edited */ report: reportPropTypes.isRequired, }; -class RoomNamePage extends Component { - constructor(props) { - super(props); - this.updateRoomName = this.updateRoomName.bind(this); - this.validate = this.validate.bind(this); - } +const RoomNamePage = (props) => { + const report = props.report; + const translate = props.translate; + const updateRoomName = useCallback((values) => { + Report.updatePolicyRoomName(report, RoomNameInputUtils.modifyRoomName(values.roomName)); + Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(report.reportID)); + }, [report]); - /** - * Submit form to update room's name - * @param {Object} values - * @param {String} values.roomName - */ - updateRoomName(values) { - Report.updatePolicyRoomName(this.props.report, RoomNameInputUtils.modifyRoomName(values.roomName)); - Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(this.props.report.reportID)); - } - - /** - * @param {Object} values - * @param {String} values.roomName - * @returns {Object} - An object containing the errors for each inputID - */ - validate(values) { + const validate = useCallback((values) => { const errors = {}; if (_.isEmpty(values.roomName)) { - errors.roomName = this.props.translate('common.error.fieldRequired'); + errors.roomName = translate('common.error.fieldRequired'); } - return errors; - } + }, [translate]); - render() { - return ( - - Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(this.props.report.reportID))} - onCloseButtonPress={() => Navigation.dismissModal(true)} - /> -
- - - -
-
- ); - } -} + return ( + + Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(report.reportID))} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> +
+ + + +
+
+ ); +}; RoomNamePage.propTypes = propTypes; +RoomNamePage.displayName = 'RoomNamePage'; export default compose( withLocalize, From 2b5e277ec480ba722cd6082ddf136968af3e135f Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 27 Apr 2023 14:24:40 -0700 Subject: [PATCH 032/349] Update policy room and navigate in one action --- src/libs/actions/Report.js | 5 +++-- src/pages/settings/Report/RoomNamePage.js | 7 +------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 22a7e3dd966..7217e5245b9 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1095,7 +1095,7 @@ function navigateToConciergeChatAndDeleteReport(reportID) { * @param {String} policyRoomReport.reportName * @param {String} policyRoomName The updated name for the policy room */ -function updatePolicyRoomName(policyRoomReport, policyRoomName) { +function updatePolicyRoomNameAndNavigate(policyRoomReport, policyRoomName) { const reportID = policyRoomReport.reportID; const previousName = policyRoomReport.reportName; const optimisticData = [ @@ -1136,6 +1136,7 @@ function updatePolicyRoomName(policyRoomReport, policyRoomName) { }, ]; API.write('UpdatePolicyRoomName', {reportID, policyRoomName}, {optimisticData, successData, failureData}); + Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(reportID)); } /** @@ -1446,7 +1447,7 @@ export { openReportFromDeepLink, navigateToAndOpenReport, openPaymentDetailsPage, - updatePolicyRoomName, + updatePolicyRoomNameAndNavigate, clearPolicyRoomNameErrors, clearIOUError, subscribeToNewActionEvent, diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index 38f70c2c2e1..aec7c320656 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -27,11 +27,6 @@ const propTypes = { const RoomNamePage = (props) => { const report = props.report; const translate = props.translate; - const updateRoomName = useCallback((values) => { - Report.updatePolicyRoomName(report, RoomNameInputUtils.modifyRoomName(values.roomName)); - Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(report.reportID)); - }, [report]); - const validate = useCallback((values) => { const errors = {}; if (_.isEmpty(values.roomName)) { @@ -51,7 +46,7 @@ const RoomNamePage = (props) => {
Report.updatePolicyRoomNameAndNavigate(report, RoomNameInputUtils.modifyRoomName(values.roomName))} validate={validate} submitButtonText={translate('common.save')} enabledWhenOffline From cb2d057b6bb836523dcad60fccc5c3e016001935 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 27 Apr 2023 14:26:34 -0700 Subject: [PATCH 033/349] Dismiss keyboard after submitting on pushed page --- src/libs/actions/Report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 7217e5245b9..47b22a5eddf 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -984,7 +984,7 @@ function updateNotificationPreferenceAndNavigate(reportID, previousValue, newVal }, ]; API.write('UpdateReportNotificationPreference', {reportID, notificationPreference: newValue}, {optimisticData, failureData}); - Navigation.navigate(ROUTES.getReportSettingsRoute(reportID)); + Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(reportID)); } /** From 1c8d75e5c9c2e89ec4458577e59d4e2662289c67 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 27 Apr 2023 14:46:49 -0700 Subject: [PATCH 034/349] Auto focus room name input --- src/pages/settings/Report/RoomNamePage.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index aec7c320656..d36526bb324 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -27,6 +27,7 @@ const propTypes = { const RoomNamePage = (props) => { const report = props.report; const translate = props.translate; + const validate = useCallback((values) => { const errors = {}; if (_.isEmpty(values.roomName)) { @@ -55,6 +56,7 @@ const RoomNamePage = (props) => { Date: Thu, 27 Apr 2023 14:52:21 -0700 Subject: [PATCH 035/349] Remove unused onyx form key --- src/ONYXKEYS.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ONYXKEYS.js b/src/ONYXKEYS.js index 9bd5c78fec5..4f1a2062f09 100755 --- a/src/ONYXKEYS.js +++ b/src/ONYXKEYS.js @@ -183,7 +183,6 @@ export default { PROFILE_SETTINGS_FORM: 'profileSettingsForm', DISPLAY_NAME_FORM: 'displayNameForm', ROOM_NAME_FORM: 'roomNameForm', - NOTIFICATION_PREFERENCES_FORM: 'notificationPreferencesForm', LEGAL_NAME_FORM: 'legalNameForm', DATE_OF_BIRTH_FORM: 'dateOfBirthForm', HOME_ADDRESS_FORM: 'homeAddressForm', From 2d9d5fef26ee1101b339e218040b6247fec8cfd4 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 27 Apr 2023 15:27:34 -0700 Subject: [PATCH 036/349] Match existing room name validation --- src/libs/actions/Report.js | 6 ++++++ src/pages/settings/Report/RoomNamePage.js | 20 +++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 388448ba3a6..452cb73d9ea 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1122,6 +1122,12 @@ function navigateToConciergeChatAndDeleteReport(reportID) { function updatePolicyRoomNameAndNavigate(policyRoomReport, policyRoomName) { const reportID = policyRoomReport.reportID; const previousName = policyRoomReport.reportName; + + // No change needed, navigate back + if (previousName === policyRoomName) { + Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(reportID)); + return; + } const optimisticData = [ { onyxMethod: CONST.ONYX.METHOD.MERGE, diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index d36526bb324..696bc53369a 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -11,6 +11,7 @@ import TextInput from '../../../components/TextInput'; import styles from '../../../styles/styles'; import Navigation from '../../../libs/Navigation/Navigation'; import compose from '../../../libs/compose'; +import * as ValidationUtils from '../../../libs/ValidationUtils' import * as RoomNameInputUtils from '../../../libs/RoomNameInputUtils'; import withReportOrNotFound from '../../home/report/withReportOrNotFound'; import reportPropTypes from '../../reportPropTypes'; @@ -30,9 +31,26 @@ const RoomNamePage = (props) => { const validate = useCallback((values) => { const errors = {}; + + // We should skip validation hence we return an empty errors and we skip Form submission on the onSubmit method + if (values.roomName === report.reportName) { + return errors; + } + if (_.isEmpty(values.roomName)) { - errors.roomName = translate('common.error.fieldRequired'); + // We error if the user doesn't enter a room name or left blank + ErrorUtils.addErrorMessage(errors, 'roomName', this.props.translate('newRoomPage.pleaseEnterRoomName')); + } else if (!ValidationUtils.isValidRoomName(values.roomName)) { + // We error if the room name has invalid characters + ErrorUtils.addErrorMessage(errors, 'roomName', this.props.translate('newRoomPage.roomNameInvalidError')); + } else if (ValidationUtils.isReservedRoomName(values.roomName)) { + // Certain names are reserved for default rooms and should not be used for policy rooms. + ErrorUtils.addErrorMessage(errors, 'roomName', this.props.translate('newRoomPage.roomNameReservedError', {reservedName: values.roomName})); + } else if (ValidationUtils.isExistingRoomName(values.roomName, this.props.reports, this.props.report.policyID)) { + // The room name can't be set to one that already exists on the policy + ErrorUtils.addErrorMessage(errors, 'roomName', this.props.translate('newRoomPage.roomAlreadyExistsError')); } + return errors; }, [translate]); From 29c4293aca7cd6b2f2db5724ae36d781b431673b Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 27 Apr 2023 15:53:50 -0700 Subject: [PATCH 037/349] Fix a bunch of misc errors --- src/pages/settings/Report/RoomNamePage.js | 42 ++++++++++++++--------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index 696bc53369a..2b469d095c0 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -1,32 +1,40 @@ import React, {useCallback} from 'react'; import _ from 'underscore'; +import {withOnyx} from 'react-native-onyx'; +import PropTypes from 'prop-types'; import {View} from 'react-native'; import ScreenWrapper from '../../../components/ScreenWrapper'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; import Form from '../../../components/Form'; import ONYXKEYS from '../../../ONYXKEYS'; -import CONST from '../../../CONST'; -import TextInput from '../../../components/TextInput'; import styles from '../../../styles/styles'; import Navigation from '../../../libs/Navigation/Navigation'; import compose from '../../../libs/compose'; +import * as ErrorUtils from '../../../libs/ErrorUtils'; import * as ValidationUtils from '../../../libs/ValidationUtils' -import * as RoomNameInputUtils from '../../../libs/RoomNameInputUtils'; import withReportOrNotFound from '../../home/report/withReportOrNotFound'; import reportPropTypes from '../../reportPropTypes'; import ROUTES from '../../../ROUTES'; import * as Report from '../../../libs/actions/Report'; +import RoomNameInput from '../../../components/RoomNameInput'; const propTypes = { ...withLocalizePropTypes, /** The room report for which the name is being edited */ report: reportPropTypes.isRequired, + + /** All reports shared with the user */ + reports: PropTypes.objectOf(reportPropTypes), +}; +const defaultProps = { + reports: {}, }; const RoomNamePage = (props) => { const report = props.report; + const reports = props.reports; const translate = props.translate; const validate = useCallback((values) => { @@ -39,20 +47,20 @@ const RoomNamePage = (props) => { if (_.isEmpty(values.roomName)) { // We error if the user doesn't enter a room name or left blank - ErrorUtils.addErrorMessage(errors, 'roomName', this.props.translate('newRoomPage.pleaseEnterRoomName')); + ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.pleaseEnterRoomName')); } else if (!ValidationUtils.isValidRoomName(values.roomName)) { // We error if the room name has invalid characters - ErrorUtils.addErrorMessage(errors, 'roomName', this.props.translate('newRoomPage.roomNameInvalidError')); + ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.roomNameInvalidError')); } else if (ValidationUtils.isReservedRoomName(values.roomName)) { // Certain names are reserved for default rooms and should not be used for policy rooms. - ErrorUtils.addErrorMessage(errors, 'roomName', this.props.translate('newRoomPage.roomNameReservedError', {reservedName: values.roomName})); - } else if (ValidationUtils.isExistingRoomName(values.roomName, this.props.reports, this.props.report.policyID)) { + ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.roomNameReservedError', {reservedName: values.roomName})); + } else if (ValidationUtils.isExistingRoomName(values.roomName, reports, report.policyID)) { // The room name can't be set to one that already exists on the policy - ErrorUtils.addErrorMessage(errors, 'roomName', this.props.translate('newRoomPage.roomAlreadyExistsError')); + ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.roomAlreadyExistsError')); } return errors; - }, [translate]); + }, [report, reports, translate]); return ( @@ -65,20 +73,16 @@ const RoomNamePage = (props) => { Report.updatePolicyRoomNameAndNavigate(report, RoomNameInputUtils.modifyRoomName(values.roomName))} + onSubmit={values => Report.updatePolicyRoomNameAndNavigate(report, values.roomName)} validate={validate} submitButtonText={translate('common.save')} enabledWhenOffline > - @@ -87,9 +91,15 @@ const RoomNamePage = (props) => { }; RoomNamePage.propTypes = propTypes; +RoomNamePage.defaultProps = defaultProps; RoomNamePage.displayName = 'RoomNamePage'; export default compose( withLocalize, withReportOrNotFound, + withOnyx({ + reports: { + key: ONYXKEYS.COLLECTION.REPORT, + }, + }), )(RoomNamePage); From 01c6a3dba731c7da4b8e566002c04bafd4540648 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 27 Apr 2023 15:56:00 -0700 Subject: [PATCH 038/349] Prevent saving unchanged report notif pref --- src/libs/actions/Report.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 452cb73d9ea..c72e9f41857 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -993,6 +993,10 @@ function saveReportActionDraftNumberOfLines(reportID, reportActionID, numberOfLi * @param {String} newValue */ function updateNotificationPreferenceAndNavigate(reportID, previousValue, newValue) { + if (previousValue === newValue) { + Navigation.drawerGoBack(ROUTES.getReportSettingsRoute(reportID)); + return; + } const optimisticData = [ { onyxMethod: CONST.ONYX.METHOD.MERGE, From df86d8c5d9c2d25f64086f0f2b88a7b54ac3b5dc Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Thu, 27 Apr 2023 16:07:45 -0700 Subject: [PATCH 039/349] Fix linter --- src/pages/settings/Report/RoomNamePage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index 2b469d095c0..c3d3dc9aecf 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -12,7 +12,7 @@ import styles from '../../../styles/styles'; import Navigation from '../../../libs/Navigation/Navigation'; import compose from '../../../libs/compose'; import * as ErrorUtils from '../../../libs/ErrorUtils'; -import * as ValidationUtils from '../../../libs/ValidationUtils' +import * as ValidationUtils from '../../../libs/ValidationUtils'; import withReportOrNotFound from '../../home/report/withReportOrNotFound'; import reportPropTypes from '../../reportPropTypes'; import ROUTES from '../../../ROUTES'; From c58bda76d19c827f253a2c6871c021c5be57f05b Mon Sep 17 00:00:00 2001 From: Youssef Lourayad Date: Fri, 28 Apr 2023 01:05:01 +0100 Subject: [PATCH 040/349] Bug fix --- src/pages/ReportParticipantsPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/ReportParticipantsPage.js b/src/pages/ReportParticipantsPage.js index 8dc40db0398..2863ec7d175 100755 --- a/src/pages/ReportParticipantsPage.js +++ b/src/pages/ReportParticipantsPage.js @@ -74,8 +74,8 @@ const getAllParticipants = (report, personalDetails) => { text: userPersonalDetail.displayName, tooltipText: userLogin, participantsList: [{login, displayName: userPersonalDetail.displayName}], - }).value(); - }); + }); + }).value(); }; const ReportParticipantsPage = (props) => { From a303cc4e6d17ff796ddec8e9a1ec29ee5449af62 Mon Sep 17 00:00:00 2001 From: neil-marcellini Date: Mon, 1 May 2023 12:27:49 -0700 Subject: [PATCH 041/349] Fix empty room name validation --- src/pages/settings/Report/RoomNamePage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Report/RoomNamePage.js b/src/pages/settings/Report/RoomNamePage.js index c3d3dc9aecf..59ee0461b31 100644 --- a/src/pages/settings/Report/RoomNamePage.js +++ b/src/pages/settings/Report/RoomNamePage.js @@ -1,8 +1,8 @@ import React, {useCallback} from 'react'; -import _ from 'underscore'; import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; import {View} from 'react-native'; +import CONST from '../../../CONST'; import ScreenWrapper from '../../../components/ScreenWrapper'; import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; @@ -45,7 +45,7 @@ const RoomNamePage = (props) => { return errors; } - if (_.isEmpty(values.roomName)) { + if (!values.roomName || values.roomName === CONST.POLICY.ROOM_PREFIX) { // We error if the user doesn't enter a room name or left blank ErrorUtils.addErrorMessage(errors, 'roomName', translate('newRoomPage.pleaseEnterRoomName')); } else if (!ValidationUtils.isValidRoomName(values.roomName)) { From 084904f6702b821628326c093ff52035c54ebc7b Mon Sep 17 00:00:00 2001 From: Terry Sahaidak Date: Thu, 27 Apr 2023 13:21:51 +0300 Subject: [PATCH 042/349] Upgrade reanimated to 3.1.0 --- ios/Podfile.lock | 4 ++-- package-lock.json | 49 ++++++++++++++++++++++++++-------------------- package.json | 2 +- src/setup/index.js | 7 ------- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e81f2d2c640..b7823d7d19f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -668,7 +668,7 @@ PODS: - React-Core - RNReactNativeHapticFeedback (1.14.0): - React-Core - - RNReanimated (3.0.0-rc.10): + - RNReanimated (3.1.0): - DoubleConversion - FBLazyVector - FBReactNativeSpec @@ -1118,7 +1118,7 @@ SPEC CHECKSUMS: RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81 RNPermissions: dcdb7b99796bbeda6975a6e79ad519c41b251b1c RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c - RNReanimated: fbc356493970e3acddc15586b1bccb5eab3ff1ec + RNReanimated: b1220a0e5168745283ff5d53bfc7d2144b2cee1b RNScreens: 0df01424e9e0ed7827200d6ed1087ddd06c493f9 RNSVG: 38ca962c970dbce1ca38991a5aebf26d163f9efb SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d diff --git a/package-lock.json b/package-lock.json index 82c931d92b2..9eb5ee3c34d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -83,7 +83,7 @@ "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#107b3786ae6bc155dec05c7fc5ee525d3421dc21", "react-native-plaid-link-sdk": "^10.0.0", "react-native-quick-sqlite": "^8.0.0-beta.2", - "react-native-reanimated": "3.0.0-rc.10", + "react-native-reanimated": "3.1.0", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.4.1", "react-native-screens": "3.17.0", @@ -34881,23 +34881,31 @@ } }, "node_modules/react-native-reanimated": { - "version": "3.0.0-rc.10", - "license": "MIT", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.1.0.tgz", + "integrity": "sha512-8YJR7yHnrqK6yKWzkGLVEawi1WZqJ9bGIehKEnE8zG58yLrSwUZe1T220XTbftpkA3r37Sy0kJJ/HOOiaIU+HQ==", "dependencies": { "@babel/plugin-transform-object-assign": "^7.16.7", "@babel/preset-typescript": "^7.16.7", - "convert-source-map": "^1.7.0", - "invariant": "^2.2.4", - "lodash.isequal": "^4.5.0", - "setimmediate": "^1.0.5", - "string-hash-64": "^1.0.3" + "convert-source-map": "^2.0.0", + "invariant": "^2.2.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0-0", + "@babel/plugin-proposal-optional-chaining": "^7.0.0-0", + "@babel/plugin-transform-arrow-functions": "^7.0.0-0", + "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", + "@babel/plugin-transform-template-literals": "^7.0.0-0", "react": "*", "react-native": "*" } }, + "node_modules/react-native-reanimated/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "node_modules/react-native-render-html": { "version": "6.3.1", "license": "BSD-2-Clause", @@ -38075,10 +38083,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/string-hash-64": { - "version": "1.0.3", - "license": "MIT" - }, "node_modules/string-length": { "version": "4.0.2", "license": "MIT", @@ -64582,15 +64586,21 @@ "requires": {} }, "react-native-reanimated": { - "version": "3.0.0-rc.10", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.1.0.tgz", + "integrity": "sha512-8YJR7yHnrqK6yKWzkGLVEawi1WZqJ9bGIehKEnE8zG58yLrSwUZe1T220XTbftpkA3r37Sy0kJJ/HOOiaIU+HQ==", "requires": { "@babel/plugin-transform-object-assign": "^7.16.7", "@babel/preset-typescript": "^7.16.7", - "convert-source-map": "^1.7.0", - "invariant": "^2.2.4", - "lodash.isequal": "^4.5.0", - "setimmediate": "^1.0.5", - "string-hash-64": "^1.0.3" + "convert-source-map": "^2.0.0", + "invariant": "^2.2.4" + }, + "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + } } }, "react-native-render-html": { @@ -66661,9 +66671,6 @@ "safe-buffer": "~5.1.0" } }, - "string-hash-64": { - "version": "1.0.3" - }, "string-length": { "version": "4.0.2", "requires": { diff --git a/package.json b/package.json index cfd39cfe59f..0df9ceb318b 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#107b3786ae6bc155dec05c7fc5ee525d3421dc21", "react-native-plaid-link-sdk": "^10.0.0", "react-native-quick-sqlite": "^8.0.0-beta.2", - "react-native-reanimated": "3.0.0-rc.10", + "react-native-reanimated": "3.1.0", "react-native-render-html": "6.3.1", "react-native-safe-area-context": "4.4.1", "react-native-screens": "3.17.0", diff --git a/src/setup/index.js b/src/setup/index.js index eae7036331c..352417242ad 100644 --- a/src/setup/index.js +++ b/src/setup/index.js @@ -53,11 +53,4 @@ export default function () { // Perform any other platform-specific setup platformSetup(); - - // Workaround to a reanimated issue -> https://github.com/software-mansion/react-native-reanimated/issues/3355 - // We can remove it as soon as we are on > reanimated 3.0.0+ - if (process.browser) { - // eslint-disable-next-line no-underscore-dangle - window._frameTimestamp = null; - } } From 249ef5739a1f46099a4b96c3b8729b57bed7295c Mon Sep 17 00:00:00 2001 From: Terry Sahaidak Date: Tue, 2 May 2023 13:46:26 +0300 Subject: [PATCH 043/349] Fix recursive crash on Android --- src/components/AvatarCropModal/Slider.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/AvatarCropModal/Slider.js b/src/components/AvatarCropModal/Slider.js index 310b5f81b34..f478d95f3dd 100644 --- a/src/components/AvatarCropModal/Slider.js +++ b/src/components/AvatarCropModal/Slider.js @@ -26,12 +26,13 @@ const defaultProps = { // This component can't be written using class since reanimated API uses hooks. const Slider = (props) => { + const sliderValue = props.sliderValue; const [tooltipIsVisible, setTooltipIsVisible] = useState(true); // A reanimated memoized style, which tracks // a translateX shared value and updates the slider position. const rSliderStyle = useAnimatedStyle(() => ({ - transform: [{translateX: props.sliderValue.value}], + transform: [{translateX: sliderValue.value}], })); // We're preventing text selection with ControlSelection.blockElement to prevent safari From 6422ca5cd9c97d1945f4314e8790b128a8808656 Mon Sep 17 00:00:00 2001 From: Terry Sahaidak Date: Tue, 2 May 2023 13:47:12 +0300 Subject: [PATCH 044/349] Refactor ReportActionList to use more common Reanimated API --- src/pages/home/report/ReportActionsList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index 87daedfd1e6..32697ebaa14 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -78,10 +78,10 @@ function keyExtractor(item) { const ReportActionsList = (props) => { const opacity = useSharedValue(0); const animatedStyles = useAnimatedStyle(() => ({ - opacity: withTiming(opacity.value, {duration: 100}), + opacity: opacity.value, })); useEffect(() => { - opacity.value = 1; + opacity.value = withTiming(1, {duration: 100}); }, [opacity]); const [skeletonViewHeight, setSkeletonViewHeight] = useState(0); From 020667b5485228090b16a23350defd6ab821e29a Mon Sep 17 00:00:00 2001 From: Terry Sahaidak Date: Tue, 2 May 2023 14:35:50 +0300 Subject: [PATCH 045/349] Podfile changes after upgrade --- ios/Podfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b7823d7d19f..ea4fc2b0000 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1014,7 +1014,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Airship: c70eed50e429f97f5adb285423c7291fb7a032ae AirshipFrameworkProxy: 2eefb77bb77b5120b0f48814b0d44439aa3ad415 - boost: a7c83b31436843459a1961bfd74b96033dc77234 + boost: 57d2868c099736d80fcd648bf211b4431e51a558 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: ff54429f0110d3c722630a98096ba689c39f6d5f @@ -1057,7 +1057,7 @@ SPEC CHECKSUMS: Permission-LocationWhenInUse: 3ba99e45c852763f730eabecec2870c2382b7bd4 Plaid: 7d340abeadb46c7aa1a91f896c5b22395a31fcf2 PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef - RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda + RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 RCTRequired: e9e7b8b45aa9bedb2fdad71740adf07a7265b9be RCTTypeSafety: 9ae0e9206625e995f0df4d5b9ddc94411929fb30 React: a71c8e1380f07e01de721ccd52bcf9c03e81867d From a3485a73da271109791cb9b241cf50cf78a31b76 Mon Sep 17 00:00:00 2001 From: Terry Sahaidak Date: Tue, 2 May 2023 17:05:28 +0300 Subject: [PATCH 046/349] Fix another props usage inside useAnimatedStyle --- .../AvatarCropModal/ImageCropView.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/components/AvatarCropModal/ImageCropView.js b/src/components/AvatarCropModal/ImageCropView.js index bf0762b960a..7865885c9ab 100644 --- a/src/components/AvatarCropModal/ImageCropView.js +++ b/src/components/AvatarCropModal/ImageCropView.js @@ -52,17 +52,24 @@ const defaultProps = { const ImageCropView = (props) => { const containerStyle = StyleUtils.getWidthAndHeightStyle(props.containerSize, props.containerSize); + const originalImageHeight = props.originalImageHeight; + const originalImageWidth = props.originalImageWidth; + const rotation = props.rotation; + const translateX = props.translateX; + const translateY = props.translateY; + const scale = props.scale; + // A reanimated memoized style, which updates when the image's size or scale changes. const imageStyle = useAnimatedStyle(() => { - const height = props.originalImageHeight.value; - const width = props.originalImageWidth.value; + const height = originalImageHeight.value; + const width = originalImageWidth.value; const aspectRatio = height > width ? height / width : width / height; - const rotate = interpolate(props.rotation.value, [0, 360], [0, 360]); + const rotate = interpolate(rotation.value, [0, 360], [0, 360]); return { transform: [ - {translateX: props.translateX.value}, - {translateY: props.translateY.value}, - {scale: props.scale.value * aspectRatio}, + {translateX: translateX.value}, + {translateY: translateY.value}, + {scale: scale.value * aspectRatio}, {rotate: `${rotate}deg`}, ], }; From 97bd3f032b863351c1790bfde4c465c6afdef91e Mon Sep 17 00:00:00 2001 From: Terry Sahaidak Date: Wed, 3 May 2023 19:24:51 +0300 Subject: [PATCH 047/349] Add iOS crash patch --- patches/react-native-reanimated+3.1.0.patch | 163 ++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 patches/react-native-reanimated+3.1.0.patch diff --git a/patches/react-native-reanimated+3.1.0.patch b/patches/react-native-reanimated+3.1.0.patch new file mode 100644 index 00000000000..ea6c4e52e01 --- /dev/null +++ b/patches/react-native-reanimated+3.1.0.patch @@ -0,0 +1,163 @@ +diff --git a/node_modules/react-native-reanimated/ios/REANodesManager.mm b/node_modules/react-native-reanimated/ios/REANodesManager.mm +index 26bb253..10e6028 100644 +--- a/node_modules/react-native-reanimated/ios/REANodesManager.mm ++++ b/node_modules/react-native-reanimated/ios/REANodesManager.mm +@@ -85,19 +85,77 @@ - (void)runSyncUIUpdatesWithObserver:(id)observer + + @end + +-@interface REANodesManager () ++#ifndef RCT_NEW_ARCH_ENABLED + ++@interface REASyncUpdateObserver : NSObject + @end + ++@implementation REASyncUpdateObserver { ++ volatile void (^_mounting)(void); ++ volatile BOOL _waitTimedOut; ++ dispatch_semaphore_t _semaphore; ++} ++ ++- (instancetype)init ++{ ++ self = [super init]; ++ if (self) { ++ _mounting = nil; ++ _waitTimedOut = NO; ++ _semaphore = dispatch_semaphore_create(0); ++ } ++ return self; ++} ++ ++- (void)dealloc ++{ ++ RCTAssert(_mounting == nil, @"Mouting block was set but never executed. This may lead to UI inconsistencies"); ++} ++ ++- (void)unblockUIThread ++{ ++ RCTAssertUIManagerQueue(); ++ dispatch_semaphore_signal(_semaphore); ++} ++ ++- (void)waitAndMountWithTimeout:(NSTimeInterval)timeout ++{ ++ RCTAssertMainQueue(); ++ long result = dispatch_semaphore_wait(_semaphore, dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_SEC)); ++ if (result != 0) { ++ @synchronized(self) { ++ _waitTimedOut = YES; ++ } ++ } ++ if (_mounting) { ++ _mounting(); ++ _mounting = nil; ++ } ++} ++ ++- (BOOL)uiManager:(RCTUIManager *)manager performMountingWithBlock:(RCTUIManagerMountingBlock)block ++{ ++ RCTAssertUIManagerQueue(); ++ @synchronized(self) { ++ if (_waitTimedOut) { ++ return NO; ++ } else { ++ _mounting = block; ++ return YES; ++ } ++ } ++} ++ ++@end ++ ++#endif ++ + @implementation REANodesManager { + CADisplayLink *_displayLink; + BOOL _wantRunUpdates; + NSMutableArray *_onAnimationCallbacks; + BOOL _tryRunBatchUpdatesSynchronously; + REAEventHandler _eventHandler; +- volatile void (^_mounting)(void); +- NSObject *_syncLayoutUpdatesWaitLock; +- volatile BOOL _syncLayoutUpdatesWaitTimedOut; + NSMutableDictionary *_componentUpdateBuffer; + NSMutableDictionary *_viewRegistry; + #ifdef RCT_NEW_ARCH_ENABLED +@@ -125,7 +183,6 @@ - (nonnull instancetype)initWithModule:(REAModule *)reanimatedModule + _operationsInBatch = [NSMutableDictionary new]; + _componentUpdateBuffer = [NSMutableDictionary new]; + _viewRegistry = [_uiManager valueForKey:@"_viewRegistry"]; +- _syncLayoutUpdatesWaitLock = [NSObject new]; + } + + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onAnimationFrame:)]; +@@ -241,19 +298,6 @@ - (void)onAnimationFrame:(CADisplayLink *)displayLink + } + } + +-- (BOOL)uiManager:(RCTUIManager *)manager performMountingWithBlock:(RCTUIManagerMountingBlock)block +-{ +- RCTAssert(_mounting == nil, @"Mouting block is expected to not be set"); +- @synchronized(_syncLayoutUpdatesWaitLock) { +- if (_syncLayoutUpdatesWaitTimedOut) { +- return NO; +- } else { +- _mounting = block; +- return YES; +- } +- } +-} +- + - (void)performOperations + { + #ifdef RCT_NEW_ARCH_ENABLED +@@ -268,8 +312,7 @@ - (void)performOperations + _tryRunBatchUpdatesSynchronously = NO; + + __weak __typeof__(self) weakSelf = self; +- dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); +- _syncLayoutUpdatesWaitTimedOut = NO; ++ REASyncUpdateObserver *syncUpdateObserver = [REASyncUpdateObserver new]; + RCTExecuteOnUIManagerQueue(^{ + __typeof__(self) strongSelf = weakSelf; + if (strongSelf == nil) { +@@ -278,7 +321,7 @@ - (void)performOperations + BOOL canUpdateSynchronously = trySynchronously && ![strongSelf.uiManager hasEnqueuedUICommands]; + + if (!canUpdateSynchronously) { +- dispatch_semaphore_signal(semaphore); ++ [syncUpdateObserver unblockUIThread]; + } + + for (int i = 0; i < copiedOperationsQueue.count; i++) { +@@ -286,8 +329,8 @@ - (void)performOperations + } + + if (canUpdateSynchronously) { +- [strongSelf.uiManager runSyncUIUpdatesWithObserver:strongSelf]; +- dispatch_semaphore_signal(semaphore); ++ [strongSelf.uiManager runSyncUIUpdatesWithObserver:syncUpdateObserver]; ++ [syncUpdateObserver unblockUIThread]; + } + // In case canUpdateSynchronously=true we still have to send uiManagerWillPerformMounting event + // to observers because some components (e.g. TextInput) update their UIViews only on that event. +@@ -298,17 +341,7 @@ - (void)performOperations + // from CADisplayLink but it is easier to hardcode it for the time being. + // The reason why we use frame duration here is that if takes longer than one frame to complete layout tasks + // there is no point of synchronizing layout with the UI interaction as we get that one frame delay anyways. +- long result = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_MSEC)); +- if (result != 0) { +- @synchronized(_syncLayoutUpdatesWaitLock) { +- _syncLayoutUpdatesWaitTimedOut = YES; +- } +- } +- } +- +- if (_mounting) { +- _mounting(); +- _mounting = nil; ++ [syncUpdateObserver waitAndMountWithTimeout:0.016]; + } + } + _wantRunUpdates = NO; From 8f36a4b412bf86028bf7beeb3bdc9b91b34174c7 Mon Sep 17 00:00:00 2001 From: Oliver Wilks Date: Sat, 6 May 2023 12:16:14 +0100 Subject: [PATCH 048/349] Add dInlineFlex to display --- src/styles/utilities/display.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/styles/utilities/display.js b/src/styles/utilities/display.js index 9e7e4107a93..bcef1c6b565 100644 --- a/src/styles/utilities/display.js +++ b/src/styles/utilities/display.js @@ -20,6 +20,11 @@ export default { dInline: { display: 'inline', }, + + dInlineFlex: { + display: 'inline-flex', + }, + dBlock: { display: 'block', }, From 15b4bf67dd1185e30ab3594f626ef013261aaf98 Mon Sep 17 00:00:00 2001 From: Oliver Wilks Date: Sat, 6 May 2023 12:16:52 +0100 Subject: [PATCH 049/349] Created editedLabelStyles --- src/styles/editedLabelStyles/index.js | 3 +++ src/styles/editedLabelStyles/index.native.js | 1 + 2 files changed, 4 insertions(+) create mode 100644 src/styles/editedLabelStyles/index.js create mode 100644 src/styles/editedLabelStyles/index.native.js diff --git a/src/styles/editedLabelStyles/index.js b/src/styles/editedLabelStyles/index.js new file mode 100644 index 00000000000..172dcc152b7 --- /dev/null +++ b/src/styles/editedLabelStyles/index.js @@ -0,0 +1,3 @@ +import display from '../utilities/display'; + +export default {...display.dInlineFlex}; diff --git a/src/styles/editedLabelStyles/index.native.js b/src/styles/editedLabelStyles/index.native.js new file mode 100644 index 00000000000..ff8b4c56321 --- /dev/null +++ b/src/styles/editedLabelStyles/index.native.js @@ -0,0 +1 @@ +export default {}; From 16d5cf17d44760e6516963096d4eb380fde8180c Mon Sep 17 00:00:00 2001 From: Oliver Wilks Date: Sat, 6 May 2023 12:18:18 +0100 Subject: [PATCH 050/349] Added styles to edited label --- .../HTMLEngineProvider/HTMLRenderers/EditedRenderer.js | 7 ++++++- src/pages/home/report/ReportActionItemFragment.js | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js b/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js index a7783449a47..1295fc0946b 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js +++ b/src/components/HTMLEngineProvider/HTMLRenderers/EditedRenderer.js @@ -5,6 +5,8 @@ import withLocalize, {withLocalizePropTypes} from '../../withLocalize'; import Text from '../../Text'; import variables from '../../../styles/variables'; import themeColors from '../../../styles/themes/default'; +import styles from '../../../styles/styles'; +import editedLabelStyles from '../../../styles/editedLabelStyles'; const propTypes = { ...htmlRendererPropTypes, @@ -19,8 +21,11 @@ const EditedRenderer = (props) => { {...defaultRendererProps} fontSize={variables.fontSizeSmall} color={themeColors.textSupporting} + style={[styles.alignItemsBaseline, editedLabelStyles]} > - {` ${props.translate('reportActionCompose.edited')}`} + {/* Native devices do not support margin between nested text */} + {' '} + {props.translate('reportActionCompose.edited')} ); }; diff --git a/src/pages/home/report/ReportActionItemFragment.js b/src/pages/home/report/ReportActionItemFragment.js index 29b6907b9d9..9eb6b0900f3 100644 --- a/src/pages/home/report/ReportActionItemFragment.js +++ b/src/pages/home/report/ReportActionItemFragment.js @@ -18,6 +18,7 @@ import * as StyleUtils from '../../../styles/StyleUtils'; import {withNetwork} from '../../../components/OnyxProvider'; import CONST from '../../../CONST'; import applyStrikethrough from '../../../components/HTMLEngineProvider/applyStrikethrough'; +import editedLabelStyles from '../../../styles/editedLabelStyles'; const propTypes = { /** The message fragment needing to be displayed */ @@ -133,8 +134,10 @@ const ReportActionItemFragment = (props) => { - {` ${props.translate('reportActionCompose.edited')}`} + {' '} + {props.translate('reportActionCompose.edited')} )} From c7e44a5da1b637f63b76df257a2b970844a25474 Mon Sep 17 00:00:00 2001 From: Terry Sahaidak Date: Mon, 8 May 2023 12:14:47 +0300 Subject: [PATCH 051/349] Address review feedback --- package-lock.json | 8 ++++++-- package.json | 1 + src/components/AvatarCropModal/ImageCropView.js | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9eb5ee3c34d..e8b3669adaf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -95,6 +95,7 @@ "react-web-config": "^1.0.0", "save": "^2.4.0", "semver": "^7.3.8", + "setimmediate": "^1.0.5", "shim-keyboard-event-key": "^1.0.3", "underscore": "^1.13.1" }, @@ -37207,7 +37208,8 @@ }, "node_modules/setimmediate": { "version": "1.0.5", - "license": "MIT" + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, "node_modules/setprototypeof": { "version": "1.2.0", @@ -66058,7 +66060,9 @@ } }, "setimmediate": { - "version": "1.0.5" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, "setprototypeof": { "version": "1.2.0" diff --git a/package.json b/package.json index 0df9ceb318b..56d3a7d87eb 100644 --- a/package.json +++ b/package.json @@ -128,6 +128,7 @@ "react-web-config": "^1.0.0", "save": "^2.4.0", "semver": "^7.3.8", + "setimmediate": "^1.0.5", "shim-keyboard-event-key": "^1.0.3", "underscore": "^1.13.1" }, diff --git a/src/components/AvatarCropModal/ImageCropView.js b/src/components/AvatarCropModal/ImageCropView.js index 7865885c9ab..0d797a6567e 100644 --- a/src/components/AvatarCropModal/ImageCropView.js +++ b/src/components/AvatarCropModal/ImageCropView.js @@ -73,7 +73,7 @@ const ImageCropView = (props) => { {rotate: `${rotate}deg`}, ], }; - }, [props.originalImageHeight, props.originalImageWidth]); + }, [originalImageHeight, originalImageWidth, rotation, translateX, translateY, scale]); // We're preventing text selection with ControlSelection.blockElement to prevent safari // default behaviour of cursor - I-beam cursor on drag. See https://github.com/Expensify/App/issues/13688 From 5d82281fe5bc6d8953add82e6accea9b430bf0da Mon Sep 17 00:00:00 2001 From: Terry Sahaidak Date: Mon, 8 May 2023 12:46:05 +0300 Subject: [PATCH 052/349] Fix setImmediate being undefined in jest --- jest/setup.js | 1 + 1 file changed, 1 insertion(+) diff --git a/jest/setup.js b/jest/setup.js index 40705925157..228f3a22f33 100644 --- a/jest/setup.js +++ b/jest/setup.js @@ -1,3 +1,4 @@ +import 'setimmediate'; import 'react-native-gesture-handler/jestSetup'; import * as reanimatedJestUtils from 'react-native-reanimated/src/reanimated2/jestUtils'; import setupMockImages from './setupMockImages'; From 0d9213eb6e521060b7b3a954f6c5097091e1bb5b Mon Sep 17 00:00:00 2001 From: Ionatan Wiznia Date: Tue, 9 May 2023 19:36:47 -0300 Subject: [PATCH 053/349] Start working on money request header --- src/components/MoneyRequestHeader.js | 35 ++++++++++++++++++- src/components/ReportActionItem/IOUPreview.js | 4 ++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/components/MoneyRequestHeader.js b/src/components/MoneyRequestHeader.js index 089276725f5..f3b131a298b 100644 --- a/src/components/MoneyRequestHeader.js +++ b/src/components/MoneyRequestHeader.js @@ -1,4 +1,5 @@ import React from 'react'; +import {withOnyx} from 'react-native-onyx'; import {View} from 'react-native'; import PropTypes from 'prop-types'; import lodashGet from 'lodash/get'; @@ -18,6 +19,9 @@ import compose from '../libs/compose'; import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; import Icon from './Icon'; +import SettlementButton from './SettlementButton'; +import * as Policy from '../libs/actions/Policy'; +import ONYXKEYS from '../ONYXKEYS'; const propTypes = { /** The report currently being looked at */ @@ -29,6 +33,9 @@ const propTypes = { name: PropTypes.string, }).isRequired, + /** The chat report this is the report is tied to */ + chatReport: iouReportPropTypes, + /** Personal details so we can get the ones for the report participants */ personalDetails: PropTypes.objectOf(participantPropTypes).isRequired, @@ -40,6 +47,14 @@ const propTypes = { const defaultProps = { isSingleTransactionView: false, + chatReport: null, +}; + +const payRequest = (paymentType, chatReport) => { + if (chatReport && chatReport.chatType === CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT) { + + } else { + } }; const MoneyRequestHeader = (props) => { @@ -53,6 +68,8 @@ const MoneyRequestHeader = (props) => { const payeeAvatar = isExpenseReport ? ReportUtils.getWorkspaceAvatar(props.report) : ReportUtils.getAvatar(lodashGet(props.personalDetails, [props.report.managerEmail, 'avatar']), props.report.managerEmail); + const policy = props.policies[`policy_${props.report.policyID}`]; + const shouldShowSettlementButton = !isSettled && (Policy.isAdminOfFreePolicy([policy]) || props.report.type === CONST.REPORT.TYPE.IOU); return ( { /> )} + {shouldShowSettlementButton && ( + payRequest(paymentType, props.chatReport)} + /> + )} @@ -121,4 +146,12 @@ MoneyRequestHeader.displayName = 'MoneyRequestHeader'; MoneyRequestHeader.propTypes = propTypes; MoneyRequestHeader.defaultProps = defaultProps; -export default compose(withWindowDimensions, withLocalize)(MoneyRequestHeader); +export default compose( + withWindowDimensions, + withLocalize, + withOnyx({ + chatReport: { + key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`, + }, + }), +)(MoneyRequestHeader); diff --git a/src/components/ReportActionItem/IOUPreview.js b/src/components/ReportActionItem/IOUPreview.js index 202689085d9..f43b73d0073 100644 --- a/src/components/ReportActionItem/IOUPreview.js +++ b/src/components/ReportActionItem/IOUPreview.js @@ -75,6 +75,8 @@ const propTypes = { /** Does the iouReport have an outstanding IOU? */ hasOutstandingIOU: PropTypes.bool, + + state: PropTypes.string, }), /** True if this is this IOU is a split instead of a 1:1 request */ @@ -205,7 +207,7 @@ const IOUPreview = (props) => { {Str.htmlDecode(lodashGet(props.action, 'originalMessage.comment', ''))} - {isCurrentUserManager && !props.shouldHidePayButton && props.iouReport.stateNum === CONST.REPORT.STATE_NUM.PROCESSING && ( + {isCurrentUserManager && !props.shouldHidePayButton && props.iouReport.state === CONST.REPORT.STATE.PROCESSING && ( - -); - -ButtonWithDropdown.propTypes = propTypes; -ButtonWithDropdown.defaultProps = defaultProps; -ButtonWithDropdown.displayName = 'ButtonWithDropdown'; -export default ButtonWithDropdown; diff --git a/src/components/ButtonWithMenu.js b/src/components/ButtonWithDropdownMenu.js similarity index 59% rename from src/components/ButtonWithMenu.js rename to src/components/ButtonWithDropdownMenu.js index a1576ad5f55..c4b5501cea7 100644 --- a/src/components/ButtonWithMenu.js +++ b/src/components/ButtonWithDropdownMenu.js @@ -4,8 +4,10 @@ import {View} from 'react-native'; import _ from 'underscore'; import styles from '../styles/styles'; import Button from './Button'; -import ButtonWithDropdown from './ButtonWithDropdown'; +import * as Expensicons from './Icon/Expensicons'; +import Icon from './Icon'; import PopoverMenu from './PopoverMenu'; +import themeColors from '../styles/themes/default'; const propTypes = { /** Text to display for the menu header */ @@ -40,7 +42,12 @@ const defaultProps = { menuHeaderText: '', }; -class ButtonWithMenu extends PureComponent { +/** + * This component shows a button that has a separation on the right part that shows a pressable caret button if more than 1 + * options was passed or a simple normal button if more than 1 option was passed. + * If there's more than one option, pressing the caret will show a popover menu with all options. + */ +class ButtonWithDropdownMenu extends PureComponent { constructor(props) { super(props); @@ -59,15 +66,33 @@ class ButtonWithMenu extends PureComponent { return ( {this.props.options.length > 1 ? ( - this.props.onPress(event, selectedItem.value)} - onDropdownPress={() => { - this.setMenuVisibility(true); - }} - /> + + + ) : ( - - ) : ( + const selectedItem = props.options[selectedItemIndex]; + return ( + + {props.options.length > 1 ? ( + + + ) : ( +