diff options
23 files changed, 137 insertions, 200 deletions
diff --git a/app.config.js b/app.config.js index 7b4937d3b..e197e6770 100644 --- a/app.config.js +++ b/app.config.js @@ -1,7 +1,5 @@ const pkg = require('./package.json') -const DARK_SPLASH_ANDROID_BACKGROUND = '#0f141b' - module.exports = function (config) { /** * App version number. Should be incremented as part of a release cycle. @@ -140,12 +138,10 @@ module.exports = function (config) { }, androidStatusBar: { barStyle: 'light-content', - backgroundColor: '#00000000', }, // Dark nav bar in light mode is better than light nav bar in dark mode androidNavigationBar: { barStyle: 'light-content', - backgroundColor: DARK_SPLASH_ANDROID_BACKGROUND, }, android: { icon: './assets/app-icons/android_icon_default_light.png', @@ -197,6 +193,10 @@ module.exports = function (config) { plugins: [ 'expo-video', 'expo-localization', + [ + 'react-native-edge-to-edge', + {android: {enforceNavigationBarContrast: false}}, + ], USE_SENTRY && [ '@sentry/react-native/expo', { @@ -240,7 +240,7 @@ module.exports = function (config) { './plugins/withAndroidManifestPlugin.js', './plugins/withAndroidManifestFCMIconPlugin.js', './plugins/withAndroidStylesAccentColorPlugin.js', - './plugins/withAndroidSplashScreenStatusBarTranslucentPlugin.js', + './plugins/withAndroidDayNightThemePlugin.js', './plugins/withAndroidNoJitpackPlugin.js', './plugins/withNoBundleCompression.js', './plugins/shareExtension/withShareExtensions.js', diff --git a/package.json b/package.json index 235b1a91b..8448fbd1e 100644 --- a/package.json +++ b/package.json @@ -141,12 +141,10 @@ "expo-linking": "~7.0.5", "expo-localization": "~16.0.1", "expo-media-library": "~17.0.6", - "expo-navigation-bar": "~4.0.9", "expo-notifications": "~0.29.14", "expo-screen-orientation": "~8.0.4", "expo-sharing": "~13.0.1", "expo-splash-screen": "~0.29.22", - "expo-status-bar": "~2.0.1", "expo-system-ui": "~4.0.9", "expo-task-manager": "~12.0.6", "expo-updates": "~0.27.4", @@ -178,12 +176,13 @@ "react-native-compressor": "1.10.3", "react-native-date-picker": "^5.0.7", "react-native-drawer-layout": "^4.1.1", + "react-native-edge-to-edge": "^1.6.0", "react-native-emoji-popup": "^0.1.2", "react-native-gesture-handler": "2.20.2", "react-native-get-random-values": "~1.11.0", "react-native-image-crop-picker": "^0.41.6", "react-native-ios-context-menu": "^1.15.3", - "react-native-keyboard-controller": "^1.14.5", + "react-native-keyboard-controller": "^1.17.1", "react-native-mmkv": "^2.12.2", "react-native-pager-view": "6.5.1", "react-native-picker-select": "^9.3.1", diff --git a/plugins/withAndroidDayNightThemePlugin.js b/plugins/withAndroidDayNightThemePlugin.js new file mode 100644 index 000000000..d9bc1b211 --- /dev/null +++ b/plugins/withAndroidDayNightThemePlugin.js @@ -0,0 +1,27 @@ +// Based on https://github.com/expo/expo/pull/33957 +// Could be removed once the app has been updated to Expo 53 +const {withAndroidStyles} = require('@expo/config-plugins') + +module.exports = function withAndroidDayNightThemePlugin(appConfig) { + const cleanupList = new Set([ + 'colorPrimary', + 'android:editTextBackground', + 'android:textColor', + 'android:editTextStyle', + ]) + + return withAndroidStyles(appConfig, config => { + config.modResults.resources.style = config.modResults.resources.style + ?.map(style => { + if (style.$.name === 'AppTheme' && style.item != null) { + style.item = style.item.filter(item => !cleanupList.has(item.$.name)) + } + return style + }) + .filter(style => { + return style.$.name !== 'ResetEditText' + }) + + return config + }) +} diff --git a/plugins/withAndroidSplashScreenStatusBarTranslucentPlugin.js b/plugins/withAndroidSplashScreenStatusBarTranslucentPlugin.js deleted file mode 100644 index 704ead054..000000000 --- a/plugins/withAndroidSplashScreenStatusBarTranslucentPlugin.js +++ /dev/null @@ -1,28 +0,0 @@ -const {withStringsXml, AndroidConfig} = require('@expo/config-plugins') - -module.exports = function withAndroidSplashScreenStatusBarTranslucentPlugin( - appConfig, -) { - return withStringsXml(appConfig, function (decoratedAppConfig) { - try { - decoratedAppConfig.modResults = AndroidConfig.Strings.setStringItem( - [ - { - _: 'true', - $: { - name: 'expo_splash_screen_status_bar_translucent', - translatable: 'false', - }, - }, - ], - decoratedAppConfig.modResults, - ) - } catch (e) { - console.error( - `withAndroidSplashScreenStatusBarTranslucentPlugin failed`, - e, - ) - } - return decoratedAppConfig - }) -} diff --git a/plugins/withAndroidStylesAccentColorPlugin.js b/plugins/withAndroidStylesAccentColorPlugin.js index c45553788..51dd44f35 100644 --- a/plugins/withAndroidStylesAccentColorPlugin.js +++ b/plugins/withAndroidStylesAccentColorPlugin.js @@ -12,7 +12,7 @@ module.exports = function withAndroidStylesAccentColorPlugin(appConfig) { decoratedAppConfig.modResults, { add: true, - parent: AndroidConfig.Styles.getAppThemeLightNoActionBarGroup(), + parent: AndroidConfig.Styles.getAppThemeGroup(), name: 'colorAccent', value: '@color/colorPrimary', }, diff --git a/src/App.native.tsx b/src/App.native.tsx index ac985e560..5023c48bb 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -46,14 +46,13 @@ import {Provider as ModerationOptsProvider} from '#/state/preferences/moderation import {Provider as UnreadNotifsProvider} from '#/state/queries/notifications/unread' import { Provider as SessionProvider, - SessionAccount, + type SessionAccount, useSession, useSessionApi, } from '#/state/session' import {readLastActiveAccount} from '#/state/session/util' import {Provider as ShellStateProvider} from '#/state/shell' import {Provider as ComposerProvider} from '#/state/shell/composer' -import {Provider as LightStatusBarProvider} from '#/state/shell/light-status-bar' import {Provider as LoggedOutViewProvider} from '#/state/shell/logged-out' import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide' import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed' @@ -219,9 +218,7 @@ function App() { <StarterPackProvider> <SafeAreaProvider initialMetrics={initialWindowMetrics}> - <LightStatusBarProvider> - <InnerApp /> - </LightStatusBarProvider> + <InnerApp /> </SafeAreaProvider> </StarterPackProvider> </BottomSheetProvider> diff --git a/src/App.web.tsx b/src/App.web.tsx index af39bee47..bbe23e5a5 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -35,14 +35,13 @@ import {Provider as ModerationOptsProvider} from '#/state/preferences/moderation import {Provider as UnreadNotifsProvider} from '#/state/queries/notifications/unread' import { Provider as SessionProvider, - SessionAccount, + type SessionAccount, useSession, useSessionApi, } from '#/state/session' import {readLastActiveAccount} from '#/state/session/util' import {Provider as ShellStateProvider} from '#/state/shell' import {Provider as ComposerProvider} from '#/state/shell/composer' -import {Provider as LightStatusBarProvider} from '#/state/shell/light-status-bar' import {Provider as LoggedOutViewProvider} from '#/state/shell/logged-out' import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide' import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed' @@ -193,9 +192,7 @@ function App() { <LightboxStateProvider> <PortalProvider> <StarterPackProvider> - <LightStatusBarProvider> - <InnerApp /> - </LightStatusBarProvider> + <InnerApp /> </StarterPackProvider> </PortalProvider> </LightboxStateProvider> diff --git a/src/alf/util/navigationBar.ts b/src/alf/util/navigationBar.ts deleted file mode 100644 index cb315f70a..000000000 --- a/src/alf/util/navigationBar.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as NavigationBar from 'expo-navigation-bar' -import * as SystemUI from 'expo-system-ui' - -import {isAndroid} from '#/platform/detection' -import {Theme} from '../types' - -export function setNavigationBar(themeType: 'theme' | 'lightbox', t: Theme) { - if (isAndroid) { - if (themeType === 'theme') { - NavigationBar.setBackgroundColorAsync(t.atoms.bg.backgroundColor) - NavigationBar.setBorderColorAsync(t.atoms.bg.backgroundColor) - NavigationBar.setButtonStyleAsync(t.name !== 'light' ? 'light' : 'dark') - SystemUI.setBackgroundColorAsync(t.atoms.bg.backgroundColor) - } else { - NavigationBar.setBackgroundColorAsync('black') - NavigationBar.setBorderColorAsync('black') - NavigationBar.setButtonStyleAsync('light') - SystemUI.setBackgroundColorAsync('black') - } - } -} diff --git a/src/alf/util/systemUI.ts b/src/alf/util/systemUI.ts new file mode 100644 index 000000000..c973e10ea --- /dev/null +++ b/src/alf/util/systemUI.ts @@ -0,0 +1,14 @@ +import * as SystemUI from 'expo-system-ui' + +import {isAndroid} from '#/platform/detection' +import {Theme} from '../types' + +export function setSystemUITheme(themeType: 'theme' | 'lightbox', t: Theme) { + if (isAndroid) { + if (themeType === 'theme') { + SystemUI.setBackgroundColorAsync(t.atoms.bg.backgroundColor) + } else { + SystemUI.setBackgroundColorAsync('black') + } + } +} diff --git a/src/components/ContextMenu/index.tsx b/src/components/ContextMenu/index.tsx index aebed6419..4a0814dfe 100644 --- a/src/components/ContextMenu/index.tsx +++ b/src/components/ContextMenu/index.tsx @@ -556,7 +556,7 @@ export function Outer({ // pure vibes based const TOP_INSET = insets.top + 80 const BOTTOM_INSET_IOS = insets.bottom + 20 - const BOTTOM_INSET_ANDROID = 12 // TODO: revisit when edge-to-edge mode is enabled -sfn + const BOTTOM_INSET_ANDROID = insets.bottom + 12 const {height} = evt.nativeEvent.layout const topPosition = diff --git a/src/components/Dialog/sheet-wrapper.ts b/src/components/Dialog/sheet-wrapper.ts index 37c663383..b655dde00 100644 --- a/src/components/Dialog/sheet-wrapper.ts +++ b/src/components/Dialog/sheet-wrapper.ts @@ -1,20 +1,25 @@ import {useCallback} from 'react' +import {SystemBars} from 'react-native-edge-to-edge' -import {useDialogStateControlContext} from '#/state/dialogs' +import {isIOS} from '#/platform/detection' /** * If we're calling a system API like the image picker that opens a sheet * wrap it in this function to make sure the status bar is the correct color. */ export function useSheetWrapper() { - const {setFullyExpandedCount} = useDialogStateControlContext() - return useCallback( - async <T>(promise: Promise<T>): Promise<T> => { - setFullyExpandedCount(c => c + 1) + return useCallback(async <T>(promise: Promise<T>): Promise<T> => { + if (isIOS) { + const entry = SystemBars.pushStackEntry({ + style: { + statusBar: 'light', + }, + }) const res = await promise - setFullyExpandedCount(c => c - 1) + SystemBars.popStackEntry(entry) return res - }, - [setFullyExpandedCount], - ) + } else { + return await promise + } + }, []) } diff --git a/src/lib/hooks/useEnableKeyboardController.tsx b/src/lib/hooks/useEnableKeyboardController.tsx index 366791c0c..858f6943a 100644 --- a/src/lib/hooks/useEnableKeyboardController.tsx +++ b/src/lib/hooks/useEnableKeyboardController.tsx @@ -26,10 +26,7 @@ export function KeyboardControllerProvider({ children: React.ReactNode }) { return ( - <KeyboardProvider - enabled={false} - // I don't think this is necessary, but Chesterton's fence and all that -sfn - statusBarTranslucent={true}> + <KeyboardProvider enabled={false}> <KeyboardControllerProviderInner> {children} </KeyboardControllerProviderInner> diff --git a/src/screens/Login/index.tsx b/src/screens/Login/index.tsx index 8ed8d2da8..e4e2f43f0 100644 --- a/src/screens/Login/index.tsx +++ b/src/screens/Login/index.tsx @@ -8,7 +8,7 @@ import {DEFAULT_SERVICE} from '#/lib/constants' import {logEvent} from '#/lib/statsig/statsig' import {logger} from '#/logger' import {useServiceQuery} from '#/state/queries/service' -import {SessionAccount, useSession} from '#/state/session' +import {type SessionAccount, useSession} from '#/state/session' import {useLoggedOutView} from '#/state/shell/logged-out' import {LoggedOutLayout} from '#/view/com/util/layouts/LoggedOutLayout' import {ForgotPasswordForm} from '#/screens/Login/ForgotPasswordForm' diff --git a/src/screens/Messages/components/MessageInput.tsx b/src/screens/Messages/components/MessageInput.tsx index ac0f7969f..69cba07f7 100644 --- a/src/screens/Messages/components/MessageInput.tsx +++ b/src/screens/Messages/components/MessageInput.tsx @@ -24,9 +24,9 @@ import { useMessageDraft, useSaveMessageDraft, } from '#/state/messages/message-drafts' -import {EmojiPickerPosition} from '#/view/com/composer/text-input/web/EmojiPicker.web' +import {type EmojiPickerPosition} from '#/view/com/composer/text-input/web/EmojiPicker.web' import * as Toast from '#/view/com/util/Toast' -import {atoms as a, useTheme} from '#/alf' +import {android, atoms as a, useTheme} from '#/alf' import {useSharedInputStyles} from '#/components/forms/TextField' import {PaperPlane_Stroke2_Corner0_Rounded as PaperPlane} from '#/components/icons/PaperPlane' import {useExtractEmbedFromFacets} from './MessageInputEmbed' @@ -174,6 +174,7 @@ export function MessageInput({ a.text_md, a.px_sm, t.atoms.text, + android({paddingTop: 0}), {paddingBottom: isIOS ? 5 : 0}, animatedStyle, ]} diff --git a/src/screens/SignupQueued.tsx b/src/screens/SignupQueued.tsx index 823ed0784..6a2c5bbc7 100644 --- a/src/screens/SignupQueued.tsx +++ b/src/screens/SignupQueued.tsx @@ -1,7 +1,7 @@ import React from 'react' import {Modal, ScrollView, View} from 'react-native' +import {SystemBars} from 'react-native-edge-to-edge' import {useSafeAreaInsets} from 'react-native-safe-area-context' -import {StatusBar} from 'expo-status-bar' import {msg, plural, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -106,7 +106,7 @@ export function SignupQueued() { animationType={native('slide')} presentationStyle="formSheet" style={[web(a.util_screen_outer)]}> - {isIOS && <StatusBar style="light" />} + {isIOS && <SystemBars style={{statusBar: 'light'}} />} <ScrollView style={[a.flex_1, t.atoms.bg]} contentContainerStyle={{borderWidth: 0}} diff --git a/src/screens/Takendown.tsx b/src/screens/Takendown.tsx index c714a775e..ef3e93658 100644 --- a/src/screens/Takendown.tsx +++ b/src/screens/Takendown.tsx @@ -1,8 +1,8 @@ import {useMemo, useState} from 'react' import {Modal, View} from 'react-native' +import {SystemBars} from 'react-native-edge-to-edge' import {KeyboardAwareScrollView} from 'react-native-keyboard-controller' import {useSafeAreaInsets} from 'react-native-safe-area-context' -import {StatusBar} from 'expo-status-bar' import {ComAtprotoAdminDefs, ComAtprotoModerationDefs} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -126,7 +126,7 @@ export function Takendown() { animationType={native('slide')} presentationStyle="formSheet" style={[web(a.util_screen_outer)]}> - {isIOS && <StatusBar style="light" />} + {isIOS && <SystemBars style={{statusBar: 'light'}} />} <KeyboardAwareScrollView style={[a.flex_1, t.atoms.bg]} centerContent> <View style={[ diff --git a/src/screens/VideoFeed/index.tsx b/src/screens/VideoFeed/index.tsx index 04c2d7792..344b93429 100644 --- a/src/screens/VideoFeed/index.tsx +++ b/src/screens/VideoFeed/index.tsx @@ -8,6 +8,7 @@ import { ViewabilityConfig, ViewToken, } from 'react-native' +import {SystemBars} from 'react-native-edge-to-edge' import { Gesture, GestureDetector, @@ -77,7 +78,7 @@ import {PostCtrls} from '#/view/com/util/post-ctrls/PostCtrls' import {UserAvatar} from '#/view/com/util/UserAvatar' import {Header} from '#/screens/VideoFeed/components/Header' import {atoms as a, ios, platform, ThemeProvider, useTheme} from '#/alf' -import {setNavigationBar} from '#/alf/util/navigationBar' +import {setSystemUITheme} from '#/alf/util/systemUI' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {Divider} from '#/components/Divider' import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeftIcon} from '#/components/icons/Arrow' @@ -126,10 +127,10 @@ export function VideoFeed({}: NativeStackScreenProps< useFocusEffect( useCallback(() => { setMinShellMode(true) - setNavigationBar('lightbox', t) + setSystemUITheme('lightbox', t) return () => { setMinShellMode(false) - setNavigationBar('theme', t) + setSystemUITheme('theme', t) } }, [setMinShellMode, t]), ) @@ -140,6 +141,7 @@ export function VideoFeed({}: NativeStackScreenProps< return ( <ThemeProvider theme="dark"> <Layout.Screen noInsetTop style={{backgroundColor: 'black'}}> + <SystemBars style={{statusBar: 'light', navigationBar: 'light'}} /> <View style={[ a.absolute, diff --git a/src/state/shell/light-status-bar.tsx b/src/state/shell/light-status-bar.tsx index 6f47689d1..80df9ad90 100644 --- a/src/state/shell/light-status-bar.tsx +++ b/src/state/shell/light-status-bar.tsx @@ -1,44 +1,17 @@ -import {createContext, useContext, useEffect, useState} from 'react' - -import {isWeb} from '#/platform/detection' - -const LightStatusBarRefCountContext = createContext<boolean>(false) -const SetLightStatusBarRefCountContext = createContext<React.Dispatch< - React.SetStateAction<number> -> | null>(null) - -export function useLightStatusBar() { - return useContext(LightStatusBarRefCountContext) -} +import {useEffect} from 'react' +import {SystemBars} from 'react-native-edge-to-edge' export function useSetLightStatusBar(enabled: boolean) { - const setRefCount = useContext(SetLightStatusBarRefCountContext) useEffect(() => { - // noop on web -sfn - if (isWeb) return - - if (!setRefCount) { - if (__DEV__) - console.error( - 'useLightStatusBar was used without a SetLightStatusBarRefCountContext provider', - ) - return - } if (enabled) { - setRefCount(prev => prev + 1) - return () => setRefCount(prev => prev - 1) + const entry = SystemBars.pushStackEntry({ + style: { + statusBar: 'light', + }, + }) + return () => { + SystemBars.popStackEntry(entry) + } } - }, [enabled, setRefCount]) -} - -export function Provider({children}: React.PropsWithChildren<{}>) { - const [refCount, setRefCount] = useState(0) - - return ( - <SetLightStatusBarRefCountContext.Provider value={setRefCount}> - <LightStatusBarRefCountContext.Provider value={refCount > 0}> - {children} - </LightStatusBarRefCountContext.Provider> - </SetLightStatusBarRefCountContext.Provider> - ) + }, [enabled]) } diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 4384783dc..aa27adb3d 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -1464,8 +1464,7 @@ function useKeyboardVerticalOffset() { // Android etc if (!isIOS) { - // if Android <35 or web, bottom is 0 anyway. if >=35, this is needed to account - // for the edge-to-edge nav bar + // need to account for the edge-to-edge nav bar return bottom * -1 } diff --git a/src/view/com/lightbox/ImageViewing/index.tsx b/src/view/com/lightbox/ImageViewing/index.tsx index 7018d753a..41a54eba6 100644 --- a/src/view/com/lightbox/ImageViewing/index.tsx +++ b/src/view/com/lightbox/ImageViewing/index.tsx @@ -9,22 +9,17 @@ // https://github.com/jobtoday/react-native-image-viewing import React, {useCallback, useEffect, useMemo, useState} from 'react' -import { - LayoutAnimation, - PixelRatio, - Platform, - StyleSheet, - View, -} from 'react-native' +import {LayoutAnimation, PixelRatio, StyleSheet, View} from 'react-native' +import {SystemBars} from 'react-native-edge-to-edge' import {Gesture} from 'react-native-gesture-handler' import PagerView from 'react-native-pager-view' import Animated, { - AnimatedRef, + type AnimatedRef, cancelAnimation, interpolate, measure, runOnJS, - SharedValue, + type SharedValue, useAnimatedReaction, useAnimatedRef, useAnimatedStyle, @@ -32,30 +27,28 @@ import Animated, { useSharedValue, withDecay, withSpring, - WithSpringConfig, + type WithSpringConfig, } from 'react-native-reanimated' import { - Edge, SafeAreaView, useSafeAreaFrame, useSafeAreaInsets, } from 'react-native-safe-area-context' import * as ScreenOrientation from 'expo-screen-orientation' -import {StatusBar} from 'expo-status-bar' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Trans} from '@lingui/macro' -import {Dimensions} from '#/lib/media/types' +import {type Dimensions} from '#/lib/media/types' import {colors, s} from '#/lib/styles' import {isIOS} from '#/platform/detection' -import {Lightbox} from '#/state/lightbox' +import {type Lightbox} from '#/state/lightbox' import {Button} from '#/view/com/util/forms/Button' import {Text} from '#/view/com/util/text/Text' import {ScrollView} from '#/view/com/util/Views' -import {ios, useTheme} from '#/alf' -import {setNavigationBar} from '#/alf/util/navigationBar' +import {useTheme} from '#/alf' +import {setSystemUITheme} from '#/alf/util/systemUI' import {PlatformInfo} from '../../../../../modules/expo-bluesky-swiss-army' -import {ImageSource, Transform} from './@types' +import {type ImageSource, type Transform} from './@types' import ImageDefaultHeader from './components/ImageDefaultHeader' import ImageItem from './components/ImageItem/ImageItem' @@ -63,10 +56,6 @@ type Rect = {x: number; y: number; width: number; height: number} const PORTRAIT_UP = ScreenOrientation.OrientationLock.PORTRAIT_UP const PIXEL_RATIO = PixelRatio.get() -const EDGES = - Platform.OS === 'android' && Platform.Version < 35 - ? (['top', 'bottom', 'left', 'right'] satisfies Edge[]) - : ([] satisfies Edge[]) // iOS or Android 15+ bleeds into safe area const SLOW_SPRING: WithSpringConfig = { mass: isIOS ? 1.25 : 0.75, @@ -167,9 +156,8 @@ export default function ImageViewRoot({ return ( // Keep it always mounted to avoid flicker on the first frame. - <SafeAreaView + <View style={[styles.screen, !activeLightbox && styles.screenHidden]} - edges={EDGES} aria-modal accessibilityViewIsModal aria-hidden={!activeLightbox}> @@ -197,7 +185,7 @@ export default function ImageViewRoot({ /> )} </Animated.View> - </SafeAreaView> + </View> ) } @@ -325,25 +313,23 @@ function ImageView({ }, ) - // style nav bar on android + // style system ui on android const t = useTheme() useEffect(() => { - setNavigationBar('lightbox', t) + setSystemUITheme('lightbox', t) return () => { - setNavigationBar('theme', t) + setSystemUITheme('theme', t) } }, [t]) return ( <Animated.View style={[styles.container, containerStyle]}> - <StatusBar - animated - style="light" - hideTransitionAnimation="slide" - backgroundColor="black" - // hiding causes layout shifts on android, - // so avoid until we add edge-to-edge mode - hidden={ios(isScaled || !showControls)} + <SystemBars + style={{statusBar: 'light', navigationBar: 'light'}} + hidden={{ + statusBar: isScaled || !showControls, + navigationBar: false, + }} /> <Animated.View style={[styles.backdrop, backdropStyle]} diff --git a/src/view/com/modals/CreateOrEditList.tsx b/src/view/com/modals/CreateOrEditList.tsx index 3a1678954..0e4e23b97 100644 --- a/src/view/com/modals/CreateOrEditList.tsx +++ b/src/view/com/modals/CreateOrEditList.tsx @@ -8,9 +8,9 @@ import { TouchableOpacity, View, } from 'react-native' -import {Image as RNImage} from 'react-native-image-crop-picker' +import {type Image as RNImage} from 'react-native-image-crop-picker' import {LinearGradient} from 'expo-linear-gradient' -import {AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api' +import {type AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' diff --git a/src/view/shell/index.tsx b/src/view/shell/index.tsx index 3fbc9a3f3..3d3a5520c 100644 --- a/src/view/shell/index.tsx +++ b/src/view/shell/index.tsx @@ -1,9 +1,9 @@ import {useCallback, useEffect, useState} from 'react' import {BackHandler, useWindowDimensions, View} from 'react-native' import {Drawer} from 'react-native-drawer-layout' +import {SystemBars} from 'react-native-edge-to-edge' import {Gesture} from 'react-native-gesture-handler' import {useSafeAreaInsets} from 'react-native-safe-area-context' -import {StatusBar} from 'expo-status-bar' import {useNavigation, useNavigationState} from '@react-navigation/native' import {useDedupe} from '#/lib/hooks/useDedupe' @@ -19,13 +19,12 @@ import { useIsDrawerSwipeDisabled, useSetDrawerOpen, } from '#/state/shell' -import {useLightStatusBar} from '#/state/shell/light-status-bar' import {useCloseAnyActiveElement} from '#/state/util' import {Lightbox} from '#/view/com/lightbox/Lightbox' import {ModalsContainer} from '#/view/com/modals/Modal' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {atoms as a, select, useTheme} from '#/alf' -import {setNavigationBar} from '#/alf/util/navigationBar' +import {setSystemUITheme} from '#/alf/util/systemUI' import {MutedWordsDialog} from '#/components/dialogs/MutedWords' import {SigninDialog} from '#/components/dialogs/Signin' import {Outlet as PortalOutlet} from '#/components/Portal' @@ -161,25 +160,23 @@ function ShellInner() { export const Shell: React.FC = function ShellImpl() { const {fullyExpandedCount} = useDialogStateControlContext() - const lightStatusBar = useLightStatusBar() const t = useTheme() useIntentHandler() useEffect(() => { - setNavigationBar('theme', t) + setSystemUITheme('theme', t) }, [t]) return ( <View testID="mobileShellView" style={[a.h_full, t.atoms.bg]}> - <StatusBar - style={ - t.name !== 'light' || - (isIOS && fullyExpandedCount > 0) || - lightStatusBar - ? 'light' - : 'dark' - } - animated + <SystemBars + style={{ + statusBar: + t.name !== 'light' || (isIOS && fullyExpandedCount > 0) + ? 'light' + : 'dark', + navigationBar: t.name !== 'light' ? 'light' : 'dark', + }} /> <RoutesContainer> <ShellInner /> diff --git a/yarn.lock b/yarn.lock index 71631527a..073c147ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11067,14 +11067,6 @@ expo-modules-core@^2.1.1: dependencies: invariant "^2.2.4" -expo-navigation-bar@~4.0.9: - version "4.0.9" - resolved "https://registry.yarnpkg.com/expo-navigation-bar/-/expo-navigation-bar-4.0.9.tgz#e0409c2db8f6384d12c87f45c2674effc9fae1b6" - integrity sha512-dCJ04yPixFOUixJaWlmCZafGeQ1L1g6vWn+oX8rqPnYN9kYCMUz2aRNnhRRoK5MBGFTK/nue2D49TE/AwwWt9w== - dependencies: - "@react-native/normalize-colors" "0.76.8" - debug "^4.3.2" - expo-notifications@~0.29.14: version "0.29.14" resolved "https://registry.yarnpkg.com/expo-notifications/-/expo-notifications-0.29.14.tgz#77beb6bc74b1b1abfa3adcab77fb6c9ea5d7d1b0" @@ -11115,11 +11107,6 @@ expo-splash-screen@~0.29.22: dependencies: "@expo/prebuild-config" "^8.0.27" -expo-status-bar@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-2.0.1.tgz#fc07726346dc30fbb68aadb0d7890b34fba42eee" - integrity sha512-AkIPX7jWHRPp83UBZ1iXtVvyr0g+DgBVvIXTtlmPtmUsm8Vq9Bb5IGj86PW8osuFlgoTVAg7HI/+Ok7yEYwiRg== - expo-structured-headers@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/expo-structured-headers/-/expo-structured-headers-4.0.0.tgz#85537ae6daec61ebfb214ede4107c8841c6e16d0" @@ -16761,6 +16748,11 @@ react-native-drawer-layout@^4.1.1: dependencies: use-latest-callback "^0.2.1" +react-native-edge-to-edge@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/react-native-edge-to-edge/-/react-native-edge-to-edge-1.6.0.tgz#2ba63b941704a7f713e298185c26cde4d9e4b973" + integrity sha512-2WCNdE3Qd6Fwg9+4BpbATUxCLcouF6YRY7K+J36KJ4l3y+tWN6XCqAC4DuoGblAAbb2sLkhEDp4FOlbOIot2Og== + react-native-emoji-popup@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/react-native-emoji-popup/-/react-native-emoji-popup-0.1.2.tgz#7cd3874ba0496031e6f3e24de77e0df895168ce6" @@ -16807,10 +16799,10 @@ react-native-is-edge-to-edge@^1.1.6: resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.6.tgz#69ec13f70d76e9245e275eed4140d0873a78f902" integrity sha512-1pHnFTlBahins6UAajXUqeCOHew9l9C2C8tErnpGC3IyLJzvxD+TpYAixnCbrVS52f7+NvMttbiSI290XfwN0w== -react-native-keyboard-controller@^1.14.5: - version "1.14.5" - resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.14.5.tgz#ec1e7d1fb8ee18b69ced4d8ddd6fd99bdaaf14bb" - integrity sha512-Cx7+SWI/P50i4PKJZN4T43RqoFkJ3GBoxjQ5ysrzZGoImHTF4j3atSwcBQGMmunKCem1yGOOQ84or+Vbcor6wQ== +react-native-keyboard-controller@^1.17.1: + version "1.17.1" + resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.17.1.tgz#46efe148c1bdd0ee22094dcb2660f70f81e6544e" + integrity sha512-YM3GYvtkuWimCKkZFURn5hIb1WiKOQqi2DijdwZSF5QSSzGqfqwzEEC3bm1xCN8HGHAEIXAaWIBUsc3Xp5L+Ng== dependencies: react-native-is-edge-to-edge "^1.1.6" |