Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[User Settings] Add new Preferences view #1716

Merged
merged 33 commits into from
Mar 23, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ca2cace
add expensifyNewsStatus toggle to preferences page
Maftalion Mar 10, 2021
ca98bf2
Merge branch 'master' into matt-preferences-toggle
Maftalion Mar 11, 2021
1edbb1c
Merge branch 'master' into matt-preferences-toggle
Maftalion Mar 11, 2021
a27b59d
add toggle library, revert accidental change
Maftalion Mar 11, 2021
fc03aee
package lock update
Maftalion Mar 11, 2021
f0475a0
add proptype comments
Maftalion Mar 11, 2021
8ffc704
Revert "package lock update"
Maftalion Mar 11, 2021
a1c53b7
style/text updates
Maftalion Mar 11, 2021
abb27a2
style/small updates
Maftalion Mar 12, 2021
7636f31
Merge branch 'master' into matt-preferences-toggle
Maftalion Mar 15, 2021
dc4dbed
update to go based off onyx prop
Maftalion Mar 16, 2021
8703977
lint error
Maftalion Mar 16, 2021
b332485
add user fetch
Maftalion Mar 17, 2021
fe50d3e
update switch styles
Maftalion Mar 17, 2021
802d076
update switchThumb style
Maftalion Mar 17, 2021
ddc8723
Merge branch 'master' into matt-preferences-toggle
Maftalion Mar 17, 2021
f48421d
Merge branch 'master' into matt-preferences-toggle
Maftalion Mar 17, 2021
67bba32
fix merge conflict
Maftalion Mar 17, 2021
38f1bc1
remove func wrapper
Maftalion Mar 17, 2021
6c4f7c3
fix whitespace
Maftalion Mar 17, 2021
a4e0640
remove shadow from thumb
Maftalion Mar 17, 2021
b7bd1da
Merge branch 'master' into matt-preferences-toggle
Maftalion Mar 18, 2021
f33775d
create switch component, remove library
Maftalion Mar 18, 2021
6732e6c
lint
Maftalion Mar 18, 2021
dcec672
Merge branch 'master' into matt-preferences-toggle
Maftalion Mar 19, 2021
942c675
remove line
Maftalion Mar 19, 2021
ff754a6
add missing style
Maftalion Mar 19, 2021
613b452
Merge branch 'master' into matt-preferences-toggle
Maftalion Mar 19, 2021
7acae50
Merge branch 'master' into matt-preferences-toggle
Maftalion Mar 20, 2021
5f92090
Merge branch 'master' into matt-preferences-toggle
Maftalion Mar 20, 2021
cfc6c56
remove package
Maftalion Mar 20, 2021
0976483
fix conflict
Maftalion Mar 20, 2021
41ae751
Merge branch 'master' into matt-preferences-toggle
Maftalion Mar 23, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/webpack/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const webpackConfig = {
*/
exclude: [
// eslint-disable-next-line max-len
/node_modules\/(?!(react-native-animatable|react-native-picker-select|react-native-web|@react-native-picker|react-native-modal|react-native-webview|react-native-onyx)\/).*|\.native\.js$/,
/node_modules\/(?!(toggle-switch-react-native|react-native-animatable|react-native-picker-select|react-native-web|@react-native-picker|react-native-modal|react-native-webview|react-native-onyx)\/).*|\.native\.js$/,
platformExclude,
],
},
Expand Down
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"react-web-config": "^1.0.0",
"rn-fetch-blob": "^0.12.0",
"save": "^2.4.0",
"toggle-switch-react-native": "^3.2.0",
"underscore": "^1.10.2",
"urbanairship-react-native": "^10.0.0"
},
Expand Down
7 changes: 4 additions & 3 deletions src/pages/home/HomePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import KeyboardShortcut from '../../libs/KeyboardShortcut';
import {redirect} from '../../libs/actions/App';
import RightDockedModal from '../../components/RightDockedModal';
import IOUModal from '../iou/IOUModal';
import {getBetas} from '../../libs/actions/User';
import * as User from '../../libs/actions/User';
import NameValuePair from '../../libs/actions/NameValuePair';

