+5
-5
app.config.js
+5
-5
app.config.js
···
1
1
const pkg = require('./package.json')
2
2
3
-
const DARK_SPLASH_ANDROID_BACKGROUND = '#0f141b'
4
-
5
3
module.exports = function (config) {
6
4
/**
7
5
* App version number. Should be incremented as part of a release cycle.
···
140
138
},
141
139
androidStatusBar: {
142
140
barStyle: 'light-content',
143
-
backgroundColor: '#00000000',
144
141
},
145
142
// Dark nav bar in light mode is better than light nav bar in dark mode
146
143
androidNavigationBar: {
147
144
barStyle: 'light-content',
148
-
backgroundColor: DARK_SPLASH_ANDROID_BACKGROUND,
149
145
},
150
146
android: {
151
147
icon: './assets/app-icons/android_icon_default_light.png',
···
197
193
plugins: [
198
194
'expo-video',
199
195
'expo-localization',
196
+
[
197
+
'react-native-edge-to-edge',
198
+
{android: {enforceNavigationBarContrast: false}},
199
+
],
200
200
USE_SENTRY && [
201
201
'@sentry/react-native/expo',
202
202
{
···
240
240
'./plugins/withAndroidManifestPlugin.js',
241
241
'./plugins/withAndroidManifestFCMIconPlugin.js',
242
242
'./plugins/withAndroidStylesAccentColorPlugin.js',
243
-
'./plugins/withAndroidSplashScreenStatusBarTranslucentPlugin.js',
243
+
'./plugins/withAndroidDayNightThemePlugin.js',
244
244
'./plugins/withAndroidNoJitpackPlugin.js',
245
245
'./plugins/withNoBundleCompression.js',
246
246
'./plugins/shareExtension/withShareExtensions.js',
+2
-3
package.json
+2
-3
package.json
···
141
141
"expo-linking": "~7.0.5",
142
142
"expo-localization": "~16.0.1",
143
143
"expo-media-library": "~17.0.6",
144
-
"expo-navigation-bar": "~4.0.9",
145
144
"expo-notifications": "~0.29.14",
146
145
"expo-screen-orientation": "~8.0.4",
147
146
"expo-sharing": "~13.0.1",
148
147
"expo-splash-screen": "~0.29.22",
149
-
"expo-status-bar": "~2.0.1",
150
148
"expo-system-ui": "~4.0.9",
151
149
"expo-task-manager": "~12.0.6",
152
150
"expo-updates": "~0.27.4",
···
178
176
"react-native-compressor": "1.10.3",
179
177
"react-native-date-picker": "^5.0.7",
180
178
"react-native-drawer-layout": "^4.1.1",
179
+
"react-native-edge-to-edge": "^1.6.0",
181
180
"react-native-emoji-popup": "^0.1.2",
182
181
"react-native-gesture-handler": "2.20.2",
183
182
"react-native-get-random-values": "~1.11.0",
184
183
"react-native-image-crop-picker": "^0.41.6",
185
184
"react-native-ios-context-menu": "^1.15.3",
186
-
"react-native-keyboard-controller": "^1.14.5",
185
+
"react-native-keyboard-controller": "^1.17.1",
187
186
"react-native-mmkv": "^2.12.2",
188
187
"react-native-pager-view": "6.5.1",
189
188
"react-native-picker-select": "^9.3.1",
+27
plugins/withAndroidDayNightThemePlugin.js
+27
plugins/withAndroidDayNightThemePlugin.js
···
1
+
// Based on https://github.com/expo/expo/pull/33957
2
+
// Could be removed once the app has been updated to Expo 53
3
+
const {withAndroidStyles} = require('@expo/config-plugins')
4
+
5
+
module.exports = function withAndroidDayNightThemePlugin(appConfig) {
6
+
const cleanupList = new Set([
7
+
'colorPrimary',
8
+
'android:editTextBackground',
9
+
'android:textColor',
10
+
'android:editTextStyle',
11
+
])
12
+
13
+
return withAndroidStyles(appConfig, config => {
14
+
config.modResults.resources.style = config.modResults.resources.style
15
+
?.map(style => {
16
+
if (style.$.name === 'AppTheme' && style.item != null) {
17
+
style.item = style.item.filter(item => !cleanupList.has(item.$.name))
18
+
}
19
+
return style
20
+
})
21
+
.filter(style => {
22
+
return style.$.name !== 'ResetEditText'
23
+
})
24
+
25
+
return config
26
+
})
27
+
}
-28
plugins/withAndroidSplashScreenStatusBarTranslucentPlugin.js
-28
plugins/withAndroidSplashScreenStatusBarTranslucentPlugin.js
···
1
-
const {withStringsXml, AndroidConfig} = require('@expo/config-plugins')
2
-
3
-
module.exports = function withAndroidSplashScreenStatusBarTranslucentPlugin(
4
-
appConfig,
5
-
) {
6
-
return withStringsXml(appConfig, function (decoratedAppConfig) {
7
-
try {
8
-
decoratedAppConfig.modResults = AndroidConfig.Strings.setStringItem(
9
-
[
10
-
{
11
-
_: 'true',
12
-
$: {
13
-
name: 'expo_splash_screen_status_bar_translucent',
14
-
translatable: 'false',
15
-
},
16
-
},
17
-
],
18
-
decoratedAppConfig.modResults,
19
-
)
20
-
} catch (e) {
21
-
console.error(
22
-
`withAndroidSplashScreenStatusBarTranslucentPlugin failed`,
23
-
e,
24
-
)
25
-
}
26
-
return decoratedAppConfig
27
-
})
28
-
}
+1
-1
plugins/withAndroidStylesAccentColorPlugin.js
+1
-1
plugins/withAndroidStylesAccentColorPlugin.js
+2
-5
src/App.native.tsx
+2
-5
src/App.native.tsx
···
46
46
import {Provider as UnreadNotifsProvider} from '#/state/queries/notifications/unread'
47
47
import {
48
48
Provider as SessionProvider,
49
-
SessionAccount,
49
+
type SessionAccount,
50
50
useSession,
51
51
useSessionApi,
52
52
} from '#/state/session'
53
53
import {readLastActiveAccount} from '#/state/session/util'
54
54
import {Provider as ShellStateProvider} from '#/state/shell'
55
55
import {Provider as ComposerProvider} from '#/state/shell/composer'
56
-
import {Provider as LightStatusBarProvider} from '#/state/shell/light-status-bar'
57
56
import {Provider as LoggedOutViewProvider} from '#/state/shell/logged-out'
58
57
import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide'
59
58
import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
···
219
218
<StarterPackProvider>
220
219
<SafeAreaProvider
221
220
initialMetrics={initialWindowMetrics}>
222
-
<LightStatusBarProvider>
223
-
<InnerApp />
224
-
</LightStatusBarProvider>
221
+
<InnerApp />
225
222
</SafeAreaProvider>
226
223
</StarterPackProvider>
227
224
</BottomSheetProvider>
+2
-5
src/App.web.tsx
+2
-5
src/App.web.tsx
···
35
35
import {Provider as UnreadNotifsProvider} from '#/state/queries/notifications/unread'
36
36
import {
37
37
Provider as SessionProvider,
38
-
SessionAccount,
38
+
type SessionAccount,
39
39
useSession,
40
40
useSessionApi,
41
41
} from '#/state/session'
42
42
import {readLastActiveAccount} from '#/state/session/util'
43
43
import {Provider as ShellStateProvider} from '#/state/shell'
44
44
import {Provider as ComposerProvider} from '#/state/shell/composer'
45
-
import {Provider as LightStatusBarProvider} from '#/state/shell/light-status-bar'
46
45
import {Provider as LoggedOutViewProvider} from '#/state/shell/logged-out'
47
46
import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide'
48
47
import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
···
193
192
<LightboxStateProvider>
194
193
<PortalProvider>
195
194
<StarterPackProvider>
196
-
<LightStatusBarProvider>
197
-
<InnerApp />
198
-
</LightStatusBarProvider>
195
+
<InnerApp />
199
196
</StarterPackProvider>
200
197
</PortalProvider>
201
198
</LightboxStateProvider>
+14
src/alf/util/systemUI.ts
+14
src/alf/util/systemUI.ts
···
1
+
import * as SystemUI from 'expo-system-ui'
2
+
3
+
import {isAndroid} from '#/platform/detection'
4
+
import {Theme} from '../types'
5
+
6
+
export function setSystemUITheme(themeType: 'theme' | 'lightbox', t: Theme) {
7
+
if (isAndroid) {
8
+
if (themeType === 'theme') {
9
+
SystemUI.setBackgroundColorAsync(t.atoms.bg.backgroundColor)
10
+
} else {
11
+
SystemUI.setBackgroundColorAsync('black')
12
+
}
13
+
}
14
+
}
+1
-1
src/components/ContextMenu/index.tsx
+1
-1
src/components/ContextMenu/index.tsx
···
556
556
// pure vibes based
557
557
const TOP_INSET = insets.top + 80
558
558
const BOTTOM_INSET_IOS = insets.bottom + 20
559
-
const BOTTOM_INSET_ANDROID = 12 // TODO: revisit when edge-to-edge mode is enabled -sfn
559
+
const BOTTOM_INSET_ANDROID = insets.bottom + 12
560
560
561
561
const {height} = evt.nativeEvent.layout
562
562
const topPosition =
+14
-9
src/components/Dialog/sheet-wrapper.ts
+14
-9
src/components/Dialog/sheet-wrapper.ts
···
1
1
import {useCallback} from 'react'
2
+
import {SystemBars} from 'react-native-edge-to-edge'
2
3
3
-
import {useDialogStateControlContext} from '#/state/dialogs'
4
+
import {isIOS} from '#/platform/detection'
4
5
5
6
/**
6
7
* If we're calling a system API like the image picker that opens a sheet
7
8
* wrap it in this function to make sure the status bar is the correct color.
8
9
*/
9
10
export function useSheetWrapper() {
10
-
const {setFullyExpandedCount} = useDialogStateControlContext()
11
-
return useCallback(
12
-
async <T>(promise: Promise<T>): Promise<T> => {
13
-
setFullyExpandedCount(c => c + 1)
11
+
return useCallback(async <T>(promise: Promise<T>): Promise<T> => {
12
+
if (isIOS) {
13
+
const entry = SystemBars.pushStackEntry({
14
+
style: {
15
+
statusBar: 'light',
16
+
},
17
+
})
14
18
const res = await promise
15
-
setFullyExpandedCount(c => c - 1)
19
+
SystemBars.popStackEntry(entry)
16
20
return res
17
-
},
18
-
[setFullyExpandedCount],
19
-
)
21
+
} else {
22
+
return await promise
23
+
}
24
+
}, [])
20
25
}
+1
-4
src/lib/hooks/useEnableKeyboardController.tsx
+1
-4
src/lib/hooks/useEnableKeyboardController.tsx
···
26
26
children: React.ReactNode
27
27
}) {
28
28
return (
29
-
<KeyboardProvider
30
-
enabled={false}
31
-
// I don't think this is necessary, but Chesterton's fence and all that -sfn
32
-
statusBarTranslucent={true}>
29
+
<KeyboardProvider enabled={false}>
33
30
<KeyboardControllerProviderInner>
34
31
{children}
35
32
</KeyboardControllerProviderInner>
+1
-1
src/screens/Login/index.tsx
+1
-1
src/screens/Login/index.tsx
···
8
8
import {logEvent} from '#/lib/statsig/statsig'
9
9
import {logger} from '#/logger'
10
10
import {useServiceQuery} from '#/state/queries/service'
11
-
import {SessionAccount, useSession} from '#/state/session'
11
+
import {type SessionAccount, useSession} from '#/state/session'
12
12
import {useLoggedOutView} from '#/state/shell/logged-out'
13
13
import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout'
14
14
import {ForgotPasswordForm} from '#/screens/Login/ForgotPasswordForm'
+3
-2
src/screens/Messages/components/MessageInput.tsx
+3
-2
src/screens/Messages/components/MessageInput.tsx
···
24
24
useMessageDraft,
25
25
useSaveMessageDraft,
26
26
} from '#/state/messages/message-drafts'
27
-
import {EmojiPickerPosition} from '#/view/com/composer/text-input/web/EmojiPicker.web'
27
+
import {type EmojiPickerPosition} from '#/view/com/composer/text-input/web/EmojiPicker.web'
28
28
import * as Toast from '#/view/com/util/Toast'
29
-
import {atoms as a, useTheme} from '#/alf'
29
+
import {android, atoms as a, useTheme} from '#/alf'
30
30
import {useSharedInputStyles} from '#/components/forms/TextField'
31
31
import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlane} from '#/components/icons/PaperPlane'
32
32
import {useExtractEmbedFromFacets} from './MessageInputEmbed'
···
174
174
a.text_md,
175
175
a.px_sm,
176
176
t.atoms.text,
177
+
android({paddingTop: 0}),
177
178
{paddingBottom: isIOS ? 5 : 0},
178
179
animatedStyle,
179
180
]}
+2
-2
src/screens/SignupQueued.tsx
+2
-2
src/screens/SignupQueued.tsx
···
1
1
import React from 'react'
2
2
import {Modal, ScrollView, View} from 'react-native'
3
+
import {SystemBars} from 'react-native-edge-to-edge'
3
4
import {useSafeAreaInsets} from 'react-native-safe-area-context'
4
-
import {StatusBar} from 'expo-status-bar'
5
5
import {msg, plural, Trans} from '@lingui/macro'
6
6
import {useLingui} from '@lingui/react'
7
7
···
106
106
animationType={native('slide')}
107
107
presentationStyle="formSheet"
108
108
style={[web(a.util_screen_outer)]}>
109
-
{isIOS && <StatusBar style="light" />}
109
+
{isIOS && <SystemBars style={{statusBar: 'light'}} />}
110
110
<ScrollView
111
111
style={[a.flex_1, t.atoms.bg]}
112
112
contentContainerStyle={{borderWidth: 0}}
+2
-2
src/screens/Takendown.tsx
+2
-2
src/screens/Takendown.tsx
···
1
1
import {useMemo, useState} from 'react'
2
2
import {Modal, View} from 'react-native'
3
+
import {SystemBars} from 'react-native-edge-to-edge'
3
4
import {KeyboardAwareScrollView} from 'react-native-keyboard-controller'
4
5
import {useSafeAreaInsets} from 'react-native-safe-area-context'
5
-
import {StatusBar} from 'expo-status-bar'
6
6
import {ComAtprotoAdminDefs, ComAtprotoModerationDefs} from '@atproto/api'
7
7
import {msg, Trans} from '@lingui/macro'
8
8
import {useLingui} from '@lingui/react'
···
126
126
animationType={native('slide')}
127
127
presentationStyle="formSheet"
128
128
style={[web(a.util_screen_outer)]}>
129
-
{isIOS && <StatusBar style="light" />}
129
+
{isIOS && <SystemBars style={{statusBar: 'light'}} />}
130
130
<KeyboardAwareScrollView style={[a.flex_1, t.atoms.bg]} centerContent>
131
131
<View
132
132
style={[
+5
-3
src/screens/VideoFeed/index.tsx
+5
-3
src/screens/VideoFeed/index.tsx
···
8
8
ViewabilityConfig,
9
9
ViewToken,
10
10
} from 'react-native'
11
+
import {SystemBars} from 'react-native-edge-to-edge'
11
12
import {
12
13
Gesture,
13
14
GestureDetector,
···
77
78
import {UserAvatar} from '#/view/com/util/UserAvatar'
78
79
import {Header} from '#/screens/VideoFeed/components/Header'
79
80
import {atoms as a, ios, platform, ThemeProvider, useTheme} from '#/alf'
80
-
import {setNavigationBar} from '#/alf/util/navigationBar'
81
+
import {setSystemUITheme} from '#/alf/util/systemUI'
81
82
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
82
83
import {Divider} from '#/components/Divider'
83
84
import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeftIcon} from '#/components/icons/Arrow'
···
126
127
useFocusEffect(
127
128
useCallback(() => {
128
129
setMinShellMode(true)
129
-
setNavigationBar('lightbox', t)
130
+
setSystemUITheme('lightbox', t)
130
131
return () => {
131
132
setMinShellMode(false)
132
-
setNavigationBar('theme', t)
133
+
setSystemUITheme('theme', t)
133
134
}
134
135
}, [setMinShellMode, t]),
135
136
)
···
140
141
return (
141
142
<ThemeProvider theme="dark">
142
143
<Layout.Screen noInsetTop style={{backgroundColor: 'black'}}>
144
+
<SystemBars style={{statusBar: 'light', navigationBar: 'light'}} />
143
145
<View
144
146
style={[
145
147
a.absolute,
+11
-38
src/state/shell/light-status-bar.tsx
+11
-38
src/state/shell/light-status-bar.tsx
···
1
-
import {createContext, useContext, useEffect, useState} from 'react'
2
-
3
-
import {isWeb} from '#/platform/detection'
4
-
5
-
const LightStatusBarRefCountContext = createContext<boolean>(false)
6
-
const SetLightStatusBarRefCountContext = createContext<React.Dispatch<
7
-
React.SetStateAction<number>
8
-
> | null>(null)
9
-
10
-
export function useLightStatusBar() {
11
-
return useContext(LightStatusBarRefCountContext)
12
-
}
1
+
import {useEffect} from 'react'
2
+
import {SystemBars} from 'react-native-edge-to-edge'
13
3
14
4
export function useSetLightStatusBar(enabled: boolean) {
15
-
const setRefCount = useContext(SetLightStatusBarRefCountContext)
16
5
useEffect(() => {
17
-
// noop on web -sfn
18
-
if (isWeb) return
19
-
20
-
if (!setRefCount) {
21
-
if (__DEV__)
22
-
console.error(
23
-
'useLightStatusBar was used without a SetLightStatusBarRefCountContext provider',
24
-
)
25
-
return
26
-
}
27
6
if (enabled) {
28
-
setRefCount(prev => prev + 1)
29
-
return () => setRefCount(prev => prev - 1)
7
+
const entry = SystemBars.pushStackEntry({
8
+
style: {
9
+
statusBar: 'light',
10
+
},
11
+
})
12
+
return () => {
13
+
SystemBars.popStackEntry(entry)
14
+
}
30
15
}
31
-
}, [enabled, setRefCount])
32
-
}
33
-
34
-
export function Provider({children}: React.PropsWithChildren<{}>) {
35
-
const [refCount, setRefCount] = useState(0)
36
-
37
-
return (
38
-
<SetLightStatusBarRefCountContext.Provider value={setRefCount}>
39
-
<LightStatusBarRefCountContext.Provider value={refCount > 0}>
40
-
{children}
41
-
</LightStatusBarRefCountContext.Provider>
42
-
</SetLightStatusBarRefCountContext.Provider>
43
-
)
16
+
}, [enabled])
44
17
}
+1
-2
src/view/com/composer/Composer.tsx
+1
-2
src/view/com/composer/Composer.tsx
+21
-35
src/view/com/lightbox/ImageViewing/index.tsx
+21
-35
src/view/com/lightbox/ImageViewing/index.tsx
···
9
9
// https://github.com/jobtoday/react-native-image-viewing
10
10
11
11
import React, {useCallback, useEffect, useMemo, useState} from 'react'
12
-
import {
13
-
LayoutAnimation,
14
-
PixelRatio,
15
-
Platform,
16
-
StyleSheet,
17
-
View,
18
-
} from 'react-native'
12
+
import {LayoutAnimation, PixelRatio, StyleSheet, View} from 'react-native'
13
+
import {SystemBars} from 'react-native-edge-to-edge'
19
14
import {Gesture} from 'react-native-gesture-handler'
20
15
import PagerView from 'react-native-pager-view'
21
16
import Animated, {
22
-
AnimatedRef,
17
+
type AnimatedRef,
23
18
cancelAnimation,
24
19
interpolate,
25
20
measure,
26
21
runOnJS,
27
-
SharedValue,
22
+
type SharedValue,
28
23
useAnimatedReaction,
29
24
useAnimatedRef,
30
25
useAnimatedStyle,
···
32
27
useSharedValue,
33
28
withDecay,
34
29
withSpring,
35
-
WithSpringConfig,
30
+
type WithSpringConfig,
36
31
} from 'react-native-reanimated'
37
32
import {
38
-
Edge,
39
33
SafeAreaView,
40
34
useSafeAreaFrame,
41
35
useSafeAreaInsets,
42
36
} from 'react-native-safe-area-context'
43
37
import * as ScreenOrientation from 'expo-screen-orientation'
44
-
import {StatusBar} from 'expo-status-bar'
45
38
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
46
39
import {Trans} from '@lingui/macro'
47
40
48
-
import {Dimensions} from '#/lib/media/types'
41
+
import {type Dimensions} from '#/lib/media/types'
49
42
import {colors, s} from '#/lib/styles'
50
43
import {isIOS} from '#/platform/detection'
51
-
import {Lightbox} from '#/state/lightbox'
44
+
import {type Lightbox} from '#/state/lightbox'
52
45
import {Button} from '#/view/com/util/forms/Button'
53
46
import {Text} from '#/view/com/util/text/Text'
54
47
import {ScrollView} from '#/view/com/util/Views'
55
-
import {ios, useTheme} from '#/alf'
56
-
import {setNavigationBar} from '#/alf/util/navigationBar'
48
+
import {useTheme} from '#/alf'
49
+
import {setSystemUITheme} from '#/alf/util/systemUI'
57
50
import {PlatformInfo} from '../../../../../modules/expo-bluesky-swiss-army'
58
-
import {ImageSource, Transform} from './@types'
51
+
import {type ImageSource, type Transform} from './@types'
59
52
import ImageDefaultHeader from './components/ImageDefaultHeader'
60
53
import ImageItem from './components/ImageItem/ImageItem'
61
54
···
63
56
64
57
const PORTRAIT_UP = ScreenOrientation.OrientationLock.PORTRAIT_UP
65
58
const PIXEL_RATIO = PixelRatio.get()
66
-
const EDGES =
67
-
Platform.OS === 'android' && Platform.Version < 35
68
-
? (['top', 'bottom', 'left', 'right'] satisfies Edge[])
69
-
: ([] satisfies Edge[]) // iOS or Android 15+ bleeds into safe area
70
59
71
60
const SLOW_SPRING: WithSpringConfig = {
72
61
mass: isIOS ? 1.25 : 0.75,
···
167
156
168
157
return (
169
158
// Keep it always mounted to avoid flicker on the first frame.
170
-
<SafeAreaView
159
+
<View
171
160
style={[styles.screen, !activeLightbox && styles.screenHidden]}
172
-
edges={EDGES}
173
161
aria-modal
174
162
accessibilityViewIsModal
175
163
aria-hidden={!activeLightbox}>
···
197
185
/>
198
186
)}
199
187
</Animated.View>
200
-
</SafeAreaView>
188
+
</View>
201
189
)
202
190
}
203
191
···
325
313
},
326
314
)
327
315
328
-
// style nav bar on android
316
+
// style system ui on android
329
317
const t = useTheme()
330
318
useEffect(() => {
331
-
setNavigationBar('lightbox', t)
319
+
setSystemUITheme('lightbox', t)
332
320
return () => {
333
-
setNavigationBar('theme', t)
321
+
setSystemUITheme('theme', t)
334
322
}
335
323
}, [t])
336
324
337
325
return (
338
326
<Animated.View style={[styles.container, containerStyle]}>
339
-
<StatusBar
340
-
animated
341
-
style="light"
342
-
hideTransitionAnimation="slide"
343
-
backgroundColor="black"
344
-
// hiding causes layout shifts on android,
345
-
// so avoid until we add edge-to-edge mode
346
-
hidden={ios(isScaled || !showControls)}
327
+
<SystemBars
328
+
style={{statusBar: 'light', navigationBar: 'light'}}
329
+
hidden={{
330
+
statusBar: isScaled || !showControls,
331
+
navigationBar: false,
332
+
}}
347
333
/>
348
334
<Animated.View
349
335
style={[styles.backdrop, backdropStyle]}
+2
-2
src/view/com/modals/CreateOrEditList.tsx
+2
-2
src/view/com/modals/CreateOrEditList.tsx
···
8
8
TouchableOpacity,
9
9
View,
10
10
} from 'react-native'
11
-
import {Image as RNImage} from 'react-native-image-crop-picker'
11
+
import {type Image as RNImage} from 'react-native-image-crop-picker'
12
12
import {LinearGradient} from 'expo-linear-gradient'
13
-
import {AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api'
13
+
import {type AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api'
14
14
import {msg, Trans} from '@lingui/macro'
15
15
import {useLingui} from '@lingui/react'
16
16
+11
-14
src/view/shell/index.tsx
+11
-14
src/view/shell/index.tsx
···
1
1
import {useCallback, useEffect, useState} from 'react'
2
2
import {BackHandler, useWindowDimensions, View} from 'react-native'
3
3
import {Drawer} from 'react-native-drawer-layout'
4
+
import {SystemBars} from 'react-native-edge-to-edge'
4
5
import {Gesture} from 'react-native-gesture-handler'
5
6
import {useSafeAreaInsets} from 'react-native-safe-area-context'
6
-
import {StatusBar} from 'expo-status-bar'
7
7
import {useNavigation, useNavigationState} from '@react-navigation/native'
8
8
9
9
import {useDedupe} from '#/lib/hooks/useDedupe'
···
19
19
useIsDrawerSwipeDisabled,
20
20
useSetDrawerOpen,
21
21
} from '#/state/shell'
22
-
import {useLightStatusBar} from '#/state/shell/light-status-bar'
23
22
import {useCloseAnyActiveElement} from '#/state/util'
24
23
import {Lightbox} from '#/view/com/lightbox/Lightbox'
25
24
import {ModalsContainer} from '#/view/com/modals/Modal'
26
25
import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
27
26
import {atoms as a, select, useTheme} from '#/alf'
28
-
import {setNavigationBar} from '#/alf/util/navigationBar'
27
+
import {setSystemUITheme} from '#/alf/util/systemUI'
29
28
import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
30
29
import {SigninDialog} from '#/components/dialogs/Signin'
31
30
import {Outlet as PortalOutlet} from '#/components/Portal'
···
161
160
162
161
export const Shell: React.FC = function ShellImpl() {
163
162
const {fullyExpandedCount} = useDialogStateControlContext()
164
-
const lightStatusBar = useLightStatusBar()
165
163
const t = useTheme()
166
164
useIntentHandler()
167
165
168
166
useEffect(() => {
169
-
setNavigationBar('theme', t)
167
+
setSystemUITheme('theme', t)
170
168
}, [t])
171
169
172
170
return (
173
171
<View testID="mobileShellView" style={[a.h_full, t.atoms.bg]}>
174
-
<StatusBar
175
-
style={
176
-
t.name !== 'light' ||
177
-
(isIOS && fullyExpandedCount > 0) ||
178
-
lightStatusBar
179
-
? 'light'
180
-
: 'dark'
181
-
}
182
-
animated
172
+
<SystemBars
173
+
style={{
174
+
statusBar:
175
+
t.name !== 'light' || (isIOS && fullyExpandedCount > 0)
176
+
? 'light'
177
+
: 'dark',
178
+
navigationBar: t.name !== 'light' ? 'light' : 'dark',
179
+
}}
183
180
/>
184
181
<RoutesContainer>
185
182
<ShellInner />
+9
-17
yarn.lock
+9
-17
yarn.lock
···
11067
11067
dependencies:
11068
11068
invariant "^2.2.4"
11069
11069
11070
-
expo-navigation-bar@~4.0.9:
11071
-
version "4.0.9"
11072
-
resolved "https://registry.yarnpkg.com/expo-navigation-bar/-/expo-navigation-bar-4.0.9.tgz#e0409c2db8f6384d12c87f45c2674effc9fae1b6"
11073
-
integrity sha512-dCJ04yPixFOUixJaWlmCZafGeQ1L1g6vWn+oX8rqPnYN9kYCMUz2aRNnhRRoK5MBGFTK/nue2D49TE/AwwWt9w==
11074
-
dependencies:
11075
-
"@react-native/normalize-colors" "0.76.8"
11076
-
debug "^4.3.2"
11077
-
11078
11070
expo-notifications@~0.29.14:
11079
11071
version "0.29.14"
11080
11072
resolved "https://registry.yarnpkg.com/expo-notifications/-/expo-notifications-0.29.14.tgz#77beb6bc74b1b1abfa3adcab77fb6c9ea5d7d1b0"
···
11114
11106
integrity sha512-f+bPpF06bqiuW1Fbrd3nxeaSsmTVTBEKEYe3epYt4IE6y4Ulli3qEUamMLlRQiDGuIXPU6zQlscpy2mdBUI5cA==
11115
11107
dependencies:
11116
11108
"@expo/prebuild-config" "^8.0.27"
11117
-
11118
-
expo-status-bar@~2.0.1:
11119
-
version "2.0.1"
11120
-
resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-2.0.1.tgz#fc07726346dc30fbb68aadb0d7890b34fba42eee"
11121
-
integrity sha512-AkIPX7jWHRPp83UBZ1iXtVvyr0g+DgBVvIXTtlmPtmUsm8Vq9Bb5IGj86PW8osuFlgoTVAg7HI/+Ok7yEYwiRg==
11122
11109
11123
11110
expo-structured-headers@~4.0.0:
11124
11111
version "4.0.0"
···
16761
16748
dependencies:
16762
16749
use-latest-callback "^0.2.1"
16763
16750
16751
+
react-native-edge-to-edge@^1.6.0:
16752
+
version "1.6.0"
16753
+
resolved "https://registry.yarnpkg.com/react-native-edge-to-edge/-/react-native-edge-to-edge-1.6.0.tgz#2ba63b941704a7f713e298185c26cde4d9e4b973"
16754
+
integrity sha512-2WCNdE3Qd6Fwg9+4BpbATUxCLcouF6YRY7K+J36KJ4l3y+tWN6XCqAC4DuoGblAAbb2sLkhEDp4FOlbOIot2Og==
16755
+
16764
16756
react-native-emoji-popup@^0.1.2:
16765
16757
version "0.1.2"
16766
16758
resolved "https://registry.yarnpkg.com/react-native-emoji-popup/-/react-native-emoji-popup-0.1.2.tgz#7cd3874ba0496031e6f3e24de77e0df895168ce6"
···
16807
16799
resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.6.tgz#69ec13f70d76e9245e275eed4140d0873a78f902"
16808
16800
integrity sha512-1pHnFTlBahins6UAajXUqeCOHew9l9C2C8tErnpGC3IyLJzvxD+TpYAixnCbrVS52f7+NvMttbiSI290XfwN0w==
16809
16801
16810
-
react-native-keyboard-controller@^1.14.5:
16811
-
version "1.14.5"
16812
-
resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.14.5.tgz#ec1e7d1fb8ee18b69ced4d8ddd6fd99bdaaf14bb"
16813
-
integrity sha512-Cx7+SWI/P50i4PKJZN4T43RqoFkJ3GBoxjQ5ysrzZGoImHTF4j3atSwcBQGMmunKCem1yGOOQ84or+Vbcor6wQ==
16802
+
react-native-keyboard-controller@^1.17.1:
16803
+
version "1.17.1"
16804
+
resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.17.1.tgz#46efe148c1bdd0ee22094dcb2660f70f81e6544e"
16805
+
integrity sha512-YM3GYvtkuWimCKkZFURn5hIb1WiKOQqi2DijdwZSF5QSSzGqfqwzEEC3bm1xCN8HGHAEIXAaWIBUsc3Xp5L+Ng==
16814
16806
dependencies:
16815
16807
react-native-is-edge-to-edge "^1.1.6"
16816
16808