#435 web dark mode (#455)

* add ThemeProvider to App.web.tsx

* make FlatNavigator use themed color

* fix extra padding on top in web

* add observer to App.web.tsx to make it react to theme changes

* fix TS for useColorSchemeStyle

* add dark mode toggle button to web LeftNav

* fix index.web.tsx border colors for web

* Move the darkmode desktop web toggle to the right nav column

---------

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
zio/stable
Ansh 2023-04-12 18:49:40 -07:00 committed by GitHub
parent 05e4e4ff93
commit f50f07f562
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 75 additions and 21 deletions

View File

@ -6,8 +6,10 @@ import * as analytics from 'lib/analytics'
import {RootStoreModel, setupState, RootStoreProvider} from './state' import {RootStoreModel, setupState, RootStoreProvider} from './state'
import {Shell} from './view/shell/index' import {Shell} from './view/shell/index'
import {ToastContainer} from './view/com/util/Toast.web' import {ToastContainer} from './view/com/util/Toast.web'
import {ThemeProvider} from 'lib/ThemeContext'
import {observer} from 'mobx-react-lite'
function App() { const App = observer(() => {
const [rootStore, setRootStore] = useState<RootStoreModel | undefined>( const [rootStore, setRootStore] = useState<RootStoreModel | undefined>(
undefined, undefined,
) )
@ -27,17 +29,19 @@ function App() {
} }
return ( return (
<RootSiblingParent> <ThemeProvider theme={rootStore.shell.darkMode ? 'dark' : 'light'}>
<analytics.Provider> <RootSiblingParent>
<RootStoreProvider value={rootStore}> <analytics.Provider>
<SafeAreaProvider> <RootStoreProvider value={rootStore}>
<Shell /> <SafeAreaProvider>
</SafeAreaProvider> <Shell />
<ToastContainer /> </SafeAreaProvider>
</RootStoreProvider> <ToastContainer />
</analytics.Provider> </RootStoreProvider>
</RootSiblingParent> </analytics.Provider>
</RootSiblingParent>
</ThemeProvider>
) )
} })
export default App export default App

View File

