mirror of
https://github.com/movie-web/native-app.git
synced 2025-09-13 18:13:25 +00:00
feat: settings storage & theme persistence
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
"@movie-web/provider-utils": "*",
|
"@movie-web/provider-utils": "*",
|
||||||
"@movie-web/tmdb": "*",
|
"@movie-web/tmdb": "*",
|
||||||
"@react-native-anywhere/polyfill-base64": "0.0.1-alpha.0",
|
"@react-native-anywhere/polyfill-base64": "0.0.1-alpha.0",
|
||||||
|
"@react-native-async-storage/async-storage": "1.21.0",
|
||||||
"@react-navigation/native": "^6.1.9",
|
"@react-navigation/native": "^6.1.9",
|
||||||
"@tamagui/animations-moti": "^1.91.4",
|
"@tamagui/animations-moti": "^1.91.4",
|
||||||
"@tamagui/babel-plugin": "^1.91.4",
|
"@tamagui/babel-plugin": "^1.91.4",
|
||||||
|
33
apps/expo/src/settings/index.ts
Normal file
33
apps/expo/src/settings/index.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
|
|
||||||
|
import type { ThemeStoreOption } from "~/stores/theme";
|
||||||
|
|
||||||
|
interface ThemeSettings {
|
||||||
|
theme: ThemeStoreOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Settings {
|
||||||
|
themes: ThemeSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsKey = "settings";
|
||||||
|
|
||||||
|
const saveSettings = async (settings: Settings) => {
|
||||||
|
await AsyncStorage.setItem(settingsKey, JSON.stringify(settings));
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadSettings = async (): Promise<Settings | null> => {
|
||||||
|
const json = await AsyncStorage.getItem(settingsKey);
|
||||||
|
return json ? (JSON.parse(json) as Settings) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTheme = async (): Promise<ThemeStoreOption> => {
|
||||||
|
const settings = await loadSettings();
|
||||||
|
return settings?.themes?.theme ?? "main";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveTheme = async (newTheme: ThemeStoreOption) => {
|
||||||
|
const settings = (await loadSettings()) ?? { themes: { theme: newTheme } };
|
||||||
|
settings.themes.theme = newTheme;
|
||||||
|
await saveSettings(settings);
|
||||||
|
};
|
@@ -2,6 +2,8 @@ import { setAppIcon } from "expo-dynamic-app-icon";
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { immer } from "zustand/middleware/immer";
|
import { immer } from "zustand/middleware/immer";
|
||||||
|
|
||||||
|
import { getTheme, saveTheme } from "~/settings";
|
||||||
|
|
||||||
export type ThemeStoreOption = "main" | "blue" | "gray" | "red" | "teal";
|
export type ThemeStoreOption = "main" | "blue" | "gray" | "red" | "teal";
|
||||||
|
|
||||||
export interface ThemeStore {
|
export interface ThemeStore {
|
||||||
@@ -10,13 +12,28 @@ export interface ThemeStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useThemeStore = create(
|
export const useThemeStore = create(
|
||||||
immer<ThemeStore>((set) => ({
|
immer<ThemeStore>((set) => {
|
||||||
theme: "main",
|
void getTheme().then((savedTheme) => {
|
||||||
setTheme(v) {
|
|
||||||
set((s) => {
|
set((s) => {
|
||||||
s.theme = v;
|
s.theme = savedTheme;
|
||||||
setAppIcon(v);
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
theme: "main",
|
||||||
|
setTheme: (newTheme) => {
|
||||||
|
setAppIcon(newTheme);
|
||||||
|
|
||||||
|
saveTheme(newTheme)
|
||||||
|
.then(() => {
|
||||||
|
set((s) => {
|
||||||
|
s.theme = newTheme;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Failed to save theme:", error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
})),
|
};
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
25
pnpm-lock.yaml
generated
25
pnpm-lock.yaml
generated
@@ -41,6 +41,9 @@ importers:
|
|||||||
'@react-native-anywhere/polyfill-base64':
|
'@react-native-anywhere/polyfill-base64':
|
||||||
specifier: 0.0.1-alpha.0
|
specifier: 0.0.1-alpha.0
|
||||||
version: 0.0.1-alpha.0
|
version: 0.0.1-alpha.0
|
||||||
|
'@react-native-async-storage/async-storage':
|
||||||
|
specifier: 1.21.0
|
||||||
|
version: 1.21.0(react-native@0.73.5)
|
||||||
'@react-navigation/native':
|
'@react-navigation/native':
|
||||||
specifier: ^6.1.9
|
specifier: ^6.1.9
|
||||||
version: 6.1.9(react-native@0.73.5)(react@18.2.0)
|
version: 6.1.9(react-native@0.73.5)(react@18.2.0)
|
||||||
@@ -2874,6 +2877,15 @@ packages:
|
|||||||
base-64: 0.1.0
|
base-64: 0.1.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@react-native-async-storage/async-storage@1.21.0(react-native@0.73.5):
|
||||||
|
resolution: {integrity: sha512-JL0w36KuFHFCvnbOXRekqVAUplmOyT/OuCQkogo6X98MtpSaJOKEAeZnYO8JB0U/RIEixZaGI5px73YbRm/oag==}
|
||||||
|
peerDependencies:
|
||||||
|
react-native: ^0.0.0-0 || >=0.60 <1.0
|
||||||
|
dependencies:
|
||||||
|
merge-options: 3.0.4
|
||||||
|
react-native: 0.73.5(@babel/core@7.23.9)(@babel/preset-env@7.23.9)(react@18.2.0)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@react-native-community/cli-clean@12.3.6:
|
/@react-native-community/cli-clean@12.3.6:
|
||||||
resolution: {integrity: sha512-gUU29ep8xM0BbnZjwz9MyID74KKwutq9x5iv4BCr2im6nly4UMf1B1D+V225wR7VcDGzbgWjaezsJShLLhC5ig==}
|
resolution: {integrity: sha512-gUU29ep8xM0BbnZjwz9MyID74KKwutq9x5iv4BCr2im6nly4UMf1B1D+V225wR7VcDGzbgWjaezsJShLLhC5ig==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -8846,6 +8858,11 @@ packages:
|
|||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/is-plain-obj@2.1.0:
|
||||||
|
resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/is-plain-object@2.0.4:
|
/is-plain-object@2.0.4:
|
||||||
resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
|
resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -9669,6 +9686,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==}
|
resolution: {integrity: sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/merge-options@3.0.4:
|
||||||
|
resolution: {integrity: sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
is-plain-obj: 2.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/merge-stream@2.0.0:
|
/merge-stream@2.0.0:
|
||||||
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
|
||||||
|
|
||||||
@@ -13503,7 +13527,6 @@ packages:
|
|||||||
id: github.com/simonsturge/expo-dynamic-app-icon/acfd73827573e99a5e1bfbe47cf2fb1656e9ccf0
|
id: github.com/simonsturge/expo-dynamic-app-icon/acfd73827573e99a5e1bfbe47cf2fb1656e9ccf0
|
||||||
name: expo-dynamic-app-icon
|
name: expo-dynamic-app-icon
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
prepare: true
|
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
expo: '>=49'
|
expo: '>=49'
|
||||||
|
Reference in New Issue
Block a user