diff --git a/.imgbotconfig b/.imgbotconfig index ff5c3345cc4d..43d1b77166cc 100644 --- a/.imgbotconfig +++ b/.imgbotconfig @@ -1,6 +1,7 @@ { "ignoredFiles": [ - "assets/images/empty-state_background-fade.png" // Caused an issue with colour gradients, https://github.com/Expensify/App/issues/30499 + "assets/images/empty-state_background-fade-dark.png", // Caused an issue with colour gradients, https://github.com/Expensify/App/issues/30499 + "assets/images/empty-state_background-fade-light.png" ], "aggressiveCompression": "false" } diff --git a/src/App.js b/src/App.js index ac34ece5c6c7..2caa6b9ffc29 100644 --- a/src/App.js +++ b/src/App.js @@ -6,8 +6,10 @@ import Onyx from 'react-native-onyx'; import {PickerStateProvider} from 'react-native-picker-select'; import {SafeAreaProvider} from 'react-native-safe-area-context'; import '../wdyr'; +import ColorSchemeWrapper from './components/ColorSchemeWrapper'; import ComposeProviders from './components/ComposeProviders'; import CustomStatusBar from './components/CustomStatusBar'; +import CustomStatusBarContextProvider from './components/CustomStatusBar/CustomStatusBarContextProvider'; import ErrorBoundary from './components/ErrorBoundary'; import HTMLEngineProvider from './components/HTMLEngineProvider'; import {LocaleContextProvider} from './components/LocaleContextProvider'; @@ -66,11 +68,14 @@ function App() { ThemeProvider, ThemeStylesProvider, ThemeIllustrationsProvider, + CustomStatusBarContextProvider, ]} > - + + + diff --git a/src/CONST.ts b/src/CONST.ts index 8d928df71ef0..d813d3e016c2 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -699,6 +699,14 @@ const CONST = { DARK: 'dark', SYSTEM: 'system', }, + COLOR_SCHEME: { + LIGHT: 'light', + DARK: 'dark', + }, + STATUS_BAR_STYLE: { + LIGHT_CONTENT: 'light-content', + DARK_CONTENT: 'dark-content', + }, TRANSACTION: { DEFAULT_MERCHANT: 'Request', UNKNOWN_MERCHANT: 'Unknown Merchant', diff --git a/src/components/ColorSchemeWrapper/index.native.tsx b/src/components/ColorSchemeWrapper/index.native.tsx new file mode 100644 index 000000000000..9f68ee97d330 --- /dev/null +++ b/src/components/ColorSchemeWrapper/index.native.tsx @@ -0,0 +1,5 @@ +function ColorSchemeWrapper({children}: React.PropsWithChildren) { + return children; +} + +export default ColorSchemeWrapper; diff --git a/src/components/ColorSchemeWrapper/index.tsx b/src/components/ColorSchemeWrapper/index.tsx new file mode 100644 index 000000000000..577ccf9f3794 --- /dev/null +++ b/src/components/ColorSchemeWrapper/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import {View} from 'react-native'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; + +function ColorSchemeWrapper({children}: React.PropsWithChildren): React.ReactElement { + const theme = useTheme(); + const themeStyles = useThemeStyles(); + + return {children}; +} + +export default ColorSchemeWrapper; diff --git a/src/components/CustomStatusBar/CustomStatusBarContext.tsx b/src/components/CustomStatusBar/CustomStatusBarContext.tsx new file mode 100644 index 000000000000..b2c317b05c25 --- /dev/null +++ b/src/components/CustomStatusBar/CustomStatusBarContext.tsx @@ -0,0 +1,11 @@ +import {createContext} from 'react'; + +type CustomStatusBarContextType = { + isRootStatusBarDisabled: boolean; + disableRootStatusBar: (isDisabled: boolean) => void; +}; + +const CustomStatusBarContext = createContext({isRootStatusBarDisabled: false, disableRootStatusBar: () => undefined}); + +export default CustomStatusBarContext; +export {type CustomStatusBarContextType}; diff --git a/src/components/CustomStatusBar/CustomStatusBarContextProvider.tsx b/src/components/CustomStatusBar/CustomStatusBarContextProvider.tsx new file mode 100644 index 000000000000..27a5cac5d8cc --- /dev/null +++ b/src/components/CustomStatusBar/CustomStatusBarContextProvider.tsx @@ -0,0 +1,17 @@ +import React, {useMemo, useState} from 'react'; +import CustomStatusBarContext from './CustomStatusBarContext'; + +function CustomStatusBarContextProvider({children}: React.PropsWithChildren) { + const [isRootStatusBarDisabled, disableRootStatusBar] = useState(false); + const value = useMemo( + () => ({ + isRootStatusBarDisabled, + disableRootStatusBar, + }), + [isRootStatusBarDisabled], + ); + + return {children}; +} + +export default CustomStatusBarContextProvider; diff --git a/src/components/CustomStatusBar/index.android.tsx b/src/components/CustomStatusBar/index.android.tsx deleted file mode 100644 index 81b4f1d25f67..000000000000 --- a/src/components/CustomStatusBar/index.android.tsx +++ /dev/null @@ -1,15 +0,0 @@ -/** - * On Android we setup the status bar in native code. - */ -import type CustomStatusBarType from './types'; - -// eslint-disable-next-line react/function-component-definition -const CustomStatusBar: CustomStatusBarType = () => - // Prefer to not render the StatusBar component in Android as it can cause - // issues with edge to edge display. We setup the status bar appearance in - // MainActivity.java and styles.xml. - null; - -CustomStatusBar.displayName = 'CustomStatusBar'; - -export default CustomStatusBar; diff --git a/src/components/CustomStatusBar/index.tsx b/src/components/CustomStatusBar/index.tsx index c5c013c2bef9..2e4994378264 100644 --- a/src/components/CustomStatusBar/index.tsx +++ b/src/components/CustomStatusBar/index.tsx @@ -1,29 +1,91 @@ -import React, {useEffect} from 'react'; -import Navigation, {navigationRef} from '@libs/Navigation/Navigation'; +import {EventListenerCallback, NavigationContainerEventMap} from '@react-navigation/native'; +import PropTypes from 'prop-types'; +import React, {useCallback, useContext, useEffect} from 'react'; +import {navigationRef} from '@libs/Navigation/Navigation'; import StatusBar from '@libs/StatusBar'; import useTheme from '@styles/themes/useTheme'; -import type CustomStatusBarType from './types'; +import CustomStatusBarContext from './CustomStatusBarContext'; + +type CustomStatusBarProps = { + isNested: boolean; +}; + +const propTypes = { + /** Whether the CustomStatusBar is nested within another CustomStatusBar. + * A nested CustomStatusBar will disable the "root" CustomStatusBar. */ + isNested: PropTypes.bool, +}; + +type CustomStatusBarType = { + (props: CustomStatusBarProps): React.ReactNode; + displayName: string; + propTypes: typeof propTypes; +}; // eslint-disable-next-line react/function-component-definition -const CustomStatusBar: CustomStatusBarType = () => { +const CustomStatusBar: CustomStatusBarType = ({isNested = false}) => { + const {isRootStatusBarDisabled, disableRootStatusBar} = useContext(CustomStatusBarContext); const theme = useTheme(); + + const isDisabled = !isNested && isRootStatusBarDisabled; + useEffect(() => { - Navigation.isNavigationReady().then(() => { - // Set the status bar colour depending on the current route. - // If we don't have any colour defined for a route, fall back to - // appBG color. - const currentRoute = navigationRef.getCurrentRoute(); - let currentScreenBackgroundColor = theme.appBG; - if (currentRoute && 'name' in currentRoute && currentRoute.name in theme.PAGE_BACKGROUND_COLORS) { - currentScreenBackgroundColor = theme.PAGE_BACKGROUND_COLORS[currentRoute.name]; + if (isNested) { + disableRootStatusBar(true); + } + + return () => { + if (!isNested) { + return; } - StatusBar.setBarStyle('light-content', true); - StatusBar.setBackgroundColor(currentScreenBackgroundColor); - }); - }, [theme.PAGE_BACKGROUND_COLORS, theme.appBG]); + disableRootStatusBar(false); + }; + }, [disableRootStatusBar, isNested]); + + const updateStatusBarStyle = useCallback>(() => { + if (isDisabled) { + return; + } + + // Set the status bar colour depending on the current route. + // If we don't have any colour defined for a route, fall back to + // appBG color. + const currentRoute = navigationRef.getCurrentRoute(); + + let currentScreenBackgroundColor = theme.appBG; + let statusBarStyle = theme.statusBarStyle; + if (currentRoute && 'name' in currentRoute && currentRoute.name in theme.PAGE_THEMES) { + const screenTheme = theme.PAGE_THEMES[currentRoute.name]; + currentScreenBackgroundColor = screenTheme.backgroundColor; + statusBarStyle = screenTheme.statusBarStyle; + } + + StatusBar.setBackgroundColor(currentScreenBackgroundColor, true); + StatusBar.setBarStyle(statusBarStyle, true); + }, [isDisabled, theme.PAGE_THEMES, theme.appBG, theme.statusBarStyle]); + + useEffect(() => { + navigationRef.addListener('state', updateStatusBarStyle); + + return () => navigationRef.removeListener('state', updateStatusBarStyle); + }, [updateStatusBarStyle]); + + useEffect(() => { + if (isDisabled) { + return; + } + + StatusBar.setBarStyle(theme.statusBarStyle, true); + }, [isDisabled, theme.statusBarStyle]); + + if (isDisabled) { + return null; + } + return ; }; CustomStatusBar.displayName = 'CustomStatusBar'; +CustomStatusBar.propTypes = propTypes; export default CustomStatusBar; diff --git a/src/components/CustomStatusBar/types.ts b/src/components/CustomStatusBar/types.ts deleted file mode 100644 index 7fecd02beba0..000000000000 --- a/src/components/CustomStatusBar/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -type CustomStatusBar = { - (): React.ReactNode; - displayName: string; -}; - -export default CustomStatusBar; diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx index cbb2e62161f3..7c0b9ef4fc8c 100644 --- a/src/libs/Navigation/NavigationRoot.tsx +++ b/src/libs/Navigation/NavigationRoot.tsx @@ -1,12 +1,9 @@ import {DefaultTheme, getPathFromState, NavigationContainer, NavigationState} from '@react-navigation/native'; import React, {useEffect, useMemo, useRef} from 'react'; -import {ColorValue} from 'react-native'; -import {interpolateColor, runOnJS, useAnimatedReaction, useSharedValue, withDelay, withTiming} from 'react-native-reanimated'; import useCurrentReportID from '@hooks/useCurrentReportID'; import useFlipper from '@hooks/useFlipper'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Log from '@libs/Log'; -import StatusBar from '@libs/StatusBar'; import useTheme from '@styles/themes/useTheme'; import AppNavigator from './AppNavigator'; import linkingConfig from './linkingConfig'; @@ -42,8 +39,8 @@ function parseAndLogRoute(state: NavigationState) { function NavigationRoot({authenticated, onReady}: NavigationRootProps) { useFlipper(navigationRef); - const theme = useTheme(); const firstRenderRef = useRef(true); + const theme = useTheme(); const currentReportIDValue = useCurrentReportID(); const {isSmallScreenWidth} = useWindowDimensions(); @@ -82,46 +79,6 @@ function NavigationRoot({authenticated, onReady}: NavigationRootProps) { navigationRef.resetRoot(navigationRef.getRootState()); }, [isSmallScreenWidth, authenticated]); - const prevStatusBarBackgroundColor = useRef(theme.appBG); - const statusBarBackgroundColor = useRef(theme.appBG); - const statusBarAnimation = useSharedValue(0); - - const updateStatusBarBackgroundColor = (color: ColorValue) => StatusBar.setBackgroundColor(color); - useAnimatedReaction( - () => statusBarAnimation.value, - (current, previous) => { - // Do not run if either of the animated value is null - // or previous animated value is greater than or equal to the current one - if (previous === null || current === null || current <= previous) { - return; - } - const color = interpolateColor(statusBarAnimation.value, [0, 1], [prevStatusBarBackgroundColor.current, statusBarBackgroundColor.current]); - runOnJS(updateStatusBarBackgroundColor)(color); - }, - ); - - const animateStatusBarBackgroundColor = () => { - const currentRoute = navigationRef.getCurrentRoute(); - - const backgroundColorFromRoute = - currentRoute?.params && 'backgroundColor' in currentRoute.params && typeof currentRoute.params.backgroundColor === 'string' && currentRoute.params.backgroundColor; - const backgroundColorFallback = currentRoute?.name ? theme.PAGE_BACKGROUND_COLORS[currentRoute.name] || theme.appBG : theme.appBG; - - // It's possible for backgroundColorFromRoute to be empty string, so we must use "||" to fallback to backgroundColorFallback. - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const currentScreenBackgroundColor = backgroundColorFromRoute || backgroundColorFallback; - - prevStatusBarBackgroundColor.current = statusBarBackgroundColor.current; - statusBarBackgroundColor.current = currentScreenBackgroundColor; - - if (currentScreenBackgroundColor === theme.appBG && prevStatusBarBackgroundColor.current === theme.appBG) { - return; - } - - statusBarAnimation.value = 0; - statusBarAnimation.value = withDelay(300, withTiming(1)); - }; - const handleStateChange = (state: NavigationState | undefined) => { if (!state) { return; @@ -132,7 +89,6 @@ function NavigationRoot({authenticated, onReady}: NavigationRootProps) { currentReportIDValue?.updateCurrentReportID(state); }, 0); parseAndLogRoute(state); - animateStatusBarBackgroundColor(); }; return ( diff --git a/src/libs/StatusBar/index.android.ts b/src/libs/StatusBar/index.android.ts index c928f0949665..6cbf1a762a0b 100644 --- a/src/libs/StatusBar/index.android.ts +++ b/src/libs/StatusBar/index.android.ts @@ -1,10 +1,14 @@ import StatusBar from './types'; -// Only has custom web implementation -StatusBar.getBackgroundColor = () => null; +const setBackgroundColor = StatusBar.setBackgroundColor; -// We override this because it's not used – on Android our app display edge-to-edge. -// Also because Reanimated's interpolateColor gives Android native colors instead of hex strings, causing this to display a warning. -StatusBar.setBackgroundColor = () => null; +let statusBarColor: string | null = null; + +StatusBar.getBackgroundColor = () => statusBarColor; + +StatusBar.setBackgroundColor = (color, animated = false) => { + statusBarColor = color as string; + setBackgroundColor(color, animated); +}; export default StatusBar; diff --git a/src/pages/TeachersUnite/SaveTheWorldPage.js b/src/pages/TeachersUnite/SaveTheWorldPage.js index 6d448b6b08b1..d179b7d1db95 100644 --- a/src/pages/TeachersUnite/SaveTheWorldPage.js +++ b/src/pages/TeachersUnite/SaveTheWorldPage.js @@ -38,7 +38,7 @@ function SaveTheWorldPage(props) { Navigation.goBack(ROUTES.HOME)} illustration={LottieAnimations.SaveTheWorld} > diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 61950e14337f..8ca1f96b3796 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -381,7 +381,7 @@ function InitialSettingsPage(props) { title={translate('common.settings')} headerContent={headerContent} headerContainerStyles={[styles.staticHeaderImage, styles.justifyContentCenter]} - backgroundColor={theme.PAGE_BACKGROUND_COLORS[SCREENS.SETTINGS.ROOT]} + backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.ROOT].backgroundColor} > {getMenuItems} diff --git a/src/pages/settings/Preferences/PreferencesPage.js b/src/pages/settings/Preferences/PreferencesPage.js index 4dbc5fda9198..b010c3790056 100755 --- a/src/pages/settings/Preferences/PreferencesPage.js +++ b/src/pages/settings/Preferences/PreferencesPage.js @@ -46,7 +46,7 @@ function PreferencesPage(props) { Navigation.goBack(ROUTES.SETTINGS)} - backgroundColor={theme.PAGE_BACKGROUND_COLORS[SCREENS.SETTINGS.PREFERENCES]} + backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.PREFERENCES].backgroundColor} illustration={LottieAnimations.PreferencesDJ} > diff --git a/src/pages/settings/Profile/CustomStatus/StatusPage.js b/src/pages/settings/Profile/CustomStatus/StatusPage.js index 285a3072f859..6850684cfda2 100644 --- a/src/pages/settings/Profile/CustomStatus/StatusPage.js +++ b/src/pages/settings/Profile/CustomStatus/StatusPage.js @@ -91,7 +91,7 @@ function StatusPage({draftStatus, currentUserPersonalDetails}) { /> } headerContainerStyles={[styles.staticHeaderImage]} - backgroundColor={theme.PAGE_BACKGROUND_COLORS[SCREENS.SETTINGS.STATUS]} + backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.STATUS].backgroundColor} footer={footerComponent} > diff --git a/src/pages/settings/Security/SecuritySettingsPage.js b/src/pages/settings/Security/SecuritySettingsPage.js index bf1edd7b806d..95a0efa2fc39 100644 --- a/src/pages/settings/Security/SecuritySettingsPage.js +++ b/src/pages/settings/Security/SecuritySettingsPage.js @@ -69,7 +69,7 @@ function SecuritySettingsPage(props) { onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS)} shouldShowBackButton illustration={LottieAnimations.Safe} - backgroundColor={theme.PAGE_BACKGROUND_COLORS[SCREENS.SETTINGS.SECURITY]} + backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.SECURITY].backgroundColor} > diff --git a/src/pages/settings/Wallet/ActivatePhysicalCardPage.js b/src/pages/settings/Wallet/ActivatePhysicalCardPage.js index 3534ef5c064c..e45b4ad67f4e 100644 --- a/src/pages/settings/Wallet/ActivatePhysicalCardPage.js +++ b/src/pages/settings/Wallet/ActivatePhysicalCardPage.js @@ -134,7 +134,7 @@ function ActivatePhysicalCardPage({ Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARD.getRoute(domain))} - backgroundColor={theme.PAGE_BACKGROUND_COLORS[SCREENS.SETTINGS.PREFERENCES]} + backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.PREFERENCES].backgroundColor} illustration={LottieAnimations.Magician} scrollViewContainerStyles={[styles.mnh100]} childrenContainerStyles={[styles.flex1]} diff --git a/src/pages/settings/Wallet/WalletEmptyState.js b/src/pages/settings/Wallet/WalletEmptyState.js index 8eacd322d695..f2235dfafef2 100644 --- a/src/pages/settings/Wallet/WalletEmptyState.js +++ b/src/pages/settings/Wallet/WalletEmptyState.js @@ -36,7 +36,7 @@ function WalletEmptyState({onAddPaymentMethod}) { const {translate} = useLocalize(); return ( Navigation.goBack(ROUTES.SETTINGS)} title={translate('common.wallet')} diff --git a/src/pages/signin/SignInPage.js b/src/pages/signin/SignInPage.js index b0ced580b30e..e7499e2aef01 100644 --- a/src/pages/signin/SignInPage.js +++ b/src/pages/signin/SignInPage.js @@ -5,6 +5,8 @@ import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import _ from 'underscore'; +import ColorSchemeWrapper from '@components/ColorSchemeWrapper'; +import CustomStatusBar from '@components/CustomStatusBar'; import useLocalize from '@hooks/useLocalize'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as ActiveClientManager from '@libs/ActiveClientManager'; @@ -279,10 +281,13 @@ function SignInPage(props) { return ( - + + + + ); diff --git a/src/pages/workspace/WorkspacesListPage.js b/src/pages/workspace/WorkspacesListPage.js index 2749ccb52b96..cf258b462285 100755 --- a/src/pages/workspace/WorkspacesListPage.js +++ b/src/pages/workspace/WorkspacesListPage.js @@ -183,7 +183,7 @@ function WorkspacesListPage({policies, allPolicyMembers, reimbursementAccount, u return ( Navigation.goBack(ROUTES.SETTINGS)} title={translate('common.workspaces')} diff --git a/src/styles/styles.ts b/src/styles/styles.ts index 45736295d4be..c7e1988c7b05 100644 --- a/src/styles/styles.ts +++ b/src/styles/styles.ts @@ -40,6 +40,9 @@ import wordBreak from './utilities/wordBreak'; import writingDirection from './utilities/writingDirection'; import variables from './variables'; +type ColorScheme = (typeof CONST.COLOR_SCHEME)[keyof typeof CONST.COLOR_SCHEME]; +type StatusBarStyle = (typeof CONST.STATUS_BAR_STYLE)[keyof typeof CONST.STATUS_BAR_STYLE]; + type AnchorPosition = { horizontal: number; vertical: number; @@ -3984,12 +3987,14 @@ const styles = (theme: ThemeColors) => singleOptionSelectorCircle: { borderColor: theme.icon, }, + + colorSchemeStyle: (colorScheme: ColorScheme) => ({colorScheme}), } satisfies Styles); +type ThemeStyle = ReturnType; + const stylesGenerator = styles; const defaultStyles = styles(defaultTheme); -type ThemeStyle = typeof defaultStyles; - export default defaultStyles; -export {stylesGenerator, type Styles, type ThemeStyle}; +export {stylesGenerator, type Styles, type ThemeStyle, type StatusBarStyle, type ColorScheme}; diff --git a/src/styles/themes/default.ts b/src/styles/themes/default.ts index 8dd76a4776a4..519e818054e0 100644 --- a/src/styles/themes/default.ts +++ b/src/styles/themes/default.ts @@ -1,4 +1,5 @@ import colors from '@styles/colors'; +import CONST from '@src/CONST'; import SCREENS from '@src/SCREENS'; import {ThemeColors} from './types'; @@ -89,16 +90,47 @@ const darkTheme = { // Note that it needs to be a screen name, not a route url. // The route urls from ROUTES.ts are only used for deep linking and configuring URLs on web. // The screen name (see SCREENS.ts) is the name of the screen as far as react-navigation is concerned, and the linkingConfig maps screen names to URLs - PAGE_BACKGROUND_COLORS: { - [SCREENS.HOME]: colors.darkHighlightBackground, - [SCREENS.SAVE_THE_WORLD.ROOT]: colors.tangerine800, - [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, - [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, - [SCREENS.SETTINGS.WALLET]: colors.darkAppBackground, - [SCREENS.SETTINGS.SECURITY]: colors.ice500, - [SCREENS.SETTINGS.STATUS]: colors.green700, - [SCREENS.SETTINGS.ROOT]: colors.darkHighlightBackground, + PAGE_THEMES: { + [SCREENS.HOME]: { + backgroundColor: colors.darkHighlightBackground, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, + [SCREENS.REPORT]: { + backgroundColor: colors.darkAppBackground, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, + [SCREENS.SAVE_THE_WORLD.ROOT]: { + backgroundColor: colors.tangerine800, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, + [SCREENS.SETTINGS.PREFERENCES]: { + backgroundColor: colors.blue500, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, + [SCREENS.SETTINGS.WORKSPACES]: { + backgroundColor: colors.pink800, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, + [SCREENS.SETTINGS.WALLET]: { + backgroundColor: colors.darkAppBackground, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, + [SCREENS.SETTINGS.SECURITY]: { + backgroundColor: colors.ice500, + statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, + }, + [SCREENS.SETTINGS.STATUS]: { + backgroundColor: colors.green700, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, + [SCREENS.SETTINGS.ROOT]: { + backgroundColor: colors.darkHighlightBackground, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, }, + + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + colorScheme: CONST.COLOR_SCHEME.DARK, } satisfies ThemeColors; export default darkTheme; diff --git a/src/styles/themes/light.ts b/src/styles/themes/light.ts index cbaadcf196ba..2f6eda2f70dc 100644 --- a/src/styles/themes/light.ts +++ b/src/styles/themes/light.ts @@ -1,4 +1,5 @@ import colors from '@styles/colors'; +import CONST from '@src/CONST'; import SCREENS from '@src/SCREENS'; import {ThemeColors} from './types'; @@ -89,16 +90,47 @@ const lightTheme = { // Note that it needs to be a screen name, not a route url. // The route urls from ROUTES.ts are only used for deep linking and configuring URLs on web. // The screen name (see SCREENS.ts) is the name of the screen as far as react-navigation is concerned, and the linkingConfig maps screen names to URLs - PAGE_BACKGROUND_COLORS: { - [SCREENS.HOME]: colors.lightHighlightBackground, - [SCREENS.SAVE_THE_WORLD.ROOT]: colors.tangerine800, - [SCREENS.SETTINGS.PREFERENCES]: colors.blue500, - [SCREENS.SETTINGS.WORKSPACES]: colors.pink800, - [SCREENS.SETTINGS.WALLET]: colors.darkAppBackground, - [SCREENS.SETTINGS.SECURITY]: colors.ice500, - [SCREENS.SETTINGS.STATUS]: colors.green700, - [SCREENS.SETTINGS.ROOT]: colors.lightHighlightBackground, + PAGE_THEMES: { + [SCREENS.HOME]: { + backgroundColor: colors.lightHighlightBackground, + statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, + }, + [SCREENS.REPORT]: { + backgroundColor: colors.lightAppBackground, + statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, + }, + [SCREENS.SAVE_THE_WORLD.ROOT]: { + backgroundColor: colors.tangerine800, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, + [SCREENS.SETTINGS.PREFERENCES]: { + backgroundColor: colors.blue500, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, + [SCREENS.SETTINGS.WORKSPACES]: { + backgroundColor: colors.pink800, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, + [SCREENS.SETTINGS.WALLET]: { + backgroundColor: colors.darkAppBackground, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, + [SCREENS.SETTINGS.SECURITY]: { + backgroundColor: colors.ice500, + statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, + }, + [SCREENS.SETTINGS.STATUS]: { + backgroundColor: colors.green700, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, + [SCREENS.SETTINGS.ROOT]: { + backgroundColor: colors.lightHighlightBackground, + statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, + }, }, + + statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, + colorScheme: CONST.COLOR_SCHEME.LIGHT, } satisfies ThemeColors; export default lightTheme; diff --git a/src/styles/themes/types.ts b/src/styles/themes/types.ts index d6d1dcea0833..c674b6057d0f 100644 --- a/src/styles/themes/types.ts +++ b/src/styles/themes/types.ts @@ -1,3 +1,4 @@ +import {type ColorScheme, type StatusBarStyle} from '@styles/styles'; import CONST from '@src/CONST'; type Color = string; @@ -88,7 +89,13 @@ type ThemeColors = { mapAttributionText: Color; white: Color; - PAGE_BACKGROUND_COLORS: Record; + PAGE_THEMES: Record; + + // Status bar and scroll bars need to adapt their theme based on the active user theme for good contrast + // Therefore, we need to define specific themes for these elements + // e.g. the StatusBar displays either "light-content" or "dark-content" based on the theme + statusBarStyle: StatusBarStyle; + colorScheme: ColorScheme; }; export {type ThemePreference, type ThemePreferenceWithoutSystem, type ThemeColors, type Color};