diff --git a/app/_layout.tsx b/app/_layout.tsx index 6f79126..0a38cb4 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -1,7 +1,8 @@ import { Slot } from 'expo-router'; import React, { useEffect } from 'react'; -import { initDatabase } from '../services/database'; +import { addCategory, initDatabase } from '../services/database'; import { AuthProvider } from './contexts/AuthContext'; +import { ThemeProvider } from './contexts/ThemeContext'; export default function _layout() { @@ -12,7 +13,9 @@ export default function _layout() { console.log("layout called") return ( - + + + ) } \ No newline at end of file diff --git a/app/contexts/ThemeContext.tsx b/app/contexts/ThemeContext.tsx new file mode 100644 index 0000000..9e00714 --- /dev/null +++ b/app/contexts/ThemeContext.tsx @@ -0,0 +1,97 @@ +import {useContext, createContext, useState, useEffect} from "react" +import AsyncStorage from "@react-native-async-storage/async-storage" +import { useColorScheme } from "react-native"; +import themeColors from "../../constants/colors"; + + +interface ThemeProps { + isSystemTheme?: boolean + theme: "light" | "dark"; + colors: typeof themeColors.light & typeof themeColors.dark; + applySystemTheme?: () => void; + applyTheme?: (theme: "light" | "dark") => void; +} + +const THEME_KEY = "THEME" +const SYSTEM_THEME_KEY = "SYSTEM_THEME" + +const ThemeContext = createContext({theme: "light", colors: themeColors.light}) + +export const useTheme = () => { + return useContext(ThemeContext) +} + +export const ThemeProvider = ({children} : any) => { + const currentSystemTheme = useColorScheme() + const [isSystemTheme, setIsSystemTheme] = useState(true); + const [theme, setTheme] = useState<"light" | "dark">("light"); + const [colors, setColors] = useState(themeColors.light) + + useEffect(() => { + console.log("effect called") + const loadTheme = async () => { + try { + const is_system_theme = await AsyncStorage.getItem(SYSTEM_THEME_KEY) + const saved_theme = await AsyncStorage.getItem(THEME_KEY); + + if(is_system_theme !== "false"){//We will use !== false bacause system theme should be used if unspecified + applySystemTheme(); + return; + } + + if(saved_theme === "light" || saved_theme === "dark"){ + applyTheme(saved_theme); + } + + } catch (error) { + console.error(error) + } + } + loadTheme(); + }, [currentSystemTheme]) + + + const applySystemTheme = () => { + const storeSystemTheme = async () => { + console.log("applySystemTheme") + await AsyncStorage.setItem(SYSTEM_THEME_KEY, "true") + } + + storeSystemTheme(); + setIsSystemTheme(true) + applyThemeInternal(currentSystemTheme ?? "light") + } + + const applyThemeInternal = (theme: "light" | "dark") => { + const storeTheme = async (theme: "light" | "dark") => { + await AsyncStorage.setItem(THEME_KEY, theme) + } + + setTheme(theme); + storeTheme(theme); + setColors(themeColors[theme]) + } + + const applyTheme = (theme: "light" | "dark") => { + const unsetSytemTheme = async () => { + AsyncStorage.setItem(SYSTEM_THEME_KEY, "false"); + } + unsetSytemTheme() + setIsSystemTheme(false); + applyThemeInternal(theme); + } + + const value = { + isSystemTheme: isSystemTheme, + applyTheme: applyTheme, + applySystemTheme: applySystemTheme, + theme: theme, + colors: colors, + } + + return ( + + {children} + + ) +} diff --git a/package-lock.json b/package-lock.json index 2e67c4c..f07d162 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "dependencies": { "@expo/vector-icons": "^13.0.0", + "@react-native-async-storage/async-storage": "1.18.2", "@react-navigation/native": "^6.0.2", "expo": "~49.0.15", "expo-font": "~11.4.0", @@ -4277,6 +4278,17 @@ "react": "^16.8 || ^17.0 || ^18.0" } }, + "node_modules/@react-native-async-storage/async-storage": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.18.2.tgz", + "integrity": "sha512-dM8AfdoeIxlh+zqgr0o5+vCTPQ0Ru1mrPzONZMsr7ufp5h+6WgNxQNza7t0r5qQ6b04AJqTlBNixTWZxqP649Q==", + "dependencies": { + "merge-options": "^3.0.4" + }, + "peerDependencies": { + "react-native": "^0.0.0-0 || 0.60 - 0.72 || 1000.0.0" + } + }, "node_modules/@react-native-community/cli": { "version": "11.3.7", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-11.3.7.tgz", @@ -13452,6 +13464,25 @@ "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", "integrity": "sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==" }, + "node_modules/merge-options": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", + "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", + "dependencies": { + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge-options/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", diff --git a/package.json b/package.json index 8fff007..a625b05 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ }, "dependencies": { "@expo/vector-icons": "^13.0.0", + "@react-native-async-storage/async-storage": "1.18.2", "@react-navigation/native": "^6.0.2", "expo": "~49.0.15", "expo-font": "~11.4.0",