From faea1c4e239b048fd7ee19eb13c684058d0c80e8 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 5 Jul 2022 15:33:06 +0200 Subject: [PATCH 01/20] New command OpenProfilePage --- src/libs/actions/App.js | 46 +++++++++++++++++++++++ src/pages/settings/InitialSettingsPage.js | 7 +--- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 06596e9003ca..f7576c3a5e75 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -1,3 +1,4 @@ +import _ from 'underscore'; import {AppState, Linking} from 'react-native'; import Onyx from 'react-native-onyx'; import lodashGet from 'lodash/get'; @@ -19,12 +20,15 @@ import NetworkConnection from '../NetworkConnection'; import Navigation from '../Navigation/Navigation'; import ROUTES from '../../ROUTES'; import * as SessionUtils from '../SessionUtils'; +import * as API from '../API'; let currentUserAccountID; +let currentUserEmail = ''; Onyx.connect({ key: ONYXKEYS.SESSION, callback: (val) => { currentUserAccountID = lodashGet(val, 'accountID', ''); + currentUserEmail = lodashGet(val, 'email', ''); }, }); @@ -41,6 +45,12 @@ Onyx.connect({ callback: val => currentPreferredLocale = val || CONST.DEFAULT_LOCALE, }); +let myPersonalDetails; +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS, + callback: val => myPersonalDetails = _.findWhere(val, {isCurrentUser: true}), +}); + /** * @param {String} url */ @@ -192,6 +202,41 @@ function setUpPoliciesAndNavigate(session) { }); } +function openProfilePage() { + const oldTimezoneData = { + ...(myPersonalDetails.timezone || {}) + }; + const newTimezoneData = { + ...oldTimezoneData, + selected: moment.tz.guess(true), + }; + + API.write('OpenProfilePage', { + timezone: { ...newTimezoneData }, + }, { + optimisticData: [{ + onyxMethod: 'merge', + key: ONYXKEYS.PERSONAL_DETAILS, + value: { + [currentUserEmail]: { + timezone: { ...newTimezoneData }, + }, + }, + }], + failureData: [{ + onyxMethod: 'merge', + key: ONYXKEYS.PERSONAL_DETAILS, + value: { + [currentUserEmail]: { + timezone: { ...oldTimezoneData }, + }, + }, + }], + }); + + Navigation.navigate(ROUTES.SETTINGS_PROFILE); +} + // When the app reconnects from being offline, fetch all initialization data NetworkConnection.onReconnect(() => getAppData(true, false)); @@ -203,4 +248,5 @@ export { getAppData, fixAccountAndReloadData, setUpPoliciesAndNavigate, + openProfilePage, }; diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 209c84bb921f..269bfc459fd8 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -19,10 +19,10 @@ import ROUTES from '../../ROUTES'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import compose from '../../libs/compose'; import CONST from '../../CONST'; -import DateUtils from '../../libs/DateUtils'; import Permissions from '../../libs/Permissions'; import networkPropTypes from '../../components/networkPropTypes'; import {withNetwork} from '../../components/OnyxProvider'; +import * as App from '../../libs/actions/App'; const propTypes = { /* Onyx Props */ @@ -86,10 +86,7 @@ const defaultMenuItems = [ { translationKey: 'common.profile', icon: Expensicons.Profile, - action: () => { - DateUtils.updateTimezone(); - Navigation.navigate(ROUTES.SETTINGS_PROFILE); - }, + action: () => { App.openProfilePage(); }, }, { translationKey: 'common.preferences', From 605711594102b048dd5c3827e9b95f2b6c99269a Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 5 Jul 2022 15:34:08 +0200 Subject: [PATCH 02/20] New command UpdateProfile --- src/libs/actions/PersonalDetails.js | 30 +++++++++++++++++++++++ src/pages/settings/Profile/ProfilePage.js | 12 ++++----- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index cc7add84320e..ae09b4f7f041 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -12,6 +12,7 @@ import * as ReportUtils from '../ReportUtils'; import Growl from '../Growl'; import * as Localize from '../Localize'; import Timing from './Timing'; +import * as API from '../API'; let currentUserEmail = ''; Onyx.connect({ @@ -295,6 +296,34 @@ function setPersonalDetails(details, shouldGrowl) { }); } +function updateProfile(firstName, lastName, pronouns, timezone) { + const myPersonalDetails = personalDetails[currentUserEmail]; + API.write('UpdateProfile', { + details: {firstName, lastName, pronouns}, + timezone, + }, { + optimisticData: [{ + onyxMethod: 'merge', + key: ONYXKEYS.PERSONAL_DETAILS, + value: { + [currentUserEmail]: {firstName, lastName, pronouns, timezone}, + }, + }], + failureData: [{ + onyxMethod: 'merge', + key: ONYXKEYS.PERSONAL_DETAILS, + value: { + [currentUserEmail]: { + firstName: myPersonalDetails.firstName, + lastName: myPersonalDetails.lastName, + pronouns: myPersonalDetails.pronouns, + timezone: myPersonalDetails.timeZone, + }, + }, + }], + }); +} + /** * Sets the onyx with the currency list from the network * @returns {Object} @@ -382,4 +411,5 @@ export { getCurrencyList, getMaxCharacterError, extractFirstAndLastNameFromAvailableDetails, + updateProfile, }; diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index fbeb7b69b16c..9b0e5fbca98a 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -179,15 +179,15 @@ class ProfilePage extends Component { this.setState({isAvatarChanged: false}); } - PersonalDetails.setPersonalDetails({ - firstName: this.state.firstName.trim(), - lastName: this.state.lastName.trim(), - pronouns: this.state.pronouns.trim(), - timezone: { + PersonalDetails.updateProfile( + this.state.firstName.trim(), + this.state.lastName.trim(), + this.state.pronouns.trim(), + { automatic: this.state.isAutomaticTimezone, selected: this.state.selectedTimezone, }, - }, true); + ); } validateInputs() { From 50ba9ec12095e767c176111185ec37d38e9fda75 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 5 Jul 2022 15:41:05 +0200 Subject: [PATCH 03/20] New command DeleteUserAvatar (WIP) --- src/libs/actions/PersonalDetails.js | 31 +++++++++++++++++++---- src/pages/settings/Profile/ProfilePage.js | 28 ++++++++++++-------- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index ae09b4f7f041..a5a0e71abab9 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -389,14 +389,35 @@ function setAvatar(file) { /** * Replaces the user's avatar image with a default avatar - * - * @param {String} defaultAvatarURL */ -function deleteAvatar(defaultAvatarURL) { +function deleteAvatar() { + const defaultAvatar = ReportUtils.getDefaultAvatar(this.props.myPersonalDetails.login) + // We don't want to save the default avatar URL in the backend since we don't want to allow // users the option of removing the default avatar, instead we'll save an empty string - DeprecatedAPI.PersonalDetails_Update({details: JSON.stringify({avatar: ''})}); - mergeLocalPersonalDetails({avatar: defaultAvatarURL}); + // DeprecatedAPI.PersonalDetails_Update({details: JSON.stringify({avatar: ''})}); + // mergeLocalPersonalDetails({avatar: defaultAvatarURL}); + + API.write('DeleteUserAvatar', {}, { + optimisticData: [{ + onyxMethod: 'merge', + key: ONYXKEYS.PERSONAL_DETAILS, + value: { + [currentUserEmail]: { + avatar: defaultAvatar, + }, + }, + }], + failureData: [{ + onyxMethod: 'merge', + key: ONYXKEYS.PERSONAL_DETAILS, + value: { + [currentUserEmail]: { + avatar: personalDetails[currentUserEmail].avatar, + }, + }, + }], + }); } export { diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 9b0e5fbca98a..b76e69bf04d9 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -156,6 +156,11 @@ class ProfilePage extends Component { */ updateAvatar(avatar) { this.setState({avatar: _.isUndefined(avatar) ? {uri: ReportUtils.getDefaultAvatar(this.props.myPersonalDetails.login)} : avatar, isAvatarChanged: true}); + + } + + removeAvatar() { + PersonalDetails.deleteAvatar(); } /** @@ -167,17 +172,18 @@ class ProfilePage extends Component { } // Check if the user has modified their avatar - if ((this.props.myPersonalDetails.avatar !== this.state.avatar.uri) && this.state.isAvatarChanged) { - // If the user removed their profile photo, replace it accordingly with the default avatar - if (this.state.avatar.uri.includes('/images/avatars/avatar')) { - PersonalDetails.deleteAvatar(this.state.avatar.uri); - } else { - PersonalDetails.setAvatar(this.state.avatar); - } + // TODO: remove this since it'll be auto-updated instead of waiting for save button?? + // if ((this.props.myPersonalDetails.avatar !== this.state.avatar.uri) && this.state.isAvatarChanged) { + // // If the user removed their profile photo, replace it accordingly with the default avatar + // if (this.state.avatar.uri.includes('/images/avatars/avatar')) { + // PersonalDetails.deleteAvatar(this.state.avatar.uri); + // } else { + // PersonalDetails.setAvatar(this.state.avatar); + // } - // Reset the changed state - this.setState({isAvatarChanged: false}); - } + // // Reset the changed state + // this.setState({isAvatarChanged: false}); + // } PersonalDetails.updateProfile( this.state.firstName.trim(), @@ -234,7 +240,7 @@ class ProfilePage extends Component { isUsingDefaultAvatar={this.state.avatar.uri.includes('/images/avatars/avatar')} avatarURL={this.state.avatar.uri} onImageSelected={this.updateAvatar} - onImageRemoved={this.updateAvatar} + onImageRemoved={this.removeAvatar} anchorPosition={styles.createMenuPositionProfile} size={CONST.AVATAR_SIZE.LARGE} /> From fd71758436319a095ea323b24b1a08dd3bc90638 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Fri, 15 Jul 2022 15:03:26 +0200 Subject: [PATCH 04/20] Remove 'page' from command --- src/libs/actions/App.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 89206aaa41fc..bb823fff1832 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -205,7 +205,7 @@ function setUpPoliciesAndNavigate(session) { }); } -function openProfilePage() { +function openProfile() { const oldTimezoneData = { ...(myPersonalDetails.timezone || {}) }; @@ -214,7 +214,7 @@ function openProfilePage() { selected: moment.tz.guess(true), }; - API.write('OpenProfilePage', { + API.write('OpenProfile', { timezone: { ...newTimezoneData }, }, { optimisticData: [{ @@ -253,6 +253,6 @@ export { getAppData, fixAccountAndReloadData, setUpPoliciesAndNavigate, - openProfilePage, + openProfile, openApp, }; From 9f8d7f0788bf6be64fb808f8ecb89868aab6eaff Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Fri, 15 Jul 2022 15:13:47 +0200 Subject: [PATCH 05/20] Clean up logic for deleting user avatar --- src/libs/actions/PersonalDetails.js | 5 ----- src/pages/settings/Profile/ProfilePage.js | 24 +++++++++-------------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index a5a0e71abab9..5fc1aa8a48f7 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -393,11 +393,6 @@ function setAvatar(file) { function deleteAvatar() { const defaultAvatar = ReportUtils.getDefaultAvatar(this.props.myPersonalDetails.login) - // We don't want to save the default avatar URL in the backend since we don't want to allow - // users the option of removing the default avatar, instead we'll save an empty string - // DeprecatedAPI.PersonalDetails_Update({details: JSON.stringify({avatar: ''})}); - // mergeLocalPersonalDetails({avatar: defaultAvatarURL}); - API.write('DeleteUserAvatar', {}, { optimisticData: [{ onyxMethod: 'merge', diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index b76e69bf04d9..5ae76d70cd59 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -68,8 +68,6 @@ class ProfilePage extends Component { constructor(props) { super(props); - this.defaultAvatar = ReportUtils.getDefaultAvatar(this.props.myPersonalDetails.login); - this.state = { firstName: props.myPersonalDetails.firstName, hasFirstNameError: false, @@ -155,10 +153,12 @@ class ProfilePage extends Component { * @param {Object} avatar */ updateAvatar(avatar) { - this.setState({avatar: _.isUndefined(avatar) ? {uri: ReportUtils.getDefaultAvatar(this.props.myPersonalDetails.login)} : avatar, isAvatarChanged: true}); - + this.setState({avatar, isAvatarChanged: true}); } + /** + * Replaces the user's current avatar image with a default avatar. + */ removeAvatar() { PersonalDetails.deleteAvatar(); } @@ -172,18 +172,12 @@ class ProfilePage extends Component { } // Check if the user has modified their avatar - // TODO: remove this since it'll be auto-updated instead of waiting for save button?? - // if ((this.props.myPersonalDetails.avatar !== this.state.avatar.uri) && this.state.isAvatarChanged) { - // // If the user removed their profile photo, replace it accordingly with the default avatar - // if (this.state.avatar.uri.includes('/images/avatars/avatar')) { - // PersonalDetails.deleteAvatar(this.state.avatar.uri); - // } else { - // PersonalDetails.setAvatar(this.state.avatar); - // } + if ((this.props.myPersonalDetails.avatar !== this.state.avatar.uri) && this.state.isAvatarChanged) { + PersonalDetails.setAvatar(this.state.avatar); - // // Reset the changed state - // this.setState({isAvatarChanged: false}); - // } + // Reset the changed state + this.setState({isAvatarChanged: false}); + } PersonalDetails.updateProfile( this.state.firstName.trim(), From 047276836ad76b68e610ffa09440d481c42c0b54 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Fri, 15 Jul 2022 17:26:32 +0200 Subject: [PATCH 06/20] Clean up logic and lint --- src/libs/actions/App.js | 18 +++++------------- src/libs/actions/PersonalDetails.js | 9 +++++++-- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index bb823fff1832..4bbbe386c5ba 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -1,3 +1,4 @@ +import moment from 'moment-timezone'; import _ from 'underscore'; import {AppState, Linking} from 'react-native'; import Onyx from 'react-native-onyx'; @@ -19,7 +20,6 @@ import NetworkConnection from '../NetworkConnection'; import Navigation from '../Navigation/Navigation'; import ROUTES from '../../ROUTES'; import * as SessionUtils from '../SessionUtils'; -import * as API from '../API'; let currentUserAccountID; let currentUserEmail = ''; @@ -38,12 +38,6 @@ Onyx.connect({ initWithStoredValues: false, }); -let currentPreferredLocale; -Onyx.connect({ - key: ONYXKEYS.NVP_PREFERRED_LOCALE, - callback: val => currentPreferredLocale = val || CONST.DEFAULT_LOCALE, -}); - let myPersonalDetails; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS, @@ -206,23 +200,21 @@ function setUpPoliciesAndNavigate(session) { } function openProfile() { - const oldTimezoneData = { - ...(myPersonalDetails.timezone || {}) - }; + const oldTimezoneData = myPersonalDetails.timezone || {}; const newTimezoneData = { ...oldTimezoneData, selected: moment.tz.guess(true), }; API.write('OpenProfile', { - timezone: { ...newTimezoneData }, + timezone: newTimezoneData, }, { optimisticData: [{ onyxMethod: 'merge', key: ONYXKEYS.PERSONAL_DETAILS, value: { [currentUserEmail]: { - timezone: { ...newTimezoneData }, + timezone: newTimezoneData, }, }, }], @@ -231,7 +223,7 @@ function openProfile() { key: ONYXKEYS.PERSONAL_DETAILS, value: { [currentUserEmail]: { - timezone: { ...oldTimezoneData }, + timezone: oldTimezoneData, }, }, }], diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index 5fc1aa8a48f7..193d975cb111 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -306,7 +306,12 @@ function updateProfile(firstName, lastName, pronouns, timezone) { onyxMethod: 'merge', key: ONYXKEYS.PERSONAL_DETAILS, value: { - [currentUserEmail]: {firstName, lastName, pronouns, timezone}, + [currentUserEmail]: { + firstName, + lastName, + pronouns, + timezone, + }, }, }], failureData: [{ @@ -391,7 +396,7 @@ function setAvatar(file) { * Replaces the user's avatar image with a default avatar */ function deleteAvatar() { - const defaultAvatar = ReportUtils.getDefaultAvatar(this.props.myPersonalDetails.login) + const defaultAvatar = ReportUtils.getDefaultAvatar(this.props.myPersonalDetails.login); API.write('DeleteUserAvatar', {}, { optimisticData: [{ From 35a24ed5e27a4eeb229f5dd137f843baaf3e28cc Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Fri, 15 Jul 2022 19:10:53 +0200 Subject: [PATCH 07/20] Fix new openProfile function name --- src/pages/settings/InitialSettingsPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 269bfc459fd8..398b624130d2 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -86,7 +86,7 @@ const defaultMenuItems = [ { translationKey: 'common.profile', icon: Expensicons.Profile, - action: () => { App.openProfilePage(); }, + action: () => { App.openProfile(); }, }, { translationKey: 'common.preferences', From 70ce9133b7c934e938f7bd7ecfe38ee2bf2fc31e Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Fri, 15 Jul 2022 19:11:33 +0200 Subject: [PATCH 08/20] Use onyx consts, stringify data --- src/libs/actions/App.js | 12 ++++++------ src/libs/actions/PersonalDetails.js | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 4bbbe386c5ba..b8e653d9c4fa 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -41,7 +41,7 @@ Onyx.connect({ let myPersonalDetails; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS, - callback: val => myPersonalDetails = _.findWhere(val, {isCurrentUser: true}), + callback: val => myPersonalDetails = _.findWhere(val, {login: currentUserEmail}), }); /** @@ -64,7 +64,7 @@ function setLocale(locale) { // Optimistically change preferred locale const optimisticData = [ { - onyxMethod: 'merge', + onyxMethod: CONST.ONYX.METHOD.MERGE, key: ONYXKEYS.NVP_PREFERRED_LOCALE, value: locale, }, @@ -202,15 +202,15 @@ function setUpPoliciesAndNavigate(session) { function openProfile() { const oldTimezoneData = myPersonalDetails.timezone || {}; const newTimezoneData = { - ...oldTimezoneData, + automatic: oldTimezoneData.automatic || true, selected: moment.tz.guess(true), }; API.write('OpenProfile', { - timezone: newTimezoneData, + timezone: JSON.stringify(newTimezoneData), }, { optimisticData: [{ - onyxMethod: 'merge', + onyxMethod: CONST.ONYX.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS, value: { [currentUserEmail]: { @@ -219,7 +219,7 @@ function openProfile() { }, }], failureData: [{ - onyxMethod: 'merge', + onyxMethod: CONST.ONYX.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS, value: { [currentUserEmail]: { diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index 193d975cb111..e38c970f3873 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -303,7 +303,7 @@ function updateProfile(firstName, lastName, pronouns, timezone) { timezone, }, { optimisticData: [{ - onyxMethod: 'merge', + onyxMethod: CONST.ONYX.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS, value: { [currentUserEmail]: { @@ -315,7 +315,7 @@ function updateProfile(firstName, lastName, pronouns, timezone) { }, }], failureData: [{ - onyxMethod: 'merge', + onyxMethod: CONST.ONYX.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS, value: { [currentUserEmail]: { @@ -400,7 +400,7 @@ function deleteAvatar() { API.write('DeleteUserAvatar', {}, { optimisticData: [{ - onyxMethod: 'merge', + onyxMethod: CONST.ONYX.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS, value: { [currentUserEmail]: { @@ -409,7 +409,7 @@ function deleteAvatar() { }, }], failureData: [{ - onyxMethod: 'merge', + onyxMethod: CONST.ONYX.METHOD.MERGE, key: ONYXKEYS.PERSONAL_DETAILS, value: { [currentUserEmail]: { From f8beb46510c2a78471f51cf68fe224f45cea5aaa Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Fri, 15 Jul 2022 19:24:39 +0200 Subject: [PATCH 09/20] Stringify data sent to server & fix default avatar --- src/libs/actions/PersonalDetails.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index e38c970f3873..3f09d33ed373 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -299,8 +299,8 @@ function setPersonalDetails(details, shouldGrowl) { function updateProfile(firstName, lastName, pronouns, timezone) { const myPersonalDetails = personalDetails[currentUserEmail]; API.write('UpdateProfile', { - details: {firstName, lastName, pronouns}, - timezone, + details: JSON.stringify({firstName, lastName, pronouns}), + timezone: JSON.stringify(timezone), }, { optimisticData: [{ onyxMethod: CONST.ONYX.METHOD.MERGE, @@ -396,7 +396,7 @@ function setAvatar(file) { * Replaces the user's avatar image with a default avatar */ function deleteAvatar() { - const defaultAvatar = ReportUtils.getDefaultAvatar(this.props.myPersonalDetails.login); + const defaultAvatar = ReportUtils.getDefaultAvatar(currentUserEmail); API.write('DeleteUserAvatar', {}, { optimisticData: [{ From 3ebc476dea0a14eead5d6331c50cdb8730dcdabe Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 18 Jul 2022 16:56:34 +0200 Subject: [PATCH 10/20] Clean up logic getting my personal details --- src/libs/actions/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 4082c09fa99e..c2ef8e75ea66 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -41,7 +41,7 @@ Onyx.connect({ let myPersonalDetails; Onyx.connect({ key: ONYXKEYS.PERSONAL_DETAILS, - callback: val => myPersonalDetails = _.findWhere(val, {login: currentUserEmail}), + callback: val => myPersonalDetails = val[currentUserEmail], }); /** From 1c23ade2adb398bec4d96d3e69fc1db7cb0a1952 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 18 Jul 2022 16:57:02 +0200 Subject: [PATCH 11/20] Update state if avatar prop changes --- src/pages/settings/Profile/ProfilePage.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 44b9a04a9a47..f65081bc6943 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -67,11 +67,11 @@ class ProfilePage extends Component { super(props); this.state = { - firstName: this.props.currentUserPersonalDetails.firstName, + firstName: this.props.currentUserPersonalDetails.firstName || '', hasFirstNameError: false, - lastName: this.props.currentUserPersonalDetails.lastName, + lastName: this.props.currentUserPersonalDetails.lastName || '', hasLastNameError: false, - pronouns: this.props.currentUserPersonalDetails.pronouns, + pronouns: this.props.currentUserPersonalDetails.pronouns || '', hasPronounError: false, hasSelfSelectedPronouns: !_.isEmpty(this.props.currentUserPersonalDetails.pronouns) && !this.props.currentUserPersonalDetails.pronouns.startsWith(CONST.PRONOUNS.PREFIX), selectedTimezone: lodashGet(this.props.currentUserPersonalDetails.timezone, 'selected', CONST.DEFAULT_TIME_ZONE.selected), @@ -96,6 +96,12 @@ class ProfilePage extends Component { stateToUpdate = {...stateToUpdate, logins: this.getLogins(this.props.loginList)}; } + // Update avatar in state if changed in props + const newAvatar = lodashGet(this.props.currentUserPersonalDetails, 'avatar'); + if (newAvatar !== lodashGet(prevProps.currentUserPersonalDetails, 'avatar')) { + stateToUpdate = {...stateToUpdate, avatar: {uri: newAvatar}}; + } + if (_.isEmpty(stateToUpdate)) { return; } @@ -170,7 +176,7 @@ class ProfilePage extends Component { } // Check if the user has modified their avatar - if ((this.props.myPersonalDetails.avatar !== this.state.avatar.uri) && this.state.isAvatarChanged) { + if ((this.props.currentUserPersonalDetails.avatar !== this.state.avatar.uri) && this.state.isAvatarChanged) { PersonalDetails.setAvatar(this.state.avatar); // Reset the changed state From 259e877860730b8cd462c6a00339dc21feb174db Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 19 Jul 2022 16:09:48 +0200 Subject: [PATCH 12/20] Remove unused import --- src/libs/actions/App.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 72b560e9ab17..fc0be6ac6bdc 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -1,5 +1,4 @@ import moment from 'moment-timezone'; -import _ from 'underscore'; import {AppState, Linking} from 'react-native'; import Onyx from 'react-native-onyx'; import lodashGet from 'lodash/get'; From 0ec78f22c36c3afe1af525500f7235003cc94461 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Tue, 19 Jul 2022 16:24:29 +0200 Subject: [PATCH 13/20] Remove dupe import --- src/libs/actions/PersonalDetails.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index aa9cc9db5bde..7a663b8268a4 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -13,7 +13,6 @@ import * as ReportUtils from '../ReportUtils'; import Growl from '../Growl'; import * as Localize from '../Localize'; import Timing from './Timing'; -import * as API from '../API'; let currentUserEmail = ''; Onyx.connect({ From 9f519732294acde80a69393deec4f6d7dd508ace Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Thu, 21 Jul 2022 15:11:46 +0200 Subject: [PATCH 14/20] Update default avatar in state better --- src/pages/settings/Profile/ProfilePage.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index ebdbfbf5e0a2..10f26bb79f32 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -95,12 +95,6 @@ class ProfilePage extends Component { stateToUpdate = {...stateToUpdate, logins: this.getLogins(this.props.loginList)}; } - // Update avatar in state if changed in props - const newAvatar = lodashGet(this.props.currentUserPersonalDetails, 'avatar'); - if (newAvatar !== lodashGet(prevProps.currentUserPersonalDetails, 'avatar')) { - stateToUpdate = {...stateToUpdate, avatar: {uri: newAvatar}}; - } - if (_.isEmpty(stateToUpdate)) { return; } @@ -164,6 +158,7 @@ class ProfilePage extends Component { */ removeAvatar() { PersonalDetails.deleteAvatar(); + this.setState({avatar: ReportUtils.getDefaultAvatar(this.props.currentUserPersonalDetails.login)}); } /** From 8b6dd2a14225fdb19ff62f4600353b18a7e809ab Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Thu, 28 Jul 2022 12:10:31 +0200 Subject: [PATCH 15/20] Clean up lodashGets, adding defaults where needed --- src/pages/settings/Profile/ProfilePage.js | 34 ++++++++++++----------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index dd0cbe553f0c..2d01dfbce6c0 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -66,18 +66,19 @@ class ProfilePage extends Component { constructor(props) { super(props); + const currentUserDetails = this.props.currentUserPersonalDetails || {}; this.state = { - firstName: this.props.currentUserPersonalDetails.firstName || '', + firstName: currentUserDetails.firstName || '', hasFirstNameError: false, - lastName: this.props.currentUserPersonalDetails.lastName || '', + lastName: currentUserDetails.lastName || '', hasLastNameError: false, - pronouns: this.props.currentUserPersonalDetails.pronouns || '', + pronouns: currentUserDetails.pronouns || '', hasPronounError: false, - hasSelfSelectedPronouns: !_.isEmpty(this.props.currentUserPersonalDetails.pronouns) && !this.props.currentUserPersonalDetails.pronouns.startsWith(CONST.PRONOUNS.PREFIX), - selectedTimezone: lodashGet(this.props.currentUserPersonalDetails, 'timezone.selected', CONST.DEFAULT_TIME_ZONE.selected), - isAutomaticTimezone: lodashGet(this.props.currentUserPersonalDetails, 'timezone.automatic', CONST.DEFAULT_TIME_ZONE.automatic), + hasSelfSelectedPronouns: !(currentUserDetails.pronouns || '').startsWith(CONST.PRONOUNS.PREFIX), + selectedTimezone: lodashGet(currentUserDetails, 'timezone.selected', CONST.DEFAULT_TIME_ZONE.selected), + isAutomaticTimezone: lodashGet(currentUserDetails, 'timezone.automatic', CONST.DEFAULT_TIME_ZONE.automatic), logins: this.getLogins(props.loginList), - avatar: {uri: lodashGet(this.props.currentUserPersonalDetails, 'avatar', ReportUtils.getDefaultAvatar(this.props.currentUserPersonalDetails.login))}, + avatar: {uri: currentUserDetails.avatar || ReportUtils.getDefaultAvatar(currentUserDetails.login)}, isAvatarChanged: false, }; @@ -128,7 +129,7 @@ class ProfilePage extends Component { const login = Str.removeSMSDomain(currentLogin.partnerUserID); // If there's already a login type that's validated and/or currentLogin isn't valid then return early - if ((login !== this.props.currentUserPersonalDetails.login) && !_.isEmpty(logins[type]) + if ((login !== lodashGet(this.props.currentUserPersonalDetails, 'login')) && !_.isEmpty(logins[type]) && (logins[type].validatedDate || !currentLogin.validatedDate)) { return logins; } @@ -171,7 +172,7 @@ class ProfilePage extends Component { } // Check if the user has modified their avatar - if ((this.props.currentUserPersonalDetails.avatar !== this.state.avatar.uri) && this.state.isAvatarChanged) { + if ((lodashGet(this.props.currentUserPersonalDetails, 'avatar') !== this.state.avatar.uri) && this.state.isAvatarChanged) { PersonalDetails.setAvatar(this.state.avatar); // Reset the changed state @@ -209,12 +210,13 @@ class ProfilePage extends Component { })); // Disables button if none of the form values have changed - const isButtonDisabled = (this.props.currentUserPersonalDetails.firstName === this.state.firstName.trim()) - && (this.props.currentUserPersonalDetails.lastName === this.state.lastName.trim()) - && (this.props.currentUserPersonalDetails.timezone.selected === this.state.selectedTimezone) - && (this.props.currentUserPersonalDetails.timezone.automatic === this.state.isAutomaticTimezone) - && (this.props.currentUserPersonalDetails.pronouns === this.state.pronouns.trim()) - && (!this.state.isAvatarChanged || this.props.currentUserPersonalDetails.avatarUploading); + const currentUserDetails = this.props.currentUserPersonalDetails || {}; + const isButtonDisabled = (currentUserDetails.firstName === this.state.firstName.trim()) + && (currentUserDetails.lastName === this.state.lastName.trim()) + && (lodashGet(currentUserDetails, 'timezone.selected') === this.state.selectedTimezone) + && (lodashGet(currentUserDetails, 'timezone.automatic') === this.state.isAutomaticTimezone) + && (currentUserDetails.pronouns === this.state.pronouns.trim()) + && (!this.state.isAvatarChanged || currentUserDetails.avatarUploading); const pronounsPickerValue = this.state.hasSelfSelectedPronouns ? CONST.PRONOUNS.SELF_SELECT : this.state.pronouns; @@ -229,7 +231,7 @@ class ProfilePage extends Component { /> Date: Thu, 28 Jul 2022 12:11:02 +0200 Subject: [PATCH 16/20] Fix deleteAvatar, rename --- src/pages/settings/Profile/ProfilePage.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 2d01dfbce6c0..6e0a9b317f49 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -87,6 +87,7 @@ class ProfilePage extends Component { this.updatePersonalDetails = this.updatePersonalDetails.bind(this); this.validateInputs = this.validateInputs.bind(this); this.updateAvatar = this.updateAvatar.bind(this); + this.deleteAvatar = this.deleteAvatar.bind(this); } componentDidUpdate(prevProps) { @@ -158,9 +159,9 @@ class ProfilePage extends Component { /** * Replaces the user's current avatar image with a default avatar. */ - removeAvatar() { + deleteAvatar() { PersonalDetails.deleteAvatar(); - this.setState({avatar: ReportUtils.getDefaultAvatar(this.props.currentUserPersonalDetails.login)}); + this.setState({avatar: {uri: ReportUtils.getDefaultAvatar(lodashGet(this.props.currentUserPersonalDetails, 'login'))}}); } /** @@ -235,7 +236,7 @@ class ProfilePage extends Component { isUsingDefaultAvatar={this.state.avatar.uri.includes('/images/avatars/avatar')} avatarURL={this.state.avatar.uri} onImageSelected={this.updateAvatar} - onImageRemoved={this.removeAvatar} + onImageRemoved={this.deleteAvatar} anchorPosition={styles.createMenuPositionProfile} size={CONST.AVATAR_SIZE.LARGE} /> From 4aeee66bd1d6f1f348be31158110b980e827e84c Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Thu, 28 Jul 2022 12:12:50 +0200 Subject: [PATCH 17/20] Fix logic to keep timezone.automatic same --- src/libs/actions/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/App.js b/src/libs/actions/App.js index 95753cc031a5..df7e70f6c6e0 100644 --- a/src/libs/actions/App.js +++ b/src/libs/actions/App.js @@ -218,7 +218,7 @@ function setUpPoliciesAndNavigate(session) { function openProfile() { const oldTimezoneData = myPersonalDetails.timezone || {}; const newTimezoneData = { - automatic: oldTimezoneData.automatic || true, + automatic: lodashGet(oldTimezoneData, 'automatic', true), selected: moment.tz.guess(true), }; From 3792cc87398961fa4c875ac0e5808ca378d9bb0d Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Fri, 29 Jul 2022 13:09:12 +0200 Subject: [PATCH 18/20] Update displayName in onyx when name changes --- src/libs/actions/PersonalDetails.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index 9a28c1eec076..6fa15db4c291 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -277,6 +277,10 @@ function updateProfile(firstName, lastName, pronouns, timezone) { lastName, pronouns, timezone, + displayName: getDisplayName(currentUserEmail, { + firstName, + lastName, + }), }, }, }], @@ -289,6 +293,7 @@ function updateProfile(firstName, lastName, pronouns, timezone) { lastName: myPersonalDetails.lastName, pronouns: myPersonalDetails.pronouns, timezone: myPersonalDetails.timeZone, + displayName: myPersonalDetails.displayName, }, }, }], From d37051b2bafb005e9c64fbabe1e8a83e220fa83c Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Fri, 29 Jul 2022 15:04:32 +0200 Subject: [PATCH 19/20] Send all personal details as own params --- src/libs/actions/PersonalDetails.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/actions/PersonalDetails.js b/src/libs/actions/PersonalDetails.js index 6fa15db4c291..4f40b1724429 100644 --- a/src/libs/actions/PersonalDetails.js +++ b/src/libs/actions/PersonalDetails.js @@ -265,7 +265,11 @@ function setPersonalDetails(details, shouldGrowl) { function updateProfile(firstName, lastName, pronouns, timezone) { const myPersonalDetails = personalDetails[currentUserEmail]; API.write('UpdateProfile', { + // 'details' is an old param that will be removed in https://github.com/Expensify/Expensify/issues/220321 details: JSON.stringify({firstName, lastName, pronouns}), + firstName, + lastName, + pronouns, timezone: JSON.stringify(timezone), }, { optimisticData: [{ From 40b05a4e378c8fc897b35b45ccb44b3faf48b719 Mon Sep 17 00:00:00 2001 From: Alex Beaman Date: Mon, 1 Aug 2022 12:44:35 +0200 Subject: [PATCH 20/20] Add back important check --- src/pages/settings/Profile/ProfilePage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/Profile/ProfilePage.js b/src/pages/settings/Profile/ProfilePage.js index 6e0a9b317f49..3dadbc80f92d 100755 --- a/src/pages/settings/Profile/ProfilePage.js +++ b/src/pages/settings/Profile/ProfilePage.js @@ -74,7 +74,7 @@ class ProfilePage extends Component { hasLastNameError: false, pronouns: currentUserDetails.pronouns || '', hasPronounError: false, - hasSelfSelectedPronouns: !(currentUserDetails.pronouns || '').startsWith(CONST.PRONOUNS.PREFIX), + hasSelfSelectedPronouns: !_.isEmpty(currentUserDetails.pronouns) && !currentUserDetails.pronouns.startsWith(CONST.PRONOUNS.PREFIX), selectedTimezone: lodashGet(currentUserDetails, 'timezone.selected', CONST.DEFAULT_TIME_ZONE.selected), isAutomaticTimezone: lodashGet(currentUserDetails, 'timezone.automatic', CONST.DEFAULT_TIME_ZONE.automatic), logins: this.getLogins(props.loginList),