Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

#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>

authored by

Ansh
Paul Frazee
and committed by
GitHub
f50f07f5 05e4e4ff

+75 -21
+16 -12
src/App.web.tsx
··· 6 6 import {RootStoreModel, setupState, RootStoreProvider} from './state' 7 7 import {Shell} from './view/shell/index' 8 8 import {ToastContainer} from './view/com/util/Toast.web' 9 + import {ThemeProvider} from 'lib/ThemeContext' 10 + import {observer} from 'mobx-react-lite' 9 11 10 - function App() { 12 + const App = observer(() => { 11 13 const [rootStore, setRootStore] = useState<RootStoreModel | undefined>( 12 14 undefined, 13 15 ) ··· 27 29 } 28 30 29 31 return ( 30 - <RootSiblingParent> 31 - <analytics.Provider> 32 - <RootStoreProvider value={rootStore}> 33 - <SafeAreaProvider> 34 - <Shell /> 35 - </SafeAreaProvider> 36 - <ToastContainer /> 37 - </RootStoreProvider> 38 - </analytics.Provider> 39 - </RootSiblingParent> 32 + <ThemeProvider theme={rootStore.shell.darkMode ? 'dark' : 'light'}> 33 + <RootSiblingParent> 34 + <analytics.Provider> 35 + <RootStoreProvider value={rootStore}> 36 + <SafeAreaProvider> 37 + <Shell /> 38 + </SafeAreaProvider> 39 + <ToastContainer /> 40 + </RootStoreProvider> 41 + </analytics.Provider> 42 + </RootSiblingParent> 43 + </ThemeProvider> 40 44 ) 41 - } 45 + }) 42 46 43 47 export default App
+3 -1
src/Navigation.tsx
··· 40 40 import {TermsOfServiceScreen} from './view/screens/TermsOfService' 41 41 import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines' 42 42 import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy' 43 + import {usePalette} from 'lib/hooks/usePalette' 43 44 44 45 const navigationRef = createNavigationContainerRef<AllNavigatorParams>() 45 46 ··· 162 163 * in a single ("flat") stack. 163 164 */ 164 165 function FlatNavigator() { 166 + const pal = usePalette('default') 165 167 return ( 166 168 <Flat.Navigator 167 169 screenOptions={{ ··· 169 171 fullScreenGestureEnabled: true, 170 172 headerShown: false, 171 173 animationDuration: 250, 172 - contentStyle: {backgroundColor: 'white'}, 174 + contentStyle: [pal.view], 173 175 }}> 174 176 <Flat.Screen name="Home" component={HomeScreen} /> 175 177 <Flat.Screen name="Search" component={SearchScreen} />
+1 -1
src/lib/hooks/useColorSchemeStyle.ts
··· 1 1 import {useTheme} from 'lib/ThemeContext' 2 2 3 - export function useColorSchemeStyle(lightStyle: any, darkStyle: any) { 3 + export function useColorSchemeStyle<T>(lightStyle: T, darkStyle: T) { 4 4 const colorScheme = useTheme().colorScheme 5 5 return colorScheme === 'dark' ? darkStyle : lightStyle 6 6 }
+2 -3
src/view/screens/Home.tsx
··· 1 1 import React from 'react' 2 - import {FlatList, View} from 'react-native' 2 + import {FlatList, View, Platform} from 'react-native' 3 3 import {useFocusEffect, useIsFocused} from '@react-navigation/native' 4 4 import {observer} from 'mobx-react-lite' 5 5 import useAppState from 'react-native-appstate-hook' ··· 187 187 key="default" 188 188 feed={feed} 189 189 scrollElRef={scrollElRef} 190 - style={s.hContentRegion} 191 190 showPostFollowBtn 192 191 onPressTryAgain={onPressTryAgain} 193 192 onScroll={onMainScroll} 194 193 renderEmptyState={renderEmptyState} 195 - headerOffset={HEADER_OFFSET} 194 + headerOffset={Platform.OS === 'web' ? 0 : HEADER_OFFSET} // only offset on mobile 196 195 /> 197 196 {feed.hasNewLatest && !feed.isRefreshing && ( 198 197 <LoadLatestBtn onPress={onPressLoadLatest} label="posts" />
+2 -1
src/view/shell/desktop/LeftNav.tsx
··· 130 130 const pal = usePalette('default') 131 131 132 132 return ( 133 - <View style={styles.leftNav}> 133 + <View style={[styles.leftNav, pal.view]}> 134 134 {store.session.hasSession && <ProfileCard />} 135 135 <BackBtn /> 136 136 <NavItem ··· 246 246 paddingHorizontal: 16, 247 247 backgroundColor: colors.blue3, 248 248 marginTop: 20, 249 + marginBottom: 10, 249 250 }, 250 251 newPostBtnIconWrapper: { 251 252 marginRight: 8,
+35
src/view/shell/desktop/RightNav.tsx
··· 10 10 import {s} from 'lib/styles' 11 11 import {useStores} from 'state/index' 12 12 import {pluralize} from 'lib/strings/helpers' 13 + import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle' 14 + import {MoonIcon} from 'lib/icons' 13 15 14 16 export const DesktopRightNav = observer(function DesktopRightNav() { 15 17 const store = useStores() 16 18 const pal = usePalette('default') 19 + const mode = useColorSchemeStyle('Light', 'Dark') 20 + 21 + const onDarkmodePress = React.useCallback(() => { 22 + store.shell.setDarkMode(!store.shell.darkMode) 23 + }, [store]) 24 + 17 25 return ( 18 26 <View style={[styles.rightNav, pal.view]}> 19 27 {store.session.hasSession && <DesktopSearch />} ··· 50 58 </View> 51 59 </View> 52 60 <InviteCodes /> 61 + <View> 62 + <TouchableOpacity 63 + style={[styles.darkModeToggle]} 64 + onPress={onDarkmodePress}> 65 + <View style={[pal.viewLight, styles.darkModeToggleIcon]}> 66 + <MoonIcon size={18} style={pal.textLight} /> 67 + </View> 68 + <Text type="sm" style={pal.textLight}> 69 + {mode} mode 70 + </Text> 71 + </TouchableOpacity> 72 + </View> 53 73 </View> 54 74 ) 55 75 }) ··· 109 129 }, 110 130 inviteCodesIcon: { 111 131 marginRight: 6, 132 + }, 133 + 134 + darkModeToggle: { 135 + flexDirection: 'row', 136 + alignItems: 'center', 137 + gap: 8, 138 + marginHorizontal: 12, 139 + }, 140 + darkModeToggleIcon: { 141 + flexDirection: 'row', 142 + alignItems: 'center', 143 + justifyContent: 'center', 144 + width: 26, 145 + height: 26, 146 + borderRadius: 15, 112 147 }, 113 148 })
+16 -3
src/view/shell/index.web.tsx
··· 14 14 import {DrawerContent} from './Drawer' 15 15 import {useWebMediaQueries} from '../../lib/hooks/useWebMediaQueries' 16 16 import {BottomBarWeb} from './bottom-bar/BottomBarWeb' 17 + import {usePalette} from 'lib/hooks/usePalette' 17 18 18 19 const ShellInner = observer(() => { 19 20 const store = useStores() 21 + const pal = usePalette('default') 20 22 const {isDesktop} = useWebMediaQueries() 21 23 22 24 return ( ··· 30 32 <> 31 33 <DesktopLeftNav /> 32 34 <DesktopRightNav /> 33 - <View style={[styles.viewBorder, styles.viewBorderLeft]} /> 34 - <View style={[styles.viewBorder, styles.viewBorderRight]} /> 35 + <View 36 + style={[ 37 + styles.viewBorder, 38 + {borderLeftColor: pal.colors.border}, 39 + styles.viewBorderLeft, 40 + ]} 41 + /> 42 + <View 43 + style={[ 44 + styles.viewBorder, 45 + {borderLeftColor: pal.colors.border}, 46 + styles.viewBorderRight, 47 + ]} 48 + /> 35 49 </> 36 50 )} 37 51 <Composer ··· 81 95 width: 1, 82 96 height: '100%', 83 97 borderLeftWidth: 1, 84 - borderLeftColor: colors.gray2, 85 98 }, 86 99 viewBorderLeft: { 87 100 left: 'calc(50vw - 300px)',