From acbbd2d27d883f55dc7beb49409bc7ed0763aa69 Mon Sep 17 00:00:00 2001 From: Arnaud Vergnet Date: Sat, 22 May 2021 11:11:53 +0200 Subject: [PATCH] Move preferences in separate contextes This improves performance when updating preferences --- App.tsx | 100 +++++++-- .../providers/PreferencesProvider.tsx | 182 +++++++++++++---- src/context/preferencesContext.tsx | 95 +++++++-- src/navigation/MainNavigator.tsx | 10 +- src/navigation/TabNavigator.tsx | 7 +- src/screens/About/DebugScreen.tsx | 8 +- src/screens/Intro/IntroScreen.tsx | 18 +- src/screens/Other/Settings/SettingsScreen.tsx | 64 ++++-- src/screens/Planex/GroupSelectionScreen.tsx | 15 +- src/screens/Planex/PlanexScreen.tsx | 14 +- src/screens/Proxiwash/ProxiwashScreen.tsx | 22 +- src/utils/asyncStorage.ts | 193 +++++++++++------- 12 files changed, 532 insertions(+), 196 deletions(-) diff --git a/App.tsx b/App.tsx index 92ac750..6287e91 100644 --- a/App.tsx +++ b/App.tsx @@ -27,12 +27,26 @@ import URLHandler from './src/utils/URLHandler'; import initLocales from './src/utils/Locales'; import { NavigationContainerRef } from '@react-navigation/core'; import { + defaultMascotPreferences, + defaultPlanexPreferences, defaultPreferences, - PreferenceKeys, - PreferencesType, + defaultProxiwashPreferences, + GeneralPreferenceKeys, + GeneralPreferencesType, + MascotPreferenceKeys, + MascotPreferencesType, + PlanexPreferenceKeys, + PlanexPreferencesType, + ProxiwashPreferenceKeys, + ProxiwashPreferencesType, retrievePreferences, } from './src/utils/asyncStorage'; -import PreferencesProvider from './src/components/providers/PreferencesProvider'; +import { + GeneralPreferencesProvider, + MascotPreferencesProvider, + PlanexPreferencesProvider, + ProxiwashPreferencesProvider, +} from './src/components/providers/PreferencesProvider'; import MainApp from './src/screens/MainApp'; // Native optimizations https://reactnavigation.org/docs/react-native-screens @@ -45,9 +59,16 @@ LogBox.ignoreLogs([ 'Cannot update a component from inside the function body of a different component', ]); +// TODO move preferences in smaller contextes for improved performances + type StateType = { isLoading: boolean; - initialPreferences: PreferencesType; + initialPreferences: { + general: GeneralPreferencesType; + planex: PlanexPreferencesType; + proxiwash: ProxiwashPreferencesType; + mascot: MascotPreferencesType; + }; }; export default class App extends React.Component<{}, StateType> { @@ -63,7 +84,12 @@ export default class App extends React.Component<{}, StateType> { super(props); this.state = { isLoading: true, - initialPreferences: defaultPreferences, + initialPreferences: { + general: defaultPreferences, + planex: defaultPlanexPreferences, + proxiwash: defaultProxiwashPreferences, + mascot: defaultMascotPreferences, + }, }; initLocales(); this.navigatorRef = React.createRef(); @@ -106,11 +132,24 @@ export default class App extends React.Component<{}, StateType> { /** * Async loading is done, finish processing startup data */ - onLoadFinished = (values: Array) => { - const [preferences] = values; + onLoadFinished = ( + values: Array< + | GeneralPreferencesType + | PlanexPreferencesType + | ProxiwashPreferencesType + | MascotPreferencesType + | void + > + ) => { + const [general, planex, proxiwash, mascot] = values; this.setState({ isLoading: false, - initialPreferences: { ...(preferences as PreferencesType) }, + initialPreferences: { + general: general as GeneralPreferencesType, + planex: planex as PlanexPreferencesType, + proxiwash: proxiwash as ProxiwashPreferencesType, + mascot: mascot as MascotPreferencesType, + }, }); SplashScreen.hide(); }; @@ -122,7 +161,22 @@ export default class App extends React.Component<{}, StateType> { */ loadAssetsAsync() { Promise.all([ - retrievePreferences(Object.values(PreferenceKeys), defaultPreferences), + retrievePreferences( + Object.values(GeneralPreferenceKeys), + defaultPreferences + ), + retrievePreferences( + Object.values(PlanexPreferenceKeys), + defaultPlanexPreferences + ), + retrievePreferences( + Object.values(ProxiwashPreferenceKeys), + defaultProxiwashPreferences + ), + retrievePreferences( + Object.values(MascotPreferenceKeys), + defaultMascotPreferences + ), ConnectionManager.getInstance().recoverLogin(), ]) .then(this.onLoadFinished) @@ -138,13 +192,27 @@ export default class App extends React.Component<{}, StateType> { return null; } return ( - - - + + + + + + + + + ); } } diff --git a/src/components/providers/PreferencesProvider.tsx b/src/components/providers/PreferencesProvider.tsx index 2a81ce3..1e3e9b7 100644 --- a/src/components/providers/PreferencesProvider.tsx +++ b/src/components/providers/PreferencesProvider.tsx @@ -1,67 +1,173 @@ import React, { useState } from 'react'; import { + defaultMascotPreferences, + defaultPlanexPreferences, defaultPreferences, + defaultProxiwashPreferences, + GeneralPreferenceKeys, + GeneralPreferencesType, + MascotPreferenceKeys, + MascotPreferencesType, + PlanexPreferenceKeys, + PlanexPreferencesType, PreferenceKeys, PreferencesType, + ProxiwashPreferenceKeys, + ProxiwashPreferencesType, setPreference, } from '../../utils/asyncStorage'; import { + MascotPreferencesContext, + PlanexPreferencesContext, PreferencesContext, PreferencesContextType, + ProxiwashPreferencesContext, } from '../../context/preferencesContext'; -type Props = { - children: React.ReactChild; - initialPreferences: PreferencesType; -}; +function updateState, K extends string>( + key: K, + value: number | string | boolean | object | Array, + prevState: PreferencesContextType +) { + const prevPreferences = { ...prevState.preferences }; + const newPrefs = setPreference(key as PreferenceKeys, value, prevPreferences); + const newSate = { + ...prevState, + preferences: { ...newPrefs }, + }; + return newSate; +} -export default function PreferencesProvider(props: Props) { +function resetState< + T extends Partial, + K extends Partial +>( + keys: Array, + defaults: T, + prevState: PreferencesContextType +) { + const prevPreferences = { ...prevState.preferences }; + let newPreferences = { ...prevPreferences }; + keys.forEach((key) => { + newPreferences = setPreference(key, defaults[key], prevPreferences); + }); + const newSate = { + ...prevState, + preferences: { ...newPreferences }, + }; + return newSate; +} + +function usePreferencesProvider< + T extends Partial, + K extends Partial +>(initialPreferences: T, defaults: T, keys: Array) { const updatePreferences = ( - key: PreferenceKeys, + key: K, value: number | string | boolean | object | Array ) => { - setPreferencesState((prevState) => { - const prevPreferences = { ...prevState.preferences }; - const newPrefs = setPreference(key, value, prevPreferences); - const newSate = { - ...prevState, - preferences: { ...newPrefs }, - }; - return newSate; - }); + setPreferencesState((prevState) => updateState(key, value, prevState)); }; const resetPreferences = () => { - setPreferencesState((prevState) => { - const prevPreferences = { ...prevState.preferences }; - let newPreferences = { ...prevPreferences }; - Object.values(PreferenceKeys).forEach((key) => { - newPreferences = setPreference( - key, - defaultPreferences[key], - prevPreferences - ); - }); - const newSate = { - ...prevState, - preferences: { ...newPreferences }, - }; - return newSate; - }); + setPreferencesState((prevState) => resetState(keys, defaults, prevState)); }; - const [ - preferencesState, - setPreferencesState, - ] = useState({ - preferences: { ...props.initialPreferences }, + const [preferencesState, setPreferencesState] = useState< + PreferencesContextType + >({ + preferences: { ...initialPreferences }, updatePreferences: updatePreferences, resetPreferences: resetPreferences, }); + return preferencesState; +} +type PreferencesProviderProps< + T extends Partial, + K extends Partial +> = { + children: React.ReactChild; + initialPreferences: T; + defaults: T; + keys: Array; + Context: React.Context>; +}; + +function PreferencesProvider< + T extends Partial, + K extends Partial +>(props: PreferencesProviderProps) { + const { Context } = props; + const preferencesState = usePreferencesProvider( + props.initialPreferences, + props.defaults, + Object.values(props.keys) + ); return ( - + {props.children} - + + ); +} + +type Props = { + children: React.ReactChild; + initialPreferences: T; +}; + +export function GeneralPreferencesProvider( + props: Props +) { + return ( + + {props.children} + + ); +} + +export function PlanexPreferencesProvider(props: Props) { + return ( + + {props.children} + + ); +} + +export function ProxiwashPreferencesProvider( + props: Props +) { + return ( + + {props.children} + + ); +} + +export function MascotPreferencesProvider(props: Props) { + return ( + + {props.children} + ); } diff --git a/src/context/preferencesContext.tsx b/src/context/preferencesContext.tsx index 6fee694..127e274 100644 --- a/src/context/preferencesContext.tsx +++ b/src/context/preferencesContext.tsx @@ -2,11 +2,21 @@ import { useNavigation } from '@react-navigation/core'; import React, { useContext } from 'react'; import { Appearance } from 'react-native-appearance'; import { + defaultMascotPreferences, + defaultPlanexPreferences, defaultPreferences, + defaultProxiwashPreferences, getPreferenceBool, getPreferenceObject, - isValidPreferenceKey, - PreferenceKeys, + MascotPreferenceKeys, + MascotPreferencesType, + PlanexPreferenceKeys, + PlanexPreferencesType, + GeneralPreferenceKeys, + GeneralPreferencesType, + ProxiwashPreferenceKeys, + ProxiwashPreferencesType, + isValidMascotPreferenceKey, PreferencesType, } from '../utils/asyncStorage'; import { @@ -18,35 +28,83 @@ import { const colorScheme = Appearance.getColorScheme(); -export type PreferencesContextType = { - preferences: PreferencesType; +export type PreferencesContextType< + T extends Partial, + K extends string +> = { + preferences: T; updatePreferences: ( - key: PreferenceKeys, + key: K, value: number | string | boolean | object | Array ) => void; resetPreferences: () => void; }; -export const PreferencesContext = React.createContext({ +// CONTEXTES +// Preferences are separated into several contextes to improve performances + +export const PreferencesContext = React.createContext< + PreferencesContextType +>({ preferences: defaultPreferences, updatePreferences: () => undefined, resetPreferences: () => undefined, }); +export const PlanexPreferencesContext = React.createContext< + PreferencesContextType +>({ + preferences: defaultPlanexPreferences, + updatePreferences: () => undefined, + resetPreferences: () => undefined, +}); + +export const ProxiwashPreferencesContext = React.createContext< + PreferencesContextType +>({ + preferences: defaultProxiwashPreferences, + updatePreferences: () => undefined, + resetPreferences: () => undefined, +}); + +export const MascotPreferencesContext = React.createContext< + PreferencesContextType +>({ + preferences: defaultMascotPreferences, + updatePreferences: () => undefined, + resetPreferences: () => undefined, +}); + +// Context Hooks + export function usePreferences() { return useContext(PreferencesContext); } +export function usePlanexPreferences() { + return useContext(PlanexPreferencesContext); +} + +export function useProxiwashPreferences() { + return useContext(ProxiwashPreferencesContext); +} + +export function useMascotPreferences() { + return useContext(MascotPreferencesContext); +} + +// Custom Hooks + export function useShouldShowMascot(route: string) { - const { preferences, updatePreferences } = usePreferences(); + const { preferences, updatePreferences } = useMascotPreferences(); const key = route + 'ShowMascot'; let shouldShow = false; - if (isValidPreferenceKey(key)) { + if (isValidMascotPreferenceKey(key)) { shouldShow = getPreferenceBool(key, preferences) !== false; } const setShouldShow = (show: boolean) => { - if (isValidPreferenceKey(key)) { + if (isValidMascotPreferenceKey(key)) { updatePreferences(key, show); } else { console.log('Invalid preference key: ' + key); @@ -59,12 +117,17 @@ export function useShouldShowMascot(route: string) { export function useDarkTheme() { const { preferences } = usePreferences(); return ( - (getPreferenceBool(PreferenceKeys.nightMode, preferences) !== false && - (getPreferenceBool(PreferenceKeys.nightModeFollowSystem, preferences) === - false || - colorScheme === 'no-preference')) || - (getPreferenceBool(PreferenceKeys.nightModeFollowSystem, preferences) !== + (getPreferenceBool(GeneralPreferenceKeys.nightMode, preferences) !== false && + (getPreferenceBool( + GeneralPreferenceKeys.nightModeFollowSystem, + preferences + ) === false || + colorScheme === 'no-preference')) || + (getPreferenceBool( + GeneralPreferenceKeys.nightModeFollowSystem, + preferences + ) !== false && colorScheme === 'dark') ); } @@ -73,12 +136,12 @@ export function useCurrentDashboard() { const { preferences, updatePreferences } = usePreferences(); const navigation = useNavigation(); const dashboardIdList = getPreferenceObject( - PreferenceKeys.dashboardItems, + GeneralPreferenceKeys.dashboardItems, preferences ) as Array; const updateCurrentDashboard = (newList: Array) => { - updatePreferences(PreferenceKeys.dashboardItems, newList); + updatePreferences(GeneralPreferenceKeys.dashboardItems, newList); }; const allDatasets = [ diff --git a/src/navigation/MainNavigator.tsx b/src/navigation/MainNavigator.tsx index f3c7394..87cb01d 100644 --- a/src/navigation/MainNavigator.tsx +++ b/src/navigation/MainNavigator.tsx @@ -47,7 +47,10 @@ import DashboardEditScreen from '../screens/Other/Settings/DashboardEditScreen'; import GameStartScreen from '../screens/Game/screens/GameStartScreen'; import ImageGalleryScreen from '../screens/Media/ImageGalleryScreen'; import { usePreferences } from '../context/preferencesContext'; -import { getPreferenceBool, PreferenceKeys } from '../utils/asyncStorage'; +import { + getPreferenceBool, + GeneralPreferenceKeys, +} from '../utils/asyncStorage'; import IntroScreen from '../screens/Intro/IntroScreen'; export enum MainRoutes { @@ -319,7 +322,10 @@ type PropsType = { function MainNavigator(props: PropsType) { const { preferences } = usePreferences(); - const showIntro = getPreferenceBool(PreferenceKeys.showIntro, preferences); + const showIntro = getPreferenceBool( + GeneralPreferenceKeys.showIntro, + preferences + ); const createTabNavigator = () => ; return ( (null); const currentPreferences: Array = []; - Object.values(PreferenceKeys).forEach((key) => { + Object.values(GeneralPreferenceKeys).forEach((key) => { const newObject: PreferenceItemType = { key: key, current: preferences[key], @@ -141,7 +141,7 @@ function DebugScreen() { }; const saveNewPrefs = (key: string, value: string) => { - if (isValidPreferenceKey(key)) { + if (isValidGeneralPreferenceKey(key)) { updatePreferences(key, value); } if (modalRef.current) { diff --git a/src/screens/Intro/IntroScreen.tsx b/src/screens/Intro/IntroScreen.tsx index aeec19e..56d8ca3 100644 --- a/src/screens/Intro/IntroScreen.tsx +++ b/src/screens/Intro/IntroScreen.tsx @@ -6,29 +6,31 @@ import AprilFoolsManager from '../../managers/AprilFoolsManager'; import { getPreferenceBool, getPreferenceNumber, - PreferenceKeys, + GeneralPreferenceKeys, } from '../../utils/asyncStorage'; export default function IntroScreen() { const { preferences, updatePreferences } = usePreferences(); const onDone = () => { - updatePreferences(PreferenceKeys.showIntro, false); - updatePreferences(PreferenceKeys.updateNumber, Update.number); - updatePreferences(PreferenceKeys.showAprilFoolsStart, false); + updatePreferences(GeneralPreferenceKeys.showIntro, false); + updatePreferences(GeneralPreferenceKeys.updateNumber, Update.number); + updatePreferences(GeneralPreferenceKeys.showAprilFoolsStart, false); }; const showIntro = - getPreferenceBool(PreferenceKeys.showIntro, preferences) !== false; + getPreferenceBool(GeneralPreferenceKeys.showIntro, preferences) !== false; const isUpdate = - getPreferenceNumber(PreferenceKeys.updateNumber, preferences) !== + getPreferenceNumber(GeneralPreferenceKeys.updateNumber, preferences) !== Update.number && !showIntro; const isAprilFools = AprilFoolsManager.getInstance().isAprilFoolsEnabled() && - getPreferenceBool(PreferenceKeys.showAprilFoolsStart, preferences) !== - false && + getPreferenceBool( + GeneralPreferenceKeys.showAprilFoolsStart, + preferences + ) !== false && !showIntro; return ( diff --git a/src/screens/Other/Settings/SettingsScreen.tsx b/src/screens/Other/Settings/SettingsScreen.tsx index 4a70e4d..d5347d4 100644 --- a/src/screens/Other/Settings/SettingsScreen.tsx +++ b/src/screens/Other/Settings/SettingsScreen.tsx @@ -32,13 +32,17 @@ import { Appearance } from 'react-native-appearance'; import CustomSlider from '../../../components/Overrides/CustomSlider'; import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView'; import GENERAL_STYLES from '../../../constants/Styles'; -import { usePreferences } from '../../../context/preferencesContext'; +import { + usePreferences, + useProxiwashPreferences, +} from '../../../context/preferencesContext'; import { useNavigation } from '@react-navigation/core'; import { getPreferenceBool, getPreferenceNumber, getPreferenceString, - PreferenceKeys, + GeneralPreferenceKeys, + ProxiwashPreferenceKeys, } from '../../../utils/asyncStorage'; const styles = StyleSheet.create({ @@ -61,42 +65,49 @@ const styles = StyleSheet.create({ function SettingsScreen() { const navigation = useNavigation(); const theme = useTheme(); - const { preferences, updatePreferences } = usePreferences(); + const generalPreferences = usePreferences(); + const proxiwashPreferences = useProxiwashPreferences(); const nightMode = getPreferenceBool( - PreferenceKeys.nightMode, - preferences + GeneralPreferenceKeys.nightMode, + generalPreferences.preferences ) as boolean; const nightModeFollowSystem = (getPreferenceBool( - PreferenceKeys.nightModeFollowSystem, - preferences + GeneralPreferenceKeys.nightModeFollowSystem, + generalPreferences.preferences ) as boolean) && Appearance.getColorScheme() !== 'no-preference'; const startScreenPickerSelected = getPreferenceString( - PreferenceKeys.defaultStartScreen, - preferences + GeneralPreferenceKeys.defaultStartScreen, + generalPreferences.preferences ) as string; const selectedWash = getPreferenceString( - PreferenceKeys.selectedWash, - preferences + ProxiwashPreferenceKeys.selectedWash, + proxiwashPreferences.preferences ) as string; const isDebugUnlocked = getPreferenceBool( - PreferenceKeys.debugUnlocked, - preferences + GeneralPreferenceKeys.debugUnlocked, + generalPreferences.preferences ) as boolean; const notif = getPreferenceNumber( - PreferenceKeys.proxiwashNotifications, - preferences + ProxiwashPreferenceKeys.proxiwashNotifications, + proxiwashPreferences.preferences ); const savedNotificationReminder = !notif || Number.isNaN(notif) ? 0 : notif; const onProxiwashNotifPickerValueChange = (value: number) => { - updatePreferences(PreferenceKeys.proxiwashNotifications, value); + proxiwashPreferences.updatePreferences( + ProxiwashPreferenceKeys.proxiwashNotifications, + value + ); }; const onStartScreenPickerValueChange = (value: string) => { if (value != null) { - updatePreferences(PreferenceKeys.defaultStartScreen, value); + generalPreferences.updatePreferences( + GeneralPreferenceKeys.defaultStartScreen, + value + ); } }; @@ -150,12 +161,15 @@ function SettingsScreen() { }; const onToggleNightMode = () => { - updatePreferences(PreferenceKeys.nightMode, !nightMode); + generalPreferences.updatePreferences( + GeneralPreferenceKeys.nightMode, + !nightMode + ); }; const onToggleNightModeFollowSystem = () => { - updatePreferences( - PreferenceKeys.nightModeFollowSystem, + generalPreferences.updatePreferences( + GeneralPreferenceKeys.nightModeFollowSystem, !nightModeFollowSystem ); }; @@ -220,12 +234,18 @@ function SettingsScreen() { const onSelectWashValueChange = (value: string) => { if (value != null) { - updatePreferences(PreferenceKeys.selectedWash, value); + proxiwashPreferences.updatePreferences( + ProxiwashPreferenceKeys.selectedWash, + value + ); } }; const unlockDebugMode = () => { - updatePreferences(PreferenceKeys.debugUnlocked, true); + generalPreferences.updatePreferences( + GeneralPreferenceKeys.debugUnlocked, + true + ); }; return ( diff --git a/src/screens/Planex/GroupSelectionScreen.tsx b/src/screens/Planex/GroupSelectionScreen.tsx index b41de5a..12e7c43 100644 --- a/src/screens/Planex/GroupSelectionScreen.tsx +++ b/src/screens/Planex/GroupSelectionScreen.tsx @@ -28,8 +28,11 @@ import Urls from '../../constants/Urls'; import { readData } from '../../utils/WebData'; import { useNavigation } from '@react-navigation/core'; import { useCachedPlanexGroups } from '../../context/cacheContext'; -import { usePreferences } from '../../context/preferencesContext'; -import { getPreferenceObject, PreferenceKeys } from '../../utils/asyncStorage'; +import { usePlanexPreferences } from '../../context/preferencesContext'; +import { + getPreferenceObject, + PlanexPreferenceKeys, +} from '../../utils/asyncStorage'; export type PlanexGroupType = { name: string; @@ -59,13 +62,13 @@ function sortName( function GroupSelectionScreen() { const navigation = useNavigation(); - const { preferences, updatePreferences } = usePreferences(); + const { preferences, updatePreferences } = usePlanexPreferences(); const { groups, setGroups } = useCachedPlanexGroups(); const [currentSearchString, setCurrentSearchString] = useState(''); const getFavoriteGroups = (): Array => { const data = getPreferenceObject( - PreferenceKeys.planexFavoriteGroups, + PlanexPreferenceKeys.planexFavoriteGroups, preferences ); if (data) { @@ -146,7 +149,7 @@ function GroupSelectionScreen() { * @param item The article pressed */ const onListItemPress = (item: PlanexGroupType) => { - updatePreferences(PreferenceKeys.planexCurrentGroup, item); + updatePreferences(PlanexPreferenceKeys.planexCurrentGroup, item); navigation.goBack(); }; @@ -158,7 +161,7 @@ function GroupSelectionScreen() { const onListFavoritePress = useCallback( (group: PlanexGroupType) => { const updateFavorites = (newValue: Array) => { - updatePreferences(PreferenceKeys.planexFavoriteGroups, newValue); + updatePreferences(PlanexPreferenceKeys.planexFavoriteGroups, newValue); }; const removeGroupFromFavorites = (g: PlanexGroupType) => { diff --git a/src/screens/Planex/PlanexScreen.tsx b/src/screens/Planex/PlanexScreen.tsx index 855e0d5..b11163f 100644 --- a/src/screens/Planex/PlanexScreen.tsx +++ b/src/screens/Planex/PlanexScreen.tsx @@ -33,8 +33,12 @@ import { getPrettierPlanexGroupName } from '../../utils/Utils'; import GENERAL_STYLES from '../../constants/Styles'; import PlanexWebview from '../../components/Screens/PlanexWebview'; import PlanexBottomBar from '../../components/Animations/PlanexBottomBar'; -import { usePreferences } from '../../context/preferencesContext'; -import { getPreferenceString, PreferenceKeys } from '../../utils/asyncStorage'; +import { + getPreferenceString, + GeneralPreferenceKeys, + PlanexPreferenceKeys, +} from '../../utils/asyncStorage'; +import { usePlanexPreferences } from '../../context/preferencesContext'; const styles = StyleSheet.create({ container: { @@ -50,7 +54,7 @@ const styles = StyleSheet.create({ function PlanexScreen() { const navigation = useNavigation(); const theme = useTheme(); - const { preferences } = usePreferences(); + const { preferences } = usePlanexPreferences(); const [dialogContent, setDialogContent] = useState< | undefined @@ -64,7 +68,7 @@ function PlanexScreen() { const getCurrentGroup: () => PlanexGroupType | undefined = useCallback(() => { let currentGroupString = getPreferenceString( - PreferenceKeys.planexCurrentGroup, + PlanexPreferenceKeys.planexCurrentGroup, preferences ); let group: PlanexGroupType; @@ -184,7 +188,7 @@ function PlanexScreen() { const showMascot = getPreferenceString( - PreferenceKeys.defaultStartScreen, + GeneralPreferenceKeys.defaultStartScreen, preferences )?.toLowerCase() !== 'planex'; diff --git a/src/screens/Proxiwash/ProxiwashScreen.tsx b/src/screens/Proxiwash/ProxiwashScreen.tsx index cc2935a..b4ab57f 100644 --- a/src/screens/Proxiwash/ProxiwashScreen.tsx +++ b/src/screens/Proxiwash/ProxiwashScreen.tsx @@ -52,13 +52,13 @@ import { readData } from '../../utils/WebData'; import { useNavigation } from '@react-navigation/core'; import { setupMachineNotification } from '../../utils/Notifications'; import ProximoListHeader from '../../components/Lists/Proximo/ProximoListHeader'; -import { usePreferences } from '../../context/preferencesContext'; import { getPreferenceNumber, getPreferenceObject, getPreferenceString, - PreferenceKeys, + ProxiwashPreferenceKeys, } from '../../utils/asyncStorage'; +import { useProxiwashPreferences } from '../../context/preferencesContext'; const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds const LIST_ITEM_HEIGHT = 64; @@ -97,26 +97,29 @@ const styles = StyleSheet.create({ function ProxiwashScreen() { const navigation = useNavigation(); const theme = useTheme(); - const { preferences, updatePreferences } = usePreferences(); + const { preferences, updatePreferences } = useProxiwashPreferences(); const [ modalCurrentDisplayItem, setModalCurrentDisplayItem, ] = useState(null); const reminder = getPreferenceNumber( - PreferenceKeys.proxiwashNotifications, + ProxiwashPreferenceKeys.proxiwashNotifications, preferences ); const getMachinesWatched = () => { const data = getPreferenceObject( - PreferenceKeys.proxiwashWatchedMachines, + ProxiwashPreferenceKeys.proxiwashWatchedMachines, preferences ) as Array; return data ? (data as Array) : []; }; const getSelectedWash = () => { - const data = getPreferenceString(PreferenceKeys.selectedWash, preferences); + const data = getPreferenceString( + ProxiwashPreferenceKeys.selectedWash, + preferences + ); if (data !== 'washinsa' && data !== 'tripodeB') { return 'washinsa'; } else { @@ -350,7 +353,10 @@ function ProxiwashScreen() { ...data.washers, ]); if (cleanedList.length !== machinesWatched.length) { - updatePreferences(PreferenceKeys.proxiwashWatchedMachines, cleanedList); + updatePreferences( + ProxiwashPreferenceKeys.proxiwashWatchedMachines, + cleanedList + ); } return [ { @@ -415,7 +421,7 @@ function ProxiwashScreen() { }; const saveNewWatchedList = (list: Array) => { - updatePreferences(PreferenceKeys.proxiwashWatchedMachines, list); + updatePreferences(ProxiwashPreferenceKeys.proxiwashWatchedMachines, list); }; const renderListHeaderComponent = ( diff --git a/src/utils/asyncStorage.ts b/src/utils/asyncStorage.ts index c07b3d2..6cea841 100644 --- a/src/utils/asyncStorage.ts +++ b/src/utils/asyncStorage.ts @@ -1,15 +1,30 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import { SERVICES_KEY } from './Services'; -export enum PreferenceKeys { +export enum GeneralPreferenceKeys { debugUnlocked = 'debugUnlocked', showIntro = 'showIntro', updateNumber = 'updateNumber', - proxiwashNotifications = 'proxiwashNotifications', nightModeFollowSystem = 'nightModeFollowSystem', nightMode = 'nightMode', defaultStartScreen = 'defaultStartScreen', + showAprilFoolsStart = 'showAprilFoolsStart', + dashboardItems = 'dashboardItems', + gameScores = 'gameScores', +} +export enum PlanexPreferenceKeys { + planexCurrentGroup = 'planexCurrentGroup', + planexFavoriteGroups = 'planexFavoriteGroups', +} + +export enum ProxiwashPreferenceKeys { + proxiwashNotifications = 'proxiwashNotifications', + proxiwashWatchedMachines = 'proxiwashWatchedMachines', + selectedWash = 'selectedWash', +} + +export enum MascotPreferenceKeys { servicesShowMascot = 'servicesShowMascot', proxiwashShowMascot = 'proxiwashShowMascot', homeShowMascot = 'homeShowMascot', @@ -19,68 +34,109 @@ export enum PreferenceKeys { voteShowMascot = 'voteShowMascot', equipmentShowMascot = 'equipmentShowMascot', gameShowMascot = 'gameShowMascot', - - proxiwashWatchedMachines = 'proxiwashWatchedMachines', - showAprilFoolsStart = 'showAprilFoolsStart', - planexCurrentGroup = 'planexCurrentGroup', - planexFavoriteGroups = 'planexFavoriteGroups', - dashboardItems = 'dashboardItems', - gameScores = 'gameScores', - selectedWash = 'selectedWash', } -export type PreferencesType = { [key in PreferenceKeys]: string }; +export const PreferenceKeys = { + ...GeneralPreferenceKeys, + ...PlanexPreferenceKeys, + ...ProxiwashPreferenceKeys, + ...MascotPreferenceKeys, +}; +export type PreferenceKeys = + | GeneralPreferenceKeys + | PlanexPreferenceKeys + | ProxiwashPreferenceKeys + | MascotPreferenceKeys; -export const defaultPreferences: { [key in PreferenceKeys]: string } = { - [PreferenceKeys.debugUnlocked]: '0', - [PreferenceKeys.showIntro]: '1', - [PreferenceKeys.updateNumber]: '0', - [PreferenceKeys.proxiwashNotifications]: '5', - [PreferenceKeys.nightModeFollowSystem]: '1', - [PreferenceKeys.nightMode]: '1', - [PreferenceKeys.defaultStartScreen]: 'home', - [PreferenceKeys.servicesShowMascot]: '1', - [PreferenceKeys.proxiwashShowMascot]: '1', - [PreferenceKeys.homeShowMascot]: '1', - [PreferenceKeys.eventsShowMascot]: '1', - [PreferenceKeys.planexShowMascot]: '1', - [PreferenceKeys.loginShowMascot]: '1', - [PreferenceKeys.voteShowMascot]: '1', - [PreferenceKeys.equipmentShowMascot]: '1', - [PreferenceKeys.gameShowMascot]: '1', - [PreferenceKeys.proxiwashWatchedMachines]: '[]', - [PreferenceKeys.showAprilFoolsStart]: '1', - [PreferenceKeys.planexCurrentGroup]: '', - [PreferenceKeys.planexFavoriteGroups]: '[]', - [PreferenceKeys.dashboardItems]: JSON.stringify([ +export type PreferencesType = { [key in PreferenceKeys]: string }; +export type GeneralPreferencesType = { [key in GeneralPreferenceKeys]: string }; +export type PlanexPreferencesType = { + [key in PlanexPreferenceKeys]: string; +}; +export type ProxiwashPreferencesType = { + [key in ProxiwashPreferenceKeys]: string; +}; +export type MascotPreferencesType = { [key in MascotPreferenceKeys]: string }; + +export const defaultPlanexPreferences: { + [key in PlanexPreferenceKeys]: string; +} = { + [PlanexPreferenceKeys.planexCurrentGroup]: '', + [PlanexPreferenceKeys.planexFavoriteGroups]: '[]', +}; + +export const defaultProxiwashPreferences: { + [key in ProxiwashPreferenceKeys]: string; +} = { + [ProxiwashPreferenceKeys.proxiwashNotifications]: '5', + [ProxiwashPreferenceKeys.proxiwashWatchedMachines]: '[]', + [ProxiwashPreferenceKeys.selectedWash]: 'washinsa', +}; + +export const defaultMascotPreferences: { + [key in MascotPreferenceKeys]: string; +} = { + [MascotPreferenceKeys.servicesShowMascot]: '1', + [MascotPreferenceKeys.proxiwashShowMascot]: '1', + [MascotPreferenceKeys.homeShowMascot]: '1', + [MascotPreferenceKeys.eventsShowMascot]: '1', + [MascotPreferenceKeys.planexShowMascot]: '1', + [MascotPreferenceKeys.loginShowMascot]: '1', + [MascotPreferenceKeys.voteShowMascot]: '1', + [MascotPreferenceKeys.equipmentShowMascot]: '1', + [MascotPreferenceKeys.gameShowMascot]: '1', +}; + +export const defaultPreferences: { [key in GeneralPreferenceKeys]: string } = { + [GeneralPreferenceKeys.debugUnlocked]: '0', + [GeneralPreferenceKeys.showIntro]: '1', + [GeneralPreferenceKeys.updateNumber]: '0', + [GeneralPreferenceKeys.nightModeFollowSystem]: '1', + [GeneralPreferenceKeys.nightMode]: '1', + [GeneralPreferenceKeys.defaultStartScreen]: 'home', + [GeneralPreferenceKeys.showAprilFoolsStart]: '1', + + [GeneralPreferenceKeys.dashboardItems]: JSON.stringify([ SERVICES_KEY.EMAIL, SERVICES_KEY.WASHERS, SERVICES_KEY.PROXIMO, SERVICES_KEY.TUTOR_INSA, SERVICES_KEY.RU, ]), - [PreferenceKeys.gameScores]: '[]', - [PreferenceKeys.selectedWash]: 'washinsa', + + [GeneralPreferenceKeys.gameScores]: '[]', }; +export function isValidGeneralPreferenceKey( + key: string +): key is GeneralPreferenceKeys { + return key in Object.values(GeneralPreferenceKeys); +} + +export function isValidMascotPreferenceKey( + key: string +): key is MascotPreferenceKeys { + return key in Object.values(MascotPreferenceKeys); +} + /** * Set preferences object current values from AsyncStorage. * This function should be called once on start. * * @return {Promise} */ -export function retrievePreferences( - keys: Array, - defaults: PreferencesType -): Promise { - return new Promise((resolve: (preferences: PreferencesType) => void) => { +export function retrievePreferences< + Keys extends PreferenceKeys, + T extends Partial +>(keys: Array, defaults: T): Promise { + return new Promise((resolve: (preferences: T) => void) => { AsyncStorage.multiGet(keys) .then((result) => { const preferences = { ...defaults }; result.forEach((item) => { let [key, value] = item; if (value !== null) { - preferences[key as PreferenceKeys] = value; + preferences[key as Keys] = value; } }); resolve(preferences); @@ -96,11 +152,14 @@ export function retrievePreferences( * @param key * @param value */ -export function setPreference( - key: PreferenceKeys, - value: number | string | boolean | object | Array, - prevPreferences: PreferencesType -): PreferencesType { +export function setPreference< + Keys extends PreferenceKeys, + T extends Partial +>( + key: Keys, + value: number | string | boolean | object | Array | undefined, + prevPreferences: T +): T { let convertedValue: string; if (typeof value === 'string') { convertedValue = value; @@ -116,20 +175,16 @@ export function setPreference( return prevPreferences; } -export function isValidPreferenceKey(key: string): key is PreferenceKeys { - return key in Object.values(PreferenceKeys); -} - /** * Gets the boolean value of the given preference * * @param key * @returns {boolean} */ -export function getPreferenceString( - key: PreferenceKeys, - preferences: PreferencesType -): string | undefined { +export function getPreferenceString< + Keys extends PreferenceKeys, + T extends Partial +>(key: Keys, preferences: T): string | undefined { return preferences[key]; } @@ -139,10 +194,10 @@ export function getPreferenceString( * @param key * @returns {boolean} */ -export function getPreferenceBool( - key: PreferenceKeys, - preferences: PreferencesType -): boolean | undefined { +export function getPreferenceBool< + Keys extends PreferenceKeys, + T extends Partial +>(key: Keys, preferences: T): boolean | undefined { const value = preferences[key]; return value ? value === '1' || value === 'true' : undefined; } @@ -153,12 +208,12 @@ export function getPreferenceBool( * @param key * @returns {number} */ -export function getPreferenceNumber( - key: PreferenceKeys, - preferences: PreferencesType -): number | undefined { - const value = preferences[key]; - return value !== undefined ? parseFloat(value) : undefined; +export function getPreferenceNumber< + Keys extends PreferenceKeys, + T extends Partial +>(key: Keys, preferences: T): number | undefined { + const value = preferences[key] as string | undefined; + return value ? parseFloat(value) : undefined; } /** @@ -167,10 +222,10 @@ export function getPreferenceNumber( * @param key * @returns {{...}} */ -export function getPreferenceObject( - key: PreferenceKeys, - preferences: PreferencesType -): object | Array | undefined { - const value = preferences[key]; +export function getPreferenceObject< + Keys extends PreferenceKeys, + T extends Partial +>(key: Keys, preferences: T): object | Array | undefined { + const value = preferences[key] as string | undefined; return value ? JSON.parse(value) : undefined; }