Commit cb4cc075 authored by Martin Santangelo's avatar Martin Santangelo

(feat) theme switch in settings and value stored

1 merge request!513theme support
......@@ -305,6 +305,11 @@ class App extends Component<Props, State> {
* Render
*/
render() {
// App not shown until the theme is loaded
if (ThemedStyles.theme === -1) {
return null;
}
const app = (
<Provider key="app" {...stores}>
<ErrorBoundary message="An error occurred" containerStyle={CS.centered}>
......
......@@ -481,7 +481,8 @@
"confirmDeleteKeychain1":"This will delete your Ethereum keychain from this phone. Ensure you backed up the private keys. If you didn't you can lose access to all your funds. There's NO UNDO!",
"pushNotification":"Push Notifications",
"regenerateKey":"Regenerate messenger keys",
"deleteBlockchain":"Delete blockchain keychain"
"deleteBlockchain":"Delete blockchain keychain",
"darkMode":"Dark mode"
},
"comingSoon":{
"try":"Try the canary app at",
......
......@@ -31,11 +31,14 @@ import logService from '../common/services/log.service';
import storageService from '../common/services/storage.service';
import { observer } from 'mobx-react/native';
import ModalPicker from '../common/components/ModalPicker';
import ThemedStyles from '../styles/ThemedStyles';
import featuresService from '../common/services/features.service';
const ICON_SIZE = 24;
export default
@observer
export default class SettingsScreen extends Component {
class SettingsScreen extends Component {
static navigationOptions = {
title: 'Settings',
......@@ -61,6 +64,14 @@ export default class SettingsScreen extends Component {
settingsStore.setLeftHanded(!settingsStore.leftHanded);
}
setDarkMode = () => {
if (ThemedStyles.theme) {
ThemedStyles.setLight();
} else {
ThemedStyles.setDark();
}
}
wipeEthereumKeychainAction = () => {
const _confirm3 = async (confirmation) => {
await new Promise(r => setTimeout(r, 500)); // Modals have a "cooldown"
......@@ -96,54 +107,55 @@ export default class SettingsScreen extends Component {
};
render() {
const CS = ThemedStyles.style;
const languages = i18n.getSupportedLocales();
const list = [
{
name: i18n.t('language')+` (${i18n.getCurrentLocale()})`,
icon: (<Icon name='flag' size={ICON_SIZE} style={ styles.icon }/>),
icon: (<Icon name='flag' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]}/>),
onPress: () => {
this.showLanguages();
}
},
{
name: i18n.t('auth.password'),
icon: (<Icon name='security' size={ICON_SIZE} style={ styles.icon }/>),
icon: (<Icon name='security' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]}/>),
onPress: () => {
this.props.navigation.navigate('SettingsPassword');
}
},
{
name: i18n.t('auth.email'),
icon: (<Icon name='email' size={ICON_SIZE} style={ styles.icon }/>),
icon: (<Icon name='email' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]}/>),
onPress: () => {
this.props.navigation.navigate('SettingsEmail');
}
},
{
name: i18n.t('settings.pushNotification'),
icon: (<Icon name='notifications' size={ICON_SIZE} style={ styles.icon }/>),
icon: (<Icon name='notifications' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]}/>),
onPress: () => {
this.props.navigation.navigate('NotificationsSettings');
}
},
{
name: i18n.t('settings.blockedChannels'),
icon: (<Icon name='block' size={ICON_SIZE} style={ styles.icon }/>),
icon: (<Icon name='block' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]}/>),
onPress: () => {
this.props.navigation.navigate('SettingsBlockedChannels');
}
},
{
name: i18n.t('settings.regenerateKey'),
icon: (<Icon name='vpn-key' size={ICON_SIZE} style={ styles.icon }/>),
icon: (<Icon name='vpn-key' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]}/>),
onPress: () => {
this.props.navigation.navigate('SettingsRekey');
}
},
{
name: i18n.t('settings.logout'),
icon: (<Icon name='power-settings-new' size={ICON_SIZE} style={ styles.icon } />),
icon: (<Icon name='power-settings-new' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]} />),
onPress: () => {
authService.logout();
......@@ -152,78 +164,76 @@ export default class SettingsScreen extends Component {
},
{
name: i18n.t('settings.deactivate'),
icon: (<Icon name='warning' size={ICON_SIZE} style={ styles.icon } />),
icon: (<Icon name='warning' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]} />),
onPress: () => {
this.props.navigation.push('DeleteChannel');
}
},
{
name: i18n.t('settings.deleteBlockchain'),
icon: (<Icon name='warning' size={ICON_SIZE} style={ styles.icon } />),
icon: (<Icon name='warning' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]} />),
onPress: this.wipeEthereumKeychainAction
},
// ListView used by log package is deprecated
// {
// name: i18n.t('settings.logs'),
// icon: (<Icon name='list' size={ICON_SIZE} style={ styles.icon }/>),
// icon: (<Icon name='list' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]}/>),
// onPress: () => {
// this.props.navigation.push('Logs');
// }
// },
{
name: i18n.t('settings.logOnlyErrors'),
icon: (<Icon name='list' size={ICON_SIZE} style={ styles.icon }/>),
switchButton: true,
icon: (<Icon name='list' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]}/>),
switch: {value: !settingsStore.appLog, onValueChange: this.appLogActivate},
hideChevron: true,
switched: !settingsStore.appLog,
onSwitch: this.appLogActivate
},
{
name: i18n.t('settings.leftHandedMode'),
icon: (<MaterialCommunityIcons name='hand' size={ICON_SIZE} style={ styles.icon }/>),
switchButton: true,
icon: (<MaterialCommunityIcons name='hand' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]}/>),
switch: {value: settingsStore.leftHanded, onValueChange: this.leftHandedActivate},
hideChevron: true,
switched: settingsStore.leftHanded,
onSwitch: this.leftHandedActivate
},
];
if (featuresService.has('dark-mode')) {
list.push({
name: i18n.t('settings.darkMode'),
icon: (<MaterialCommunityIcons name='hand' size={ICON_SIZE} style={[styles.icon, CS.colorPrimaryText]}/>),
switch: {value: !!ThemedStyles.theme, onValueChange: this.setDarkMode},
hideChevron: true,
});
}
return (
<ScrollView style={styles.scrollView}>
<ModalPicker
onSelect={this.languageSelected}
onCancel={this.cancel}
show={this.state.showLanguages}
title={i18n.t('language')}
valueField="value"
labelField="name"
value={this.state.language}
items={languages}
/>
<View style={styles.scrollViewContainer}>
<View style={styles.container}>
{
list.map((l, i) => (
<ListItem
key={i}
title={l.name}
titleStyle={styles.listTitle}
containerStyle={styles.listItem}
subtitle={l.subtitle}
switchButton={l.switchButton}
hideChevron ={l.hideChevron}
onSwitch={l.onSwitch}
switched={l.switched}
leftIcon={l.icon}
onPress= {l.onPress}
noBorder
/>
))
}
</View>
</View>
</ScrollView>
<ScrollView style={[styles.scrollView, CS.backgroundThemePrimary]}>
<ModalPicker
onSelect={this.languageSelected}
onCancel={this.cancel}
show={this.state.showLanguages}
title={i18n.t('language')}
valueField="value"
labelField="name"
value={this.state.language}
items={languages}
/>
{
list.map((l, i) => (
<ListItem
key={i}
title={l.name}
titleStyle={[CS.fontL, CS.colorPrimaryText, CS.paddingVertical2x]}
containerStyle={styles.listItem}
subtitle={l.subtitle}
hideChevron ={l.hideChevron}
switch={l.switch}
leftIcon={l.icon}
onPress= {l.onPress}
/>
))
}
</ScrollView>
);
}
......@@ -246,10 +256,8 @@ export default class SettingsScreen extends Component {
const styles = StyleSheet.create({
scrollView: {
backgroundColor: '#FFF',
flexDirection: 'column',
},
scrollViewContainer: {
flex:1
},
container: {
flex: 1,
......@@ -258,10 +266,7 @@ const styles = StyleSheet.create({
borderBottomWidth: 0,
},
listItem: {
borderBottomWidth: 1,
borderBottomColor: '#ddd',
paddingTop: 8,
paddingBottom: 8,
backgroundColor: 'transparent'
//height:20
},
listTitle: {
......@@ -269,7 +274,6 @@ const styles = StyleSheet.create({
fontFamily: 'Roboto',
},
icon: {
color: '#455a64',
alignSelf: 'center',
},
......@@ -278,7 +282,7 @@ const styles = StyleSheet.create({
paddingTop: 8,
paddingBottom: 8,
textAlignVertical: 'center',
backgroundColor: '#f4f4f4',
// backgroundColor: '#f4f4f4',
width: '100%',
//height: 40,
borderTopWidth: StyleSheet.hairlineWidth,
......
import { observable, action } from 'mobx'
import { observable, action } from 'mobx';
import logService from '../common/services/log.service';
import storageService from '../common/services/storage.service';
import appStore from '../../AppStores';
import ThemedStyles from '../styles/ThemedStyles';
/**
* Store for the values held in Settings.
......@@ -22,8 +22,24 @@ class SettingsStore {
*/
@action.bound
async init() {
const data = await storageService.multiGet(['LeftHanded', 'AppLog', 'CreatorNsfw', 'ConsumerNsfw', 'UseHashtags']);
if (!data) return;
const data = await storageService.multiGet([
'LeftHanded',
'AppLog',
'CreatorNsfw',
'ConsumerNsfw',
'UseHashtags',
'Theme',
]);
// store theme changes
ThemedStyles.onThemeChange((value) => {
this.setTheme(value);
});
if (!data) {
ThemedStyles.theme = 0;
return;
}
this.leftHanded = data[0][1];
this.appLog = data[1][1];
this.creatorNsfw = data[2][1] || [];
......@@ -33,9 +49,20 @@ class SettingsStore {
// set the initial value for hashtag
appStore.hashtag.setAll(!this.useHashtags);
// theme
ThemedStyles.theme = data[5][1] || 0;
return this;
}
/**
* Set the theme in the stored values
* @param {numeric} value
*/
setTheme(value) {
storageService.setItem('Theme', value);
}
/**
* Sets in local store and changes this class variable
*/
......
import { StyleSheet } from 'react-native';
import { observable, action } from 'mobx';
import { observable, action, reaction } from 'mobx';
import { DARK_THEME, LIGHT_THEME } from './Colors';
......@@ -34,9 +34,12 @@ for (let index = 0; index < repetitions; index++) {
class ThemedStylesStore {
/**
* Theme observable
* 1 Dark
* 0 Light
* -1 Not loaded
* @property {Observable<numeric>}
*/
@observable theme = 0;
@observable theme = -1;
/**
* Style
......@@ -63,6 +66,12 @@ class ThemedStylesStore {
this.generateStyle();
}
onThemeChange(fn) {
return reaction(() => [this.theme], async args => await fn(...args), {
fireImmediately: false,
});
}
/**
* Generates the current theme
*/
......
Please register or to comment