const propTypes = {
Expand Down Expand Up @@ -63,7 +63,8 @@ class HomePage extends PureComponent {
NameValuePair.get(CONST.NVP.PRIORITY_MODE, ONYXKEYS.PRIORITY_MODE, 'default');
PersonalDetails.fetch();
PersonalDetails.fetchTimezone();
getBetas();
User.getBetas();
User.fetch();
fetchAllReports(true, true);
fetchCountryCodeByRequestIP();
UnreadIndicatorUpdater.listenForReportChanges();
Expand All @@ -77,7 +78,7 @@ class HomePage extends PureComponent {
}
PersonalDetails.fetch();
PersonalDetails.fetchTimezone();
getBetas();
User.getBetas();
Maftalion marked this conversation as resolved.
Show resolved Hide resolved
}, 1000 * 60 * 30);

Timing.end(CONST.TIMING.HOMEPAGE_INITIAL_RENDER);
Expand Down
114 changes: 78 additions & 36 deletions src/pages/settings/PreferencesPage.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
import React from 'react';
import React, {Component} from 'react';
import {View} from 'react-native';
import RNPickerSelect from 'react-native-picker-select';
import {withOnyx} from 'react-native-onyx';
import PropTypes from 'prop-types';
import ToggleSwitch from 'toggle-switch-react-native';

import HeaderWithCloseButton from '../../components/HeaderWithCloseButton';
import {redirect} from '../../libs/actions/App';
import HeaderGap from '../../components/HeaderGap';
import ROUTES from '../../ROUTES';
import ONYXKEYS from '../../ONYXKEYS';
import styles from '../../styles/styles';
import colors from '../../styles/colors';
import Text from '../../components/Text';
import Icon from '../../components/Icon';
import NameValuePair from '../../libs/actions/NameValuePair';
import CONST from '../../CONST';
import {DownArrow} from '../../components/Icon/Expensicons';
import {setExpensifyNewsStatus} from '../../libs/actions/User';

const propTypes = {
// The chat priority mode
priorityMode: PropTypes.string,

// The details about the user that is signed in
user: PropTypes.shape({
// Whether or not the user is subscribed to news updates
expensifyNewsStatus: PropTypes.bool,
}),
};

const defaultProps = {
priorityMode: CONST.PRIORITY_MODE.DEFAULT,
user: {},
};

const priorityModes = {
Expand All @@ -38,42 +48,71 @@ const priorityModes = {
},
};

