import React, { useEffect } from 'react'; import '../global.css'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { FunctionComponent, PropsWithChildren, createContext, useContext, useMemo, useState, } from 'react'; import { Appearance, ColorSchemeName, Platform } from 'react-native'; import { ThemeProvider as ApplicationThemeProvider, DarkTheme, DefaultTheme, Theme, } from '@react-navigation/native'; import { NAV_THEME } from '../lib/constants'; import { useColorScheme } from 'nativewind'; import { setAndroidNavigationBar } from '../lib/android-navigation-bar'; const COLOR_SCHEME_KEY = 'colorScheme'; const LIGHT_THEME: Theme = { ...DefaultTheme, colors: NAV_THEME.light, }; const DARK_THEME: Theme = { ...DarkTheme, colors: NAV_THEME.dark, }; interface UseThemeChangeContext { setColorScheme: (scheme: NonNullable) => void; loadingColorScheme: boolean; colorScheme: NonNullable; } const ThemeProviderContext = createContext({ loadingColorScheme: true, setColorScheme: () => {}, colorScheme: 'light', }); export function useThemeChange() { return useContext(ThemeProviderContext); } const usePlatformSpecificSetup = Platform.select({ web: useSetWebBackgroundClassName, android: useSetAndroidNavigationBar, default: noop, }); export const ThemeProvider: FunctionComponent = ({ children, }) => { const { colorScheme, setColorScheme } = useColorScheme(); const [loadingColorScheme, setLoadingColorScheme] = useState(true); usePlatformSpecificSetup(); useEffect(() => { const lookupCurColorScheme = async () => { setColorScheme(Appearance.getColorScheme() ?? 'system'); const storedColorSchemeName: ColorSchemeName = ((await AsyncStorage.getItem(COLOR_SCHEME_KEY)) as ColorSchemeName) || Appearance.getColorScheme(); if (storedColorSchemeName) { setColorScheme(storedColorSchemeName); } setLoadingColorScheme(false); }; lookupCurColorScheme(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const context = useMemo( () => ({ setColorScheme: async (newColorScheme: NonNullable) => { setColorScheme(newColorScheme); await AsyncStorage.setItem(COLOR_SCHEME_KEY, newColorScheme); }, loadingColorScheme, colorScheme: colorScheme ?? 'light', }), [loadingColorScheme, colorScheme, setColorScheme], ); return ( <> {children} ); }; const useIsomorphicLayoutEffect = Platform.OS === 'web' && typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect; function useSetWebBackgroundClassName() { useIsomorphicLayoutEffect(() => { // Adds the background color to the html element to prevent white background on overscroll. document.documentElement.classList.add('bg-background'); }, []); } function useSetAndroidNavigationBar() { React.useLayoutEffect(() => { setAndroidNavigationBar(Appearance.getColorScheme() ?? 'light'); }, []); } function noop() {}