From edb227e0ab7b309e27d8936deeed5be49803b01f Mon Sep 17 00:00:00 2001 From: Gautam Arora Date: Thu, 29 Apr 2021 15:40:41 +0530 Subject: [PATCH 1/5] SettingsScreen: Change the "Night Mode" label to "Theme". This commit changes the "Night Mode" label to "Theme". --- src/settings/SettingsScreen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/SettingsScreen.js b/src/settings/SettingsScreen.js index 6c5f7bf49ac..27e524127a9 100644 --- a/src/settings/SettingsScreen.js +++ b/src/settings/SettingsScreen.js @@ -53,7 +53,7 @@ class SettingsScreen extends PureComponent { From f90eb10aeb8c0358d2379e83223ac4ec1229179a Mon Sep 17 00:00:00 2001 From: Gautam Arora Date: Thu, 29 Apr 2021 15:21:00 +0530 Subject: [PATCH 2/5] ThemeScreen: Create a basic ThemeScreen component. This commit introduces a new component called 'ThemeScreen' which will be used to provide different options (currently 'Dark' and 'Light') to change the theme in the app. Fixes part of #4009 --- src/settings/ThemeScreen.js | 77 +++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/settings/ThemeScreen.js diff --git a/src/settings/ThemeScreen.js b/src/settings/ThemeScreen.js new file mode 100644 index 00000000000..e08706ef956 --- /dev/null +++ b/src/settings/ThemeScreen.js @@ -0,0 +1,77 @@ +/* @flow strict-local */ +import React from 'react'; +import { View, FlatList } from 'react-native'; + +import type { RouteProp } from '../react-navigation'; +import { useSelector, useDispatch } from '../react-redux'; +import { getSettings } from '../selectors'; +import '../boot/AppEventHandlers'; +import type { AppNavigationProp } from '../nav/AppNavigator'; +import { settingsChange } from '../actions'; +import { Screen, Touchable, OptionDivider, Label } from '../common'; +import { IconDone } from '../common/Icons'; +import { createStyleSheet, BRAND_COLOR, ThemeContext } from '../styles'; + +const styles = createStyleSheet({ + listWrapper: { + flex: 1, + flexDirection: 'column', + }, + name: { + fontWeight: '300', + fontSize: 13, + }, + listItem: { + flexDirection: 'row', + alignItems: 'center', + paddingTop: 12, + paddingBottom: 12, + paddingLeft: 16, + paddingRight: 16, + }, +}); + +type Props = $ReadOnly<{| + navigation: AppNavigationProp<'theme'>, + route: RouteProp<'theme', void>, +|}>; + +const themeData = [ + { + name: 'Dark', + value: 'night', + }, + { + name: 'Light', + value: 'default', + }, +]; + +export default function ThemeScreen(props: Props) { + const dispatch = useDispatch(); + const theme = useSelector(state => getSettings(state).theme); + const { color } = React.useContext(ThemeContext); + + const handleThemeChange = item => { + dispatch(settingsChange({ theme: item.value })); + }; + + return ( + + ( + handleThemeChange(item)}> + + + + {theme === item.value && } + + + )} + /> + + ); +} From 9b2897ed744476a2b86b0a317d52e6946c5d8a6a Mon Sep 17 00:00:00 2001 From: Gautam Arora Date: Fri, 30 Apr 2021 01:26:22 +0530 Subject: [PATCH 3/5] SettingsScreen: Add navigation logic to navigate to 'ThemeScreen'. Create a new action called 'navigateToThemeScreen' which will help the 'Theme' OptionButton to navigate to 'ThemeScreen' component on clicking. Also wired it up with the 'AppNavigator'. Since, the logic for changing of theme is handled by the 'ThemeScreen' so there is no use of 'handleThemeChange' function in the 'SettingsScreen'. Similarly there is no use of Dispatch and the theme prop so all these are removed in this commit. Fixes part of #4009 --- src/nav/AppNavigator.js | 3 +++ src/nav/navActions.js | 2 ++ src/settings/SettingsScreen.js | 28 +++++++--------------------- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/nav/AppNavigator.js b/src/nav/AppNavigator.js index d95354eeccc..ce87be1a655 100644 --- a/src/nav/AppNavigator.js +++ b/src/nav/AppNavigator.js @@ -26,6 +26,7 @@ import ChatScreen from '../chat/ChatScreen'; import LanguageScreen from '../settings/LanguageScreen'; import PasswordAuthScreen from '../start/PasswordAuthScreen'; import DebugScreen from '../settings/DebugScreen'; +import ThemeScreen from '../settings/ThemeScreen'; import DiagnosticsScreen from '../diagnostics/DiagnosticsScreen'; import VariablesScreen from '../diagnostics/VariablesScreen'; import TimingScreen from '../diagnostics/TimingScreen'; @@ -62,6 +63,7 @@ export type AppNavigatorParamList = {| lightbox: RouteParamsOf, 'create-group': RouteParamsOf, 'invite-users': RouteParamsOf, + theme: RouteParamsOf, diagnostics: RouteParamsOf, variables: RouteParamsOf, timing: RouteParamsOf, @@ -127,6 +129,7 @@ export default function AppNavigator(props: Props) { + diff --git a/src/nav/navActions.js b/src/nav/navActions.js index 266cb589215..c76a3d90c38 100644 --- a/src/nav/navActions.js +++ b/src/nav/navActions.js @@ -77,6 +77,8 @@ export const navigateToLanguage = (): GenericNavigationAction => StackActions.pu export const navigateToCreateGroup = (): GenericNavigationAction => StackActions.push('create-group'); +export const navigateToThemeScreen = (): GenericNavigationAction => StackActions.push('theme'); + export const navigateToDiagnostics = (): GenericNavigationAction => StackActions.push('diagnostics'); diff --git a/src/settings/SettingsScreen.js b/src/settings/SettingsScreen.js index 27e524127a9..0b23eacfc16 100644 --- a/src/settings/SettingsScreen.js +++ b/src/settings/SettingsScreen.js @@ -6,11 +6,8 @@ import { ScrollView } from 'react-native'; import type { RouteProp } from '../react-navigation'; import type { MainTabsNavigationProp } from '../main/MainTabsScreen'; import * as NavigationService from '../nav/NavigationService'; -import type { Dispatch } from '../types'; import { createStyleSheet } from '../styles'; -import { connect } from '../react-redux'; -import { getSettings } from '../selectors'; -import { OptionButton, OptionRow } from '../common'; +import { OptionButton } from '../common'; import { IconDiagnostics, IconNotifications, @@ -19,7 +16,7 @@ import { IconMoreHorizontal, } from '../common/Icons'; import { - settingsChange, + navigateToThemeScreen, navigateToNotifications, navigateToLanguage, navigateToDiagnostics, @@ -35,27 +32,18 @@ const styles = createStyleSheet({ type Props = $ReadOnly<{| navigation: MainTabsNavigationProp<'settings'>, route: RouteProp<'settings', void>, - - theme: string, - dispatch: Dispatch, |}>; class SettingsScreen extends PureComponent { - handleThemeChange = () => { - const { dispatch, theme } = this.props; - dispatch(settingsChange({ theme: theme === 'default' ? 'night' : 'default' })); - }; - render() { - const { theme } = this.props; - return ( - { + NavigationService.dispatch(navigateToThemeScreen()); + }} /> { } } -export default connect(state => ({ - theme: getSettings(state).theme, -}))(SettingsScreen); +export default SettingsScreen; From f2c7a793d63b62dc74514f3cc183e6f2cc660348 Mon Sep 17 00:00:00 2001 From: Gautam Arora Date: Fri, 30 Apr 2021 01:28:11 +0530 Subject: [PATCH 4/5] ThemeProvider [nfc]: Convert to functional component. Convert to functional component. Make use of 'useSelector` hook instead of 'connect'. Removed Dispatch and the theme props since they are not required now. --- src/boot/ThemeProvider.js | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/boot/ThemeProvider.js b/src/boot/ThemeProvider.js index de02eb78d7d..b3183aebeb3 100644 --- a/src/boot/ThemeProvider.js +++ b/src/boot/ThemeProvider.js @@ -1,36 +1,27 @@ /* @flow strict-local */ -import React, { PureComponent } from 'react'; +import React from 'react'; import type { Node as React$Node } from 'react'; -import type { ThemeName, Dispatch } from '../types'; -import { connect } from '../react-redux'; +import { useSelector } from '../react-redux'; import { getSettings } from '../directSelectors'; import { themeData, ThemeContext } from '../styles/theme'; import { ZulipStatusBar } from '../common'; type Props = $ReadOnly<{| - dispatch: Dispatch, - theme: ThemeName, children: React$Node, |}>; -class ThemeProvider extends PureComponent { - static defaultProps = { - theme: 'default', - }; +function ThemeProvider(props: Props) { + const theme = useSelector(state => getSettings(state).theme); + const { children } = props; - render() { - const { children, theme } = this.props; - return ( - - - {children} - - ); - } + return ( + + + {children} + + ); } -export default connect(state => ({ - theme: getSettings(state).theme, -}))(ThemeProvider); +export default ThemeProvider; From b82d99ad503bc889d5959c49478c56e8511d2dec Mon Sep 17 00:00:00 2001 From: Gautam Arora Date: Fri, 30 Apr 2021 01:29:41 +0530 Subject: [PATCH 5/5] ThemeProvider: Add an "automatic" option for theme change. Along with "night" and "default", made a third option called "automatic". This option will check the system settings and automatically provide the correct theme to ThemeProvider's value prop. Discussion of this in CZO [1]. [1] https://chat.zulip.org/#narrow/stream/48-mobile/topic/Automatic.20dark.2Flight.20preference Fixes part of #4009 --- src/boot/ThemeProvider.js | 9 ++++++++- src/reduxTypes.js | 2 +- src/settings/ThemeScreen.js | 4 ++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/boot/ThemeProvider.js b/src/boot/ThemeProvider.js index b3183aebeb3..26ea62ce751 100644 --- a/src/boot/ThemeProvider.js +++ b/src/boot/ThemeProvider.js @@ -1,6 +1,7 @@ /* @flow strict-local */ import React from 'react'; +import { useColorScheme } from 'react-native'; import type { Node as React$Node } from 'react'; import { useSelector } from '../react-redux'; @@ -15,9 +16,15 @@ type Props = $ReadOnly<{| function ThemeProvider(props: Props) { const theme = useSelector(state => getSettings(state).theme); const { children } = props; + const colorScheme = useColorScheme(); + let themeToUse = theme; + + if (themeToUse === 'automatic') { + themeToUse = colorScheme === 'light' || colorScheme == null ? 'light' : 'night'; + } return ( - + {children} diff --git a/src/reduxTypes.js b/src/reduxTypes.js index a73b106a048..9976f737307 100644 --- a/src/reduxTypes.js +++ b/src/reduxTypes.js @@ -261,7 +261,7 @@ export type RealmState = {| // TODO: Stop using the 'default' name. Any 'default' semantics should // only apply the device level, not within the app. See // https://github.com/zulip/zulip-mobile/issues/4009#issuecomment-619280681. -export type ThemeName = 'default' | 'night'; +export type ThemeName = 'default' | 'night' | 'automatic'; export type SettingsState = {| locale: string, diff --git a/src/settings/ThemeScreen.js b/src/settings/ThemeScreen.js index e08706ef956..14e9c16482b 100644 --- a/src/settings/ThemeScreen.js +++ b/src/settings/ThemeScreen.js @@ -37,6 +37,10 @@ type Props = $ReadOnly<{| |}>; const themeData = [ + { + name: 'System Default', + value: 'automatic', + }, { name: 'Dark', value: 'night',