Improved settings screen elements

This commit is contained in:
Arnaud Vergnet 2020-05-01 15:59:47 +02:00
parent 517e75f4b9
commit 0b7191887d
4 changed files with 111 additions and 45 deletions

View file

@ -22,6 +22,7 @@
"@nartc/react-native-barcode-mask": "^1.1.9", "@nartc/react-native-barcode-mask": "^1.1.9",
"@react-native-community/masked-view": "0.1.6", "@react-native-community/masked-view": "0.1.6",
"@react-native-community/push-notification-ios": "^1.1.1", "@react-native-community/push-notification-ios": "^1.1.1",
"@react-native-community/slider": "^2.0.9",
"@react-navigation/bottom-tabs": "^5.1.1", "@react-navigation/bottom-tabs": "^5.1.1",
"@react-navigation/drawer": "^5.1.1", "@react-navigation/drawer": "^5.1.1",
"@react-navigation/native": "^5.0.9", "@react-navigation/native": "^5.0.9",

View file

@ -18,7 +18,7 @@ type Props = {
} }
type State = { type State = {
expanded: boolean expanded: boolean,
} }
const AnimatedListIcon = Animatable.createAnimatableComponent(List.Icon); const AnimatedListIcon = Animatable.createAnimatableComponent(List.Icon);
@ -56,16 +56,14 @@ class AnimatedAccordion extends React.Component<Props, State> {
} }
toggleAccordion = () => { toggleAccordion = () => {
if (this.chevronRef.current != null) if (this.chevronRef.current != null) {
this.chevronRef.current.transitionTo({rotate: this.state.expanded ? this.animStart : this.animEnd}); this.chevronRef.current.transitionTo({rotate: this.state.expanded ? this.animStart : this.animEnd});
this.setState({expanded: !this.state.expanded}) this.setState({expanded: !this.state.expanded})
}
}; };
shouldComponentUpdate(nextProps: Props) { shouldComponentUpdate(nextProps: Props, nextState: State): boolean {
if (nextProps.opened != null) return nextState.expanded !== this.state.expanded;
this.state.expanded = nextProps.opened;
this.setupChevron();
return true;
} }
render() { render() {

View file

@ -0,0 +1,58 @@
// @flow
import * as React from 'react';
import {Text, withTheme} from 'react-native-paper';
import {View} from "react-native-animatable";
import type {CustomTheme} from "../../managers/ThemeManager";
import Slider, {SliderProps} from "@react-native-community/slider";
type Props = {
theme: CustomTheme,
valueSuffix: string,
...SliderProps
}
type State = {
currentValue: number,
}
/**
* Abstraction layer for Modalize component, using custom configuration
*
* @param props Props to pass to the element. Must specify an onRef prop to get an Modalize ref.
* @return {*}
*/
class CustomSlider extends React.Component<Props, State> {
static defaultProps = {
valueSuffix: "",
}
state = {
currentValue: this.props.value,
}
onValueChange = (value: number) => {
this.setState({currentValue: value});
if (this.props.onValueChange != null)
this.props.onValueChange(value);
}
render() {
return (
<View style={{flex: 1, flexDirection: 'row'}}>
<Text style={{marginHorizontal: 10, marginTop: 'auto', marginBottom: 'auto'}}>
{this.state.currentValue}min
</Text>
<Slider
{...this.props}
onValueChange={this.onValueChange}
/>
</View>
);
}
}
export default withTheme(CustomSlider);

View file

@ -1,39 +1,47 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import {ScrollView} from "react-native"; import {ScrollView, View} from "react-native";
import type {CustomTheme} from "../../managers/ThemeManager";
import ThemeManager from '../../managers/ThemeManager'; import ThemeManager from '../../managers/ThemeManager';
import i18n from "i18n-js"; import i18n from "i18n-js";
import AsyncStorageManager from "../../managers/AsyncStorageManager"; import AsyncStorageManager from "../../managers/AsyncStorageManager";
import {Card, List, Switch, ToggleButton} from 'react-native-paper'; import {Card, List, Switch, ToggleButton, withTheme} from 'react-native-paper';
import {Appearance} from "react-native-appearance"; import {Appearance} from "react-native-appearance";
import AnimatedAccordion from "../../components/Animations/AnimatedAccordion"; import CustomSlider from "../../components/Overrides/CustomSlider";
type Props = { type Props = {
navigation: Object, theme: CustomTheme,
}; };
type State = { type State = {
nightMode: boolean, nightMode: boolean,
nightModeFollowSystem: boolean, nightModeFollowSystem: boolean,
proxiwashNotifPickerSelected: string, notificationReminderSelected: number,
startScreenPickerSelected: string, startScreenPickerSelected: string,
}; };
/** /**
* Class defining the Settings screen. This screen shows controls to modify app preferences. * Class defining the Settings screen. This screen shows controls to modify app preferences.
*/ */
export default class SettingsScreen extends React.Component<Props, State> { class SettingsScreen extends React.Component<Props, State> {
state = {
nightMode: ThemeManager.getNightMode(), savedNotificationReminder: number;
nightModeFollowSystem: AsyncStorageManager.getInstance().preferences.nightModeFollowSystem.current === '1' &&
Appearance.getColorScheme() !== 'no-preference',
proxiwashNotifPickerSelected: AsyncStorageManager.getInstance().preferences.proxiwashNotifications.current,
startScreenPickerSelected: AsyncStorageManager.getInstance().preferences.defaultStartScreen.current,
};
constructor() { constructor() {
super(); super();
let notifReminder = AsyncStorageManager.getInstance().preferences.proxiwashNotifications.current;
this.savedNotificationReminder = parseInt(notifReminder);
if (isNaN(this.savedNotificationReminder))
this.savedNotificationReminder = 0;
this.state = {
nightMode: ThemeManager.getNightMode(),
nightModeFollowSystem: AsyncStorageManager.getInstance().preferences.nightModeFollowSystem.current === '1' &&
Appearance.getColorScheme() !== 'no-preference',
notificationReminderSelected: this.savedNotificationReminder,
startScreenPickerSelected: AsyncStorageManager.getInstance().preferences.defaultStartScreen.current,
};
} }
/** /**
@ -41,14 +49,10 @@ export default class SettingsScreen extends React.Component<Props, State> {
* *
* @param value The value to store * @param value The value to store
*/ */
onProxiwashNotifPickerValueChange = (value: string) => { onProxiwashNotifPickerValueChange = (value: number) => {
if (value != null) {
let key = AsyncStorageManager.getInstance().preferences.proxiwashNotifications.key; let key = AsyncStorageManager.getInstance().preferences.proxiwashNotifications.key;
AsyncStorageManager.getInstance().savePref(key, value); AsyncStorageManager.getInstance().savePref(key, value.toString());
this.setState({ this.setState({notificationReminderSelected: value})
proxiwashNotifPickerSelected: value
});
}
}; };
/** /**
@ -73,15 +77,16 @@ export default class SettingsScreen extends React.Component<Props, State> {
*/ */
getProxiwashNotifPicker() { getProxiwashNotifPicker() {
return ( return (
<ToggleButton.Row <CustomSlider
style={{flex: 1, marginHorizontal: 10, height: 50}}
minimumValue={0}
maximumValue={10}
step={1}
value={this.savedNotificationReminder}
onValueChange={this.onProxiwashNotifPickerValueChange} onValueChange={this.onProxiwashNotifPickerValueChange}
value={this.state.proxiwashNotifPickerSelected} thumbTintColor={this.props.theme.colors.primary}
style={{marginLeft: 'auto', marginRight: 'auto'}} minimumTrackTintColor={this.props.theme.colors.primary}
> />
<ToggleButton icon="close" value="never"/>
<ToggleButton icon="numeric-2" value="2"/>
<ToggleButton icon="numeric-5" value="5"/>
</ToggleButton.Row>
); );
} }
@ -133,6 +138,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
* @param icon The icon name to display on the list item * @param icon The icon name to display on the list item
* @param title The text to display as this list item title * @param title The text to display as this list item title
* @param subtitle The text to display as this list item subtitle * @param subtitle The text to display as this list item subtitle
* @param state The current state of the switch
* @returns {React.Node} * @returns {React.Node}
*/ */
getToggleItem(onPressCallback: Function, icon: string, title: string, subtitle: string, state: boolean) { getToggleItem(onPressCallback: Function, icon: string, title: string, subtitle: string, state: boolean) {
@ -141,7 +147,7 @@ export default class SettingsScreen extends React.Component<Props, State> {
title={title} title={title}
description={subtitle} description={subtitle}
left={props => <List.Icon {...props} icon={icon}/>} left={props => <List.Icon {...props} icon={icon}/>}
right={props => right={() =>
<Switch <Switch
value={state} value={state}
onValueChange={onPressCallback} onValueChange={onPressCallback}
@ -177,28 +183,31 @@ export default class SettingsScreen extends React.Component<Props, State> {
this.state.nightMode this.state.nightMode
) : null ) : null
} }
<AnimatedAccordion <List.Item
title={i18n.t('settingsScreen.startScreen')} title={i18n.t('settingsScreen.startScreen')}
subtitle={i18n.t('settingsScreen.startScreenSub')} subtitle={i18n.t('settingsScreen.startScreenSub')}
left={props => <List.Icon {...props} icon="power"/>} left={props => <List.Icon {...props} icon="power"/>}
> />
{this.getStartScreenPicker()} {this.getStartScreenPicker()}
</AnimatedAccordion>
</List.Section> </List.Section>
</Card> </Card>
<Card style={{margin: 5}}> <Card style={{margin: 5}}>
<Card.Title title="Proxiwash"/> <Card.Title title="Proxiwash"/>
<List.Section> <List.Section>
<AnimatedAccordion <List.Item
title={i18n.t('settingsScreen.proxiwashNotifReminder')} title={i18n.t('settingsScreen.proxiwashNotifReminder')}
description={i18n.t('settingsScreen.proxiwashNotifReminderSub')} description={i18n.t('settingsScreen.proxiwashNotifReminderSub')}
left={props => <List.Icon {...props} icon="washing-machine"/>} left={props => <List.Icon {...props} icon="washing-machine"/>}
> opened={true}
/>
<View style={{marginLeft: 30}}>
{this.getProxiwashNotifPicker()} {this.getProxiwashNotifPicker()}
</AnimatedAccordion> </View>
</List.Section> </List.Section>
</Card> </Card>
</ScrollView> </ScrollView>
); );
} }
} }
export default withTheme(SettingsScreen);