forked from NWACus/avy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Preferences.tsx
88 lines (72 loc) · 3.07 KB
/
Preferences.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Create an object that uses AsyncStorage to persist user preferences
// and provide a hook to update them.
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as Sentry from '@sentry/react-native';
import {merge} from 'lodash';
import React, {createContext, ReactNode, useContext, useEffect, useState} from 'react';
import uuid from 'react-native-uuid';
import {useAsyncEffect} from 'use-async-effect';
import {z} from 'zod';
import {PREFERENCES_KEY} from 'data/asyncStorageKeys';
import {avalancheCenterIDSchema} from 'types/nationalAvalancheCenter';
const preferencesSchema = z.object({
center: avalancheCenterIDSchema.default('NWAC'),
hasSeenCenterPicker: z.boolean().default(false),
developerMenuCollapsed: z.boolean().default(true),
mixpanelUserId: z.string().uuid().optional(),
});
export type Preferences = z.infer<typeof preferencesSchema>;
const defaultPreferences = preferencesSchema.parse({});
interface PreferencesContextType {
preferences: Preferences;
setPreferences: (preferences: Partial<Preferences>) => void;
clearPreferences: () => void;
}
const PreferencesContext = createContext<PreferencesContextType>({
preferences: defaultPreferences,
setPreferences: () => undefined,
clearPreferences: () => undefined,
});
interface PreferencesProviderProps {
children?: ReactNode;
}
export const PreferencesProvider: React.FC<PreferencesProviderProps> = ({children}) => {
const [preferences, setPreferences] = useState<Preferences>(defaultPreferences);
useAsyncEffect(async () => {
let storedPreferences = {};
try {
storedPreferences = preferencesSchema.parse(JSON.parse((await AsyncStorage.getItem(PREFERENCES_KEY)) ?? '{}'));
} catch (e) {
// Error parsing preferences, ignore as we'll fall back to defaults
await AsyncStorage.removeItem(PREFERENCES_KEY);
// But do log it to Sentry as it shouldn't happen
Sentry.captureException(e);
}
setPartialPreferences(
merge(
{},
defaultPreferences,
{
// Generate a new UUID if one doesn't exist, but overwrite with stored prefs if that's set
mixpanelUserId: uuid.v4() as string,
},
storedPreferences,
),
);
}, []);
useEffect(() => {
void AsyncStorage.setItem(PREFERENCES_KEY, JSON.stringify(preferences));
}, [preferences]);
const setPartialPreferences = (newPreferences: Partial<Preferences>) => {
setPreferences(merge({}, preferences, newPreferences));
};
const clearPreferences = () => {
// Clear everything _except_ the mixpanelUserId. It might not be set yet, but if it is we don't want to lose it.
setPreferences({...defaultPreferences, mixpanelUserId: preferences.mixpanelUserId});
};
return <PreferencesContext.Provider value={{preferences, setPreferences: setPartialPreferences, clearPreferences}}>{children}</PreferencesContext.Provider>;
};
export const usePreferences = () => useContext(PreferencesContext);
export const resetPreferencesForTests = async () => {
await AsyncStorage.setItem(PREFERENCES_KEY, JSON.stringify(defaultPreferences));
};