@ -40,6 +40,7 @@ import {PrivacyPolicyScreen} from './view/screens/PrivacyPolicy'
import {TermsOfServiceScreen} from './view/screens/TermsOfService' import {TermsOfServiceScreen} from './view/screens/TermsOfService'
import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines' import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines'
import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy' import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy'
import {usePalette} from 'lib/hooks/usePalette'
const navigationRef = createNavigationContainerRef<AllNavigatorParams>() const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
@ -162,6 +163,7 @@ function NotificationsTabNavigator() {
* in a single ("flat") stack. * in a single ("flat") stack.
*/ */
function FlatNavigator() { function FlatNavigator() {
const pal = usePalette('default')
return ( return (
<Flat.Navigator <Flat.Navigator
screenOptions={{ screenOptions={{
@ -169,7 +171,7 @@ function FlatNavigator() {
fullScreenGestureEnabled: true, fullScreenGestureEnabled: true,
headerShown: false, headerShown: false,
animationDuration: 250, animationDuration: 250,
contentStyle: {backgroundColor: 'white'}, contentStyle: [pal.view],
}}> }}>
<Flat.Screen name="Home" component={HomeScreen} /> <Flat.Screen name="Home" component={HomeScreen} />
<Flat.Screen name="Search" component={SearchScreen} /> <Flat.Screen name="Search" component={SearchScreen} />

View File

@ -1,6 +1,6 @@
import {useTheme} from 'lib/ThemeContext' import {useTheme} from 'lib/ThemeContext'
export function useColorSchemeStyle(lightStyle: any, darkStyle: any) { export function useColorSchemeStyle<T>(lightStyle: T, darkStyle: T) {
const colorScheme = useTheme().colorScheme const colorScheme = useTheme().colorScheme
return colorScheme === 'dark' ? darkStyle : lightStyle return colorScheme === 'dark' ? darkStyle : lightStyle
} }

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import {FlatList, View} from 'react-native' import {FlatList, View, Platform} from 'react-native'
import {useFocusEffect, useIsFocused} from '@react-navigation/native' import {useFocusEffect, useIsFocused} from '@react-navigation/native'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import useAppState from 'react-native-appstate-hook' import useAppState from 'react-native-appstate-hook'
@ -187,12 +187,11 @@ const FeedPage = observer(
key="default" key="default"
feed={feed} feed={feed}
scrollElRef={scrollElRef} scrollElRef={scrollElRef}
style={s.hContentRegion}
showPostFollowBtn showPostFollowBtn
onPressTryAgain={onPressTryAgain} onPressTryAgain={onPressTryAgain}
onScroll={onMainScroll} onScroll={onMainScroll}
renderEmptyState={renderEmptyState} renderEmptyState={renderEmptyState}
headerOffset={HEADER_OFFSET} headerOffset={Platform.OS === 'web' ? 0 : HEADER_OFFSET} // only offset on mobile
/> />
{feed.hasNewLatest && !feed.isRefreshing && ( {feed.hasNewLatest && !feed.isRefreshing && (
<LoadLatestBtn onPress={onPressLoadLatest} label="posts" /> <LoadLatestBtn onPress={onPressLoadLatest} label="posts" />

View File

@ -130,7 +130,7 @@ export const DesktopLeftNav = observer(function DesktopLeftNav() {
const pal = usePalette('default') const pal = usePalette('default')
return ( return (
<View style={styles.leftNav}> <View style={[styles.leftNav, pal.view]}>
{store.session.hasSession && <ProfileCard />} {store.session.hasSession && <ProfileCard />}
<BackBtn /> <BackBtn />
<NavItem <NavItem
@ -246,6 +246,7 @@ const styles = StyleSheet.create({
paddingHorizontal: 16, paddingHorizontal: 16,
backgroundColor: colors.blue3, backgroundColor: colors.blue3,
marginTop: 20, marginTop: 20,
marginBottom: 10,
}, },
newPostBtnIconWrapper: { newPostBtnIconWrapper: {
marginRight: 8, marginRight: 8,

View File

@ -10,10 +10,18 @@ import {FEEDBACK_FORM_URL} from 'lib/constants'
import {s} from 'lib/styles' import {s} from 'lib/styles'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {pluralize} from 'lib/strings/helpers' import {pluralize} from 'lib/strings/helpers'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
import {MoonIcon} from 'lib/icons'
export const DesktopRightNav = observer(function DesktopRightNav() { export const DesktopRightNav = observer(function DesktopRightNav() {
const store = useStores() const store = useStores()
const pal = usePalette('default') const pal = usePalette('default')
const mode = useColorSchemeStyle('Light', 'Dark')
const onDarkmodePress = React.useCallback(() => {
store.shell.setDarkMode(!store.shell.darkMode)
}, [store])
return ( return (
<View style={[styles.rightNav, pal.view]}> <View style={[styles.rightNav, pal.view]}>
{store.session.hasSession && <DesktopSearch />} {store.session.hasSession && <DesktopSearch />}
@ -50,6 +58,18 @@ export const DesktopRightNav = observer(function DesktopRightNav() {
</View> </View>
</View> </View>
<InviteCodes /> <InviteCodes />
<View>
<TouchableOpacity
style={[styles.darkModeToggle]}
onPress={onDarkmodePress}>
<View style={[pal.viewLight, styles.darkModeToggleIcon]}>
<MoonIcon size={18} style={pal.textLight} />
</View>
<Text type="sm" style={pal.textLight}>
{mode} mode
</Text>
</TouchableOpacity>
</View>
</View> </View>
) )
}) })
@ -110,4 +130,19 @@ const styles = StyleSheet.create({
inviteCodesIcon: { inviteCodesIcon: {
marginRight: 6, marginRight: 6,
}, },
darkModeToggle: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
marginHorizontal: 12,
},
darkModeToggleIcon: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
width: 26,
height: 26,
borderRadius: 15,
},
}) })

View File

@ -14,9 +14,11 @@ import {RoutesContainer, FlatNavigator} from '../../Navigation'
import {DrawerContent} from './Drawer' import {DrawerContent} from './Drawer'
import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries'
import {BottomBarWeb} from './bottom-bar/BottomBarWeb' import {BottomBarWeb} from './bottom-bar/BottomBarWeb'
import {usePalette} from 'lib/hooks/usePalette'
const ShellInner = observer(() => { const ShellInner = observer(() => {
const store = useStores() const store = useStores()
const pal = usePalette('default')
const {isDesktop} = useWebMediaQueries() const {isDesktop} = useWebMediaQueries()
return ( return (
@ -30,8 +32,20 @@ const ShellInner = observer(() => {
<> <>
<DesktopLeftNav /> <DesktopLeftNav />
<DesktopRightNav /> <DesktopRightNav />
<View style={[styles.viewBorder, styles.viewBorderLeft]} /> <View
<View style={[styles.viewBorder, styles.viewBorderRight]} /> style={[
styles.viewBorder,
{borderLeftColor: pal.colors.border},
styles.viewBorderLeft,
]}
/>
<View
style={[
styles.viewBorder,
{borderLeftColor: pal.colors.border},
styles.viewBorderRight,
]}
/>
</> </>
)} )}
<Composer <Composer
@ -81,7 +95,6 @@ const styles = StyleSheet.create({
width: 1, width: 1,
height: '100%', height: '100%',
borderLeftWidth: 1, borderLeftWidth: 1,
borderLeftColor: colors.gray2,
}, },
viewBorderLeft: { viewBorderLeft: {
left: 'calc(50vw - 300px)', left: 'calc(50vw - 300px)',