const PreferencesPage = ({priorityMode}) => (
<>
<HeaderGap />
<HeaderWithCloseButton
title="Preferences"
shouldShowBackButton
onBackButtonPress={() => redirect(ROUTES.SETTINGS)}
onCloseButtonPress={() => redirect(ROUTES.HOME)}
/>
<View style={styles.pageWrapper}>
<View style={[styles.settingsPageBody, styles.mb6]}>
<Text style={[styles.formLabel]} numberOfLines={1}>
Priority Mode
</Text>
<View style={[styles.mb2]}>
{/* empty object in placeholder below to prevent default */}
{/* placeholder from appearing as a selection option. */}
<RNPickerSelect
onValueChange={
mode => NameValuePair.set(CONST.NVP.PRIORITY_MODE, mode, ONYXKEYS.PRIORITY_MODE)
}
items={Object.values(priorityModes)}
style={styles.picker}
useNativeAndroidPickerStyle={false}
placeholder={{}}
value={priorityMode}
Icon={() => <Icon src={DownArrow} />}
/>
class PreferencesPage extends Component {
Copy link
Contributor

Choose a reason for hiding this comment

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

why did you convert this from a functional component? couldn't we keep it as a functional component and pass user as a prop like we were doing before with priorityMode?

The state you've added should be handled already by Onyx so as long as the isOn is bound to the prop user.expensifyNewsStatus I'm pretty sure it will update properly without the need for this to be a regular/pure component and reference state.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey, so this was the direction I went down to follow the "offline first" approach. Basically if it was going purely based off the user.expensifyNewsStatus then it would seem unresponsive/slow for users with no or slow connections. I guess a nice follow up to handle the errors would be to show a toast & reset back to the initial onyx value but I believe that's out of scope.

I can also revert it back to a func component and go solely off the prop if needed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm I see that does make sense now but that makes me question whether the User.setExpensifyNewsStatus should first be setting the data in onyx and then calling the API.

cc @tgolen what are your thoughts on this?

Copy link
Contributor

Choose a reason for hiding this comment

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

I like the idea of optimistically setting the value in Onyx before calling the API, yeah. Then if the API fails, the Onyx data can be reset.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I'm not sure I agree with this, as I think we're going to end up with a lot of duplicated code with having to handle the case where we don't get a response at all and when we get a failed response (see here). I get that we want to keep that offline first functionality though, so I'm not really sure which is the best route to go.

@marcaaron this didn't get brought up when I first added User.setExpensifyNewsStatus so I'm curious to see if you have opinions on this.

Copy link
Contributor

Choose a reason for hiding this comment

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

I like the optimistic approach here as well. Only thought is that we should consider updating the value of this data with the data returned from the response instead of relying purely on response codes - but it's probably OK with this particular setting being a boolean and all.

I'm also inclined to wait and see how many examples like this we end up with (though I agree it will be a lot eventually). Would love to see a more elegant or baked in approach to this optimistic response pattern soon!

constructor(props) {
super(props);
this.state = {
isNewsStatusEnabled: !(this.props.user.expensifyNewsStatus === false),
};
this.toggleNewsStatus = this.toggleNewsStatus.bind(this);
}

toggleNewsStatus(toggle) {
Maftalion marked this conversation as resolved.
Show resolved Hide resolved
this.setState({isNewsStatusEnabled: toggle});
Maftalion marked this conversation as resolved.
Show resolved Hide resolved
setExpensifyNewsStatus(toggle);
}

render() {
return (
<>
<HeaderGap />
<HeaderWithCloseButton
title="Preferences"
shouldShowBackButton
onBackButtonPress={() => redirect(ROUTES.SETTINGS)}
onCloseButtonPress={() => redirect(ROUTES.HOME)}
/>
<View style={styles.pageWrapper}>
<View style={[styles.settingsPageBody, styles.mb6]}>
<View style={[styles.flexRow, styles.mb5, styles.justifyContentBetween]}>
Maftalion marked this conversation as resolved.
Show resolved Hide resolved
<Text>
Relevent feature updates and
Maftalion marked this conversation as resolved.
Show resolved Hide resolved
{'\n'}
Maftalion marked this conversation as resolved.
Show resolved Hide resolved
Expensify news
</Text>
<ToggleSwitch
isOn={this.state.isNewsStatusEnabled}
onColor={colors.green}
onToggle={this.toggleNewsStatus}
/>
</View>
<Text style={[styles.formLabel]} numberOfLines={1}>
Priority Mode
</Text>
<View style={[styles.mb2]}>
{/* empty object in placeholder below to prevent default */}
{/* placeholder from appearing as a selection option. */}
<RNPickerSelect
onValueChange={
mode => NameValuePair.set(CONST.NVP.PRIORITY_MODE, mode, ONYXKEYS.PRIORITY_MODE)
bondydaa marked this conversation as resolved.
Show resolved Hide resolved
}
items={Object.values(priorityModes)}
style={styles.picker}
useNativeAndroidPickerStyle={false}
placeholder={{}}
value={this.props.priorityMode}
Icon={() => <Icon src={DownArrow} />}
/>
</View>
<Text style={[styles.textLabel, styles.colorMuted]}>
{priorityModes[this.props.priorityMode].description}
</Text>
</View>
</View>
<Text style={[styles.textLabel, styles.colorMuted]}>
{priorityModes[priorityMode].description}
</Text>
</View>
</View>
</>
);
</>
);
}
}

PreferencesPage.propTypes = propTypes;
PreferencesPage.defaultProps = defaultProps;
Expand All @@ -83,4 +122,7 @@ export default withOnyx({
priorityMode: {
key: ONYXKEYS.PRIORITY_MODE,
},
user: {
key: ONYXKEYS.USER,
},
})(PreferencesPage);