Skip to content

Commit d2e8fba

Browse files
authored
Add a way to persist theme selection (49) (#942)
* Add a way to persist theme selection * Add @react-native-async-storage/async-storage to themes package * Fix tests
1 parent 0e701da commit d2e8fba

File tree

6 files changed

+72
-15
lines changed

6 files changed

+72
-15
lines changed

packages/core/jest-setup.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@ setupReaanimatedTests();
66
global.ReanimatedDataMock = {
77
now: () => 0,
88
};
9+
10+
jest.mock("@react-native-async-storage/async-storage", () =>
11+
require("@react-native-async-storage/async-storage/jest/async-storage-mock")
12+
);

packages/maps/jest-setup.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ jest.mock("react-native/Libraries/EventEmitter/NativeEventEmitter");
33
jest.mock("react-native-webview", () => ({
44
default: () => jest.fn(), // or any mocked component instead of native view,
55
}));
6+
7+
jest.mock("@react-native-async-storage/async-storage", () =>
8+
require("@react-native-async-storage/async-storage/jest/async-storage-mock")
9+
);

packages/theme/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
"color": "^4.2.3",
4343
"deepmerge": "^4.3.1",
4444
"lodash.isobject": "^3.0.2",
45-
"react-native-typography": "^1.4.1"
45+
"react-native-typography": "^1.4.1",
46+
"@react-native-async-storage/async-storage": "1.18.2"
4647
},
4748
"eslintIgnore": [
4849
"node_modules/",

packages/theme/src/Provider.tsx

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import React from "react";
22
import { Dimensions, Platform } from "react-native";
3+
import AsyncStorage from "@react-native-async-storage/async-storage";
34
import createThemeValuesProxy from "./createThemeValuesProxy";
45
import DefaultTheme from "./DefaultTheme";
5-
import { Breakpoints, ReadTheme, ValidatedTheme } from "./types";
6+
import {
7+
Breakpoints,
8+
ChangeThemeOptions,
9+
ReadTheme,
10+
ValidatedTheme,
11+
} from "./types";
12+
13+
const SAVED_SELECTED_THEME_KEY = "saved_selected_theme";
614

715
type ThemeContextType = {
816
theme: ReadTheme;
@@ -34,18 +42,9 @@ const Provider: React.FC<React.PropsWithChildren<ProviderProps>> = ({
3442
Dimensions.get("window").width
3543
);
3644

37-
React.useEffect(() => {
38-
const listener = Dimensions.addEventListener("change", ({ window }) =>
39-
setDeviceWidth(window.width)
40-
);
41-
return () => {
42-
listener.remove();
43-
};
44-
});
45-
4645
const changeTheme = React.useCallback(
47-
(themeName: string) => {
48-
const theme = themes.find((theme) => theme.name === themeName);
46+
(themeName: string, options?: ChangeThemeOptions) => {
47+
const theme = themes.find((t) => t.name === themeName);
4948
if (!theme) {
5049
console.warn(
5150
"Theme with name",
@@ -55,6 +54,12 @@ const Provider: React.FC<React.PropsWithChildren<ProviderProps>> = ({
5554
return;
5655
}
5756
setCurrentTheme(theme);
57+
58+
if (options?.persistent === true) {
59+
AsyncStorage.setItem(SAVED_SELECTED_THEME_KEY, themeName).catch((e) => {
60+
console.warn("Failed to persist selected theme", e);
61+
});
62+
}
5863
},
5964
[themes, setCurrentTheme]
6065
);
@@ -98,6 +103,30 @@ const Provider: React.FC<React.PropsWithChildren<ProviderProps>> = ({
98103
[currentTheme, deviceWidth, breakpoints]
99104
);
100105

106+
React.useEffect(() => {
107+
const listener = Dimensions.addEventListener("change", ({ window }) =>
108+
setDeviceWidth(window.width)
109+
);
110+
return () => {
111+
listener.remove();
112+
};
113+
});
114+
115+
React.useEffect(() => {
116+
const run = async () => {
117+
const savedSelectedThemeName = await AsyncStorage.getItem(
118+
SAVED_SELECTED_THEME_KEY
119+
);
120+
if (savedSelectedThemeName) {
121+
changeTheme(savedSelectedThemeName);
122+
}
123+
};
124+
run();
125+
126+
// This should only run once, ignore deps
127+
// eslint-disable-next-line react-hooks/exhaustive-deps
128+
}, []);
129+
101130
return (
102131
<ThemeContext.Provider value={{ theme: proxiedTheme, changeTheme }}>
103132
{children}
@@ -110,7 +139,10 @@ const useTheme = (): ReadTheme => {
110139
return theme;
111140
};
112141

113-
const useChangeTheme = (): ((themeName: string) => void) => {
142+
const useChangeTheme = (): ((
143+
themeName: string,
144+
options?: ChangeThemeOptions
145+
) => void) => {
114146
const { changeTheme } = React.useContext(ThemeContext);
115147
return changeTheme;
116148
};

packages/theme/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,5 @@ export type ColorPalettes = {
3939
export type Breakpoints = {
4040
[key: string]: number;
4141
};
42+
43+
export type ChangeThemeOptions = { persistent?: boolean };

yarn.lock

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3237,6 +3237,13 @@
32373237
resolved "https://registry.yarnpkg.com/@react-google-maps/marker-clusterer/-/marker-clusterer-2.16.1.tgz#882878a7b49ce9ae44c7b02ff6fbc4c2aeb77c95"
32383238
integrity sha512-jOuyqzWLeXvQcoAu6TCVWHAuko+sDt0JjawNHBGqUNLywMtTCvYP0L0PiqJZOUCUeRYGdUy0AKxQ+30vAkvwag==
32393239

3240+
"@react-native-async-storage/[email protected]":
3241+
version "1.18.2"
3242+
resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.18.2.tgz#ec8fd487a0b6c9500b43ece4b8779d1561f12e91"
3243+
integrity sha512-dM8AfdoeIxlh+zqgr0o5+vCTPQ0Ru1mrPzONZMsr7ufp5h+6WgNxQNza7t0r5qQ6b04AJqTlBNixTWZxqP649Q==
3244+
dependencies:
3245+
merge-options "^3.0.4"
3246+
32403247
"@react-native-community/[email protected]":
32413248
version "11.3.7"
32423249
resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-11.3.7.tgz#cb4c2f225f78593412c2d191b55b8570f409a48f"
@@ -8910,7 +8917,7 @@ is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
89108917
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
89118918
integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==
89128919

8913-
is-plain-obj@^2.0.0:
8920+
is-plain-obj@^2.0.0, is-plain-obj@^2.1.0:
89148921
version "2.1.0"
89158922
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
89168923
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
@@ -10528,6 +10535,13 @@ [email protected]:
1052810535
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
1052910536
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
1053010537

10538+
merge-options@^3.0.4:
10539+
version "3.0.4"
10540+
resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-3.0.4.tgz#84709c2aa2a4b24c1981f66c179fe5565cc6dbb7"
10541+
integrity sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==
10542+
dependencies:
10543+
is-plain-obj "^2.1.0"
10544+
1053110545
merge-stream@^2.0.0:
1053210546
version "2.0.0"
1053310547
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"

0 commit comments

Comments
 (0)