Move preferences in separate contextes

This improves performance when updating preferences
This commit is contained in:
Arnaud Vergnet 2021-05-22 11:11:53 +02:00
parent 20d5e790d0
commit acbbd2d27d
12 changed files with 532 additions and 196 deletions

100
App.tsx
View file

@ -27,12 +27,26 @@ import URLHandler from './src/utils/URLHandler';
import initLocales from './src/utils/Locales'; import initLocales from './src/utils/Locales';
import { NavigationContainerRef } from '@react-navigation/core'; import { NavigationContainerRef } from '@react-navigation/core';
import { import {
defaultMascotPreferences,
defaultPlanexPreferences,
defaultPreferences, defaultPreferences,
PreferenceKeys, defaultProxiwashPreferences,
PreferencesType, GeneralPreferenceKeys,
GeneralPreferencesType,
MascotPreferenceKeys,
MascotPreferencesType,
PlanexPreferenceKeys,
PlanexPreferencesType,
ProxiwashPreferenceKeys,
ProxiwashPreferencesType,
retrievePreferences, retrievePreferences,
} from './src/utils/asyncStorage'; } 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'; import MainApp from './src/screens/MainApp';
// Native optimizations https://reactnavigation.org/docs/react-native-screens // 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', 'Cannot update a component from inside the function body of a different component',
]); ]);
// TODO move preferences in smaller contextes for improved performances
type StateType = { type StateType = {
isLoading: boolean; isLoading: boolean;
initialPreferences: PreferencesType; initialPreferences: {
general: GeneralPreferencesType;
planex: PlanexPreferencesType;
proxiwash: ProxiwashPreferencesType;
mascot: MascotPreferencesType;
};
}; };
export default class App extends React.Component<{}, StateType> { export default class App extends React.Component<{}, StateType> {
@ -63,7 +84,12 @@ export default class App extends React.Component<{}, StateType> {
super(props); super(props);
this.state = { this.state = {
isLoading: true, isLoading: true,
initialPreferences: defaultPreferences, initialPreferences: {
general: defaultPreferences,
planex: defaultPlanexPreferences,
proxiwash: defaultProxiwashPreferences,
mascot: defaultMascotPreferences,
},
}; };
initLocales(); initLocales();
this.navigatorRef = React.createRef(); this.navigatorRef = React.createRef();
@ -106,11 +132,24 @@ export default class App extends React.Component<{}, StateType> {
/** /**
* Async loading is done, finish processing startup data * Async loading is done, finish processing startup data
*/ */
onLoadFinished = (values: Array<PreferencesType | void>) => { onLoadFinished = (
const [preferences] = values; values: Array<
| GeneralPreferencesType
| PlanexPreferencesType
| ProxiwashPreferencesType
| MascotPreferencesType
| void
>
) => {
const [general, planex, proxiwash, mascot] = values;
this.setState({ this.setState({
isLoading: false, 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(); SplashScreen.hide();
}; };
@ -122,7 +161,22 @@ export default class App extends React.Component<{}, StateType> {
*/ */
loadAssetsAsync() { loadAssetsAsync() {
Promise.all([ 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(), ConnectionManager.getInstance().recoverLogin(),
]) ])
.then(this.onLoadFinished) .then(this.onLoadFinished)
@ -138,13 +192,27 @@ export default class App extends React.Component<{}, StateType> {
return null; return null;
} }
return ( return (
<PreferencesProvider initialPreferences={this.state.initialPreferences}> <GeneralPreferencesProvider
<MainApp initialPreferences={this.state.initialPreferences.general}
ref={this.navigatorRef} >
defaultHomeData={this.defaultHomeData} <PlanexPreferencesProvider
defaultHomeRoute={this.defaultHomeRoute} initialPreferences={this.state.initialPreferences.planex}
/> >
</PreferencesProvider> <ProxiwashPreferencesProvider
initialPreferences={this.state.initialPreferences.proxiwash}
>
<MascotPreferencesProvider
initialPreferences={this.state.initialPreferences.mascot}
>
<MainApp
ref={this.navigatorRef}
defaultHomeData={this.defaultHomeData}
defaultHomeRoute={this.defaultHomeRoute}
/>
</MascotPreferencesProvider>
</ProxiwashPreferencesProvider>
</PlanexPreferencesProvider>
</GeneralPreferencesProvider>
); );
} }
} }

View file

@ -1,67 +1,173 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { import {
defaultMascotPreferences,
defaultPlanexPreferences,
defaultPreferences, defaultPreferences,
defaultProxiwashPreferences,
GeneralPreferenceKeys,
GeneralPreferencesType,
MascotPreferenceKeys,
MascotPreferencesType,
PlanexPreferenceKeys,
PlanexPreferencesType,
PreferenceKeys, PreferenceKeys,
PreferencesType, PreferencesType,
ProxiwashPreferenceKeys,
ProxiwashPreferencesType,
setPreference, setPreference,
} from '../../utils/asyncStorage'; } from '../../utils/asyncStorage';
import { import {
MascotPreferencesContext,
PlanexPreferencesContext,
PreferencesContext, PreferencesContext,
PreferencesContextType, PreferencesContextType,
ProxiwashPreferencesContext,
} from '../../context/preferencesContext'; } from '../../context/preferencesContext';
type Props = { function updateState<T extends Partial<PreferencesType>, K extends string>(
children: React.ReactChild; key: K,
initialPreferences: PreferencesType; value: number | string | boolean | object | Array<any>,
}; prevState: PreferencesContextType<T, K>
) {
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<PreferencesType>,
K extends Partial<PreferenceKeys>
>(
keys: Array<PreferenceKeys>,
defaults: T,
prevState: PreferencesContextType<T, K>
) {
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<PreferencesType>,
K extends Partial<PreferenceKeys>
>(initialPreferences: T, defaults: T, keys: Array<K>) {
const updatePreferences = ( const updatePreferences = (
key: PreferenceKeys, key: K,
value: number | string | boolean | object | Array<any> value: number | string | boolean | object | Array<any>
) => { ) => {
setPreferencesState((prevState) => { setPreferencesState((prevState) => updateState(key, value, prevState));
const prevPreferences = { ...prevState.preferences };
const newPrefs = setPreference(key, value, prevPreferences);
const newSate = {
...prevState,
preferences: { ...newPrefs },
};
return newSate;
});
}; };
const resetPreferences = () => { const resetPreferences = () => {
setPreferencesState((prevState) => { setPreferencesState((prevState) => resetState(keys, defaults, 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;
});
}; };
const [ const [preferencesState, setPreferencesState] = useState<
preferencesState, PreferencesContextType<T, K>
setPreferencesState, >({
] = useState<PreferencesContextType>({ preferences: { ...initialPreferences },
preferences: { ...props.initialPreferences },
updatePreferences: updatePreferences, updatePreferences: updatePreferences,
resetPreferences: resetPreferences, resetPreferences: resetPreferences,
}); });
return preferencesState;
}
type PreferencesProviderProps<
T extends Partial<PreferencesType>,
K extends Partial<PreferenceKeys>
> = {
children: React.ReactChild;
initialPreferences: T;
defaults: T;
keys: Array<K>;
Context: React.Context<PreferencesContextType<T, K>>;
};
function PreferencesProvider<
T extends Partial<PreferencesType>,
K extends Partial<PreferenceKeys>
>(props: PreferencesProviderProps<T, K>) {
const { Context } = props;
const preferencesState = usePreferencesProvider<T, K>(
props.initialPreferences,
props.defaults,
Object.values(props.keys)
);
return ( return (
<PreferencesContext.Provider value={preferencesState}> <Context.Provider value={preferencesState}>
{props.children} {props.children}
</PreferencesContext.Provider> </Context.Provider>
);
}
type Props<T> = {
children: React.ReactChild;
initialPreferences: T;
};
export function GeneralPreferencesProvider(
props: Props<GeneralPreferencesType>
) {
return (
<PreferencesProvider
Context={PreferencesContext}
initialPreferences={props.initialPreferences}
defaults={defaultPreferences}
keys={Object.values(GeneralPreferenceKeys)}
>
{props.children}
</PreferencesProvider>
);
}
export function PlanexPreferencesProvider(props: Props<PlanexPreferencesType>) {
return (
<PreferencesProvider
Context={PlanexPreferencesContext}
initialPreferences={props.initialPreferences}
defaults={defaultPlanexPreferences}
keys={Object.values(PlanexPreferenceKeys)}
>
{props.children}
</PreferencesProvider>
);
}
export function ProxiwashPreferencesProvider(
props: Props<ProxiwashPreferencesType>
) {
return (
<PreferencesProvider
Context={ProxiwashPreferencesContext}
initialPreferences={props.initialPreferences}
defaults={defaultProxiwashPreferences}
keys={Object.values(ProxiwashPreferenceKeys)}
>
{props.children}
</PreferencesProvider>
);
}
export function MascotPreferencesProvider(props: Props<MascotPreferencesType>) {
return (
<PreferencesProvider
Context={MascotPreferencesContext}
initialPreferences={props.initialPreferences}
defaults={defaultMascotPreferences}
keys={Object.values(MascotPreferenceKeys)}
>
{props.children}
</PreferencesProvider>
); );
} }

View file

@ -2,11 +2,21 @@ import { useNavigation } from '@react-navigation/core';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { Appearance } from 'react-native-appearance'; import { Appearance } from 'react-native-appearance';
import { import {
defaultMascotPreferences,
defaultPlanexPreferences,
defaultPreferences, defaultPreferences,
defaultProxiwashPreferences,
getPreferenceBool, getPreferenceBool,
getPreferenceObject, getPreferenceObject,
isValidPreferenceKey, MascotPreferenceKeys,
PreferenceKeys, MascotPreferencesType,
PlanexPreferenceKeys,
PlanexPreferencesType,
GeneralPreferenceKeys,
GeneralPreferencesType,
ProxiwashPreferenceKeys,
ProxiwashPreferencesType,
isValidMascotPreferenceKey,
PreferencesType, PreferencesType,
} from '../utils/asyncStorage'; } from '../utils/asyncStorage';
import { import {
@ -18,35 +28,83 @@ import {
const colorScheme = Appearance.getColorScheme(); const colorScheme = Appearance.getColorScheme();
export type PreferencesContextType = { export type PreferencesContextType<
preferences: PreferencesType; T extends Partial<PreferencesType>,
K extends string
> = {
preferences: T;
updatePreferences: ( updatePreferences: (
key: PreferenceKeys, key: K,
value: number | string | boolean | object | Array<any> value: number | string | boolean | object | Array<any>
) => void; ) => void;
resetPreferences: () => void; resetPreferences: () => void;
}; };
export const PreferencesContext = React.createContext<PreferencesContextType>({ // CONTEXTES
// Preferences are separated into several contextes to improve performances
export const PreferencesContext = React.createContext<
PreferencesContextType<GeneralPreferencesType, GeneralPreferenceKeys>
>({
preferences: defaultPreferences, preferences: defaultPreferences,
updatePreferences: () => undefined, updatePreferences: () => undefined,
resetPreferences: () => undefined, resetPreferences: () => undefined,
}); });
export const PlanexPreferencesContext = React.createContext<
PreferencesContextType<PlanexPreferencesType, PlanexPreferenceKeys>
>({
preferences: defaultPlanexPreferences,
updatePreferences: () => undefined,
resetPreferences: () => undefined,
});
export const ProxiwashPreferencesContext = React.createContext<
PreferencesContextType<ProxiwashPreferencesType, ProxiwashPreferenceKeys>
>({
preferences: defaultProxiwashPreferences,
updatePreferences: () => undefined,
resetPreferences: () => undefined,
});
export const MascotPreferencesContext = React.createContext<
PreferencesContextType<MascotPreferencesType, MascotPreferenceKeys>
>({
preferences: defaultMascotPreferences,
updatePreferences: () => undefined,
resetPreferences: () => undefined,
});
// Context Hooks
export function usePreferences() { export function usePreferences() {
return useContext(PreferencesContext); 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) { export function useShouldShowMascot(route: string) {
const { preferences, updatePreferences } = usePreferences(); const { preferences, updatePreferences } = useMascotPreferences();
const key = route + 'ShowMascot'; const key = route + 'ShowMascot';
let shouldShow = false; let shouldShow = false;
if (isValidPreferenceKey(key)) { if (isValidMascotPreferenceKey(key)) {
shouldShow = getPreferenceBool(key, preferences) !== false; shouldShow = getPreferenceBool(key, preferences) !== false;
} }
const setShouldShow = (show: boolean) => { const setShouldShow = (show: boolean) => {
if (isValidPreferenceKey(key)) { if (isValidMascotPreferenceKey(key)) {
updatePreferences(key, show); updatePreferences(key, show);
} else { } else {
console.log('Invalid preference key: ' + key); console.log('Invalid preference key: ' + key);
@ -59,12 +117,17 @@ export function useShouldShowMascot(route: string) {
export function useDarkTheme() { export function useDarkTheme() {
const { preferences } = usePreferences(); const { preferences } = usePreferences();
return ( return (
(getPreferenceBool(PreferenceKeys.nightMode, preferences) !== false && (getPreferenceBool(GeneralPreferenceKeys.nightMode, preferences) !==
(getPreferenceBool(PreferenceKeys.nightModeFollowSystem, preferences) ===
false ||
colorScheme === 'no-preference')) ||
(getPreferenceBool(PreferenceKeys.nightModeFollowSystem, preferences) !==
false && false &&
(getPreferenceBool(
GeneralPreferenceKeys.nightModeFollowSystem,
preferences
) === false ||
colorScheme === 'no-preference')) ||
(getPreferenceBool(
GeneralPreferenceKeys.nightModeFollowSystem,
preferences
) !== false &&
colorScheme === 'dark') colorScheme === 'dark')
); );
} }
@ -73,12 +136,12 @@ export function useCurrentDashboard() {
const { preferences, updatePreferences } = usePreferences(); const { preferences, updatePreferences } = usePreferences();
const navigation = useNavigation(); const navigation = useNavigation();
const dashboardIdList = getPreferenceObject( const dashboardIdList = getPreferenceObject(
PreferenceKeys.dashboardItems, GeneralPreferenceKeys.dashboardItems,
preferences preferences
) as Array<string>; ) as Array<string>;
const updateCurrentDashboard = (newList: Array<string>) => { const updateCurrentDashboard = (newList: Array<string>) => {
updatePreferences(PreferenceKeys.dashboardItems, newList); updatePreferences(GeneralPreferenceKeys.dashboardItems, newList);
}; };
const allDatasets = [ const allDatasets = [

View file

@ -47,7 +47,10 @@ import DashboardEditScreen from '../screens/Other/Settings/DashboardEditScreen';
import GameStartScreen from '../screens/Game/screens/GameStartScreen'; import GameStartScreen from '../screens/Game/screens/GameStartScreen';
import ImageGalleryScreen from '../screens/Media/ImageGalleryScreen'; import ImageGalleryScreen from '../screens/Media/ImageGalleryScreen';
import { usePreferences } from '../context/preferencesContext'; import { usePreferences } from '../context/preferencesContext';
import { getPreferenceBool, PreferenceKeys } from '../utils/asyncStorage'; import {
getPreferenceBool,
GeneralPreferenceKeys,
} from '../utils/asyncStorage';
import IntroScreen from '../screens/Intro/IntroScreen'; import IntroScreen from '../screens/Intro/IntroScreen';
export enum MainRoutes { export enum MainRoutes {
@ -319,7 +322,10 @@ type PropsType = {
function MainNavigator(props: PropsType) { function MainNavigator(props: PropsType) {
const { preferences } = usePreferences(); const { preferences } = usePreferences();
const showIntro = getPreferenceBool(PreferenceKeys.showIntro, preferences); const showIntro = getPreferenceBool(
GeneralPreferenceKeys.showIntro,
preferences
);
const createTabNavigator = () => <TabNavigator {...props} />; const createTabNavigator = () => <TabNavigator {...props} />;
return ( return (
<MainStackComponent <MainStackComponent

View file

@ -41,7 +41,10 @@ import ServicesSectionScreen from '../screens/Services/ServicesSectionScreen';
import AmicaleContactScreen from '../screens/Amicale/AmicaleContactScreen'; import AmicaleContactScreen from '../screens/Amicale/AmicaleContactScreen';
import Mascot, { MASCOT_STYLE } from '../components/Mascot/Mascot'; import Mascot, { MASCOT_STYLE } from '../components/Mascot/Mascot';
import { usePreferences } from '../context/preferencesContext'; import { usePreferences } from '../context/preferencesContext';
import { getPreferenceString, PreferenceKeys } from '../utils/asyncStorage'; import {
getPreferenceString,
GeneralPreferenceKeys,
} from '../utils/asyncStorage';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
header: { header: {
@ -267,7 +270,7 @@ const ICONS: {
function TabNavigator(props: PropsType) { function TabNavigator(props: PropsType) {
const { preferences } = usePreferences(); const { preferences } = usePreferences();
let defaultRoute = getPreferenceString( let defaultRoute = getPreferenceString(
PreferenceKeys.defaultStartScreen, GeneralPreferenceKeys.defaultStartScreen,
preferences preferences
); );
if (!defaultRoute) { if (!defaultRoute) {

View file

@ -33,8 +33,8 @@ import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatLis
import { usePreferences } from '../../context/preferencesContext'; import { usePreferences } from '../../context/preferencesContext';
import { import {
defaultPreferences, defaultPreferences,
isValidPreferenceKey, GeneralPreferenceKeys,
PreferenceKeys, isValidGeneralPreferenceKey,
} from '../../utils/asyncStorage'; } from '../../utils/asyncStorage';
type PreferenceItemType = { type PreferenceItemType = {
@ -70,7 +70,7 @@ function DebugScreen() {
] = useState<PreferenceItemType | null>(null); ] = useState<PreferenceItemType | null>(null);
const currentPreferences: Array<PreferenceItemType> = []; const currentPreferences: Array<PreferenceItemType> = [];
Object.values(PreferenceKeys).forEach((key) => { Object.values(GeneralPreferenceKeys).forEach((key) => {
const newObject: PreferenceItemType = { const newObject: PreferenceItemType = {
key: key, key: key,
current: preferences[key], current: preferences[key],
@ -141,7 +141,7 @@ function DebugScreen() {
}; };
const saveNewPrefs = (key: string, value: string) => { const saveNewPrefs = (key: string, value: string) => {
if (isValidPreferenceKey(key)) { if (isValidGeneralPreferenceKey(key)) {
updatePreferences(key, value); updatePreferences(key, value);
} }
if (modalRef.current) { if (modalRef.current) {

View file

@ -6,29 +6,31 @@ import AprilFoolsManager from '../../managers/AprilFoolsManager';
import { import {
getPreferenceBool, getPreferenceBool,
getPreferenceNumber, getPreferenceNumber,
PreferenceKeys, GeneralPreferenceKeys,
} from '../../utils/asyncStorage'; } from '../../utils/asyncStorage';
export default function IntroScreen() { export default function IntroScreen() {
const { preferences, updatePreferences } = usePreferences(); const { preferences, updatePreferences } = usePreferences();
const onDone = () => { const onDone = () => {
updatePreferences(PreferenceKeys.showIntro, false); updatePreferences(GeneralPreferenceKeys.showIntro, false);
updatePreferences(PreferenceKeys.updateNumber, Update.number); updatePreferences(GeneralPreferenceKeys.updateNumber, Update.number);
updatePreferences(PreferenceKeys.showAprilFoolsStart, false); updatePreferences(GeneralPreferenceKeys.showAprilFoolsStart, false);
}; };
const showIntro = const showIntro =
getPreferenceBool(PreferenceKeys.showIntro, preferences) !== false; getPreferenceBool(GeneralPreferenceKeys.showIntro, preferences) !== false;
const isUpdate = const isUpdate =
getPreferenceNumber(PreferenceKeys.updateNumber, preferences) !== getPreferenceNumber(GeneralPreferenceKeys.updateNumber, preferences) !==
Update.number && !showIntro; Update.number && !showIntro;
const isAprilFools = const isAprilFools =
AprilFoolsManager.getInstance().isAprilFoolsEnabled() && AprilFoolsManager.getInstance().isAprilFoolsEnabled() &&
getPreferenceBool(PreferenceKeys.showAprilFoolsStart, preferences) !== getPreferenceBool(
false && GeneralPreferenceKeys.showAprilFoolsStart,
preferences
) !== false &&
!showIntro; !showIntro;
return ( return (

View file

@ -32,13 +32,17 @@ import { Appearance } from 'react-native-appearance';
import CustomSlider from '../../../components/Overrides/CustomSlider'; import CustomSlider from '../../../components/Overrides/CustomSlider';
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView'; import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
import GENERAL_STYLES from '../../../constants/Styles'; import GENERAL_STYLES from '../../../constants/Styles';
import { usePreferences } from '../../../context/preferencesContext'; import {
usePreferences,
useProxiwashPreferences,
} from '../../../context/preferencesContext';
import { useNavigation } from '@react-navigation/core'; import { useNavigation } from '@react-navigation/core';
import { import {
getPreferenceBool, getPreferenceBool,
getPreferenceNumber, getPreferenceNumber,
getPreferenceString, getPreferenceString,
PreferenceKeys, GeneralPreferenceKeys,
ProxiwashPreferenceKeys,
} from '../../../utils/asyncStorage'; } from '../../../utils/asyncStorage';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@ -61,42 +65,49 @@ const styles = StyleSheet.create({
function SettingsScreen() { function SettingsScreen() {
const navigation = useNavigation(); const navigation = useNavigation();
const theme = useTheme(); const theme = useTheme();
const { preferences, updatePreferences } = usePreferences(); const generalPreferences = usePreferences();
const proxiwashPreferences = useProxiwashPreferences();
const nightMode = getPreferenceBool( const nightMode = getPreferenceBool(
PreferenceKeys.nightMode, GeneralPreferenceKeys.nightMode,
preferences generalPreferences.preferences
) as boolean; ) as boolean;
const nightModeFollowSystem = const nightModeFollowSystem =
(getPreferenceBool( (getPreferenceBool(
PreferenceKeys.nightModeFollowSystem, GeneralPreferenceKeys.nightModeFollowSystem,
preferences generalPreferences.preferences
) as boolean) && Appearance.getColorScheme() !== 'no-preference'; ) as boolean) && Appearance.getColorScheme() !== 'no-preference';
const startScreenPickerSelected = getPreferenceString( const startScreenPickerSelected = getPreferenceString(
PreferenceKeys.defaultStartScreen, GeneralPreferenceKeys.defaultStartScreen,
preferences generalPreferences.preferences
) as string; ) as string;
const selectedWash = getPreferenceString( const selectedWash = getPreferenceString(
PreferenceKeys.selectedWash, ProxiwashPreferenceKeys.selectedWash,
preferences proxiwashPreferences.preferences
) as string; ) as string;
const isDebugUnlocked = getPreferenceBool( const isDebugUnlocked = getPreferenceBool(
PreferenceKeys.debugUnlocked, GeneralPreferenceKeys.debugUnlocked,
preferences generalPreferences.preferences
) as boolean; ) as boolean;
const notif = getPreferenceNumber( const notif = getPreferenceNumber(
PreferenceKeys.proxiwashNotifications, ProxiwashPreferenceKeys.proxiwashNotifications,
preferences proxiwashPreferences.preferences
); );
const savedNotificationReminder = !notif || Number.isNaN(notif) ? 0 : notif; const savedNotificationReminder = !notif || Number.isNaN(notif) ? 0 : notif;
const onProxiwashNotifPickerValueChange = (value: number) => { const onProxiwashNotifPickerValueChange = (value: number) => {
updatePreferences(PreferenceKeys.proxiwashNotifications, value); proxiwashPreferences.updatePreferences(
ProxiwashPreferenceKeys.proxiwashNotifications,
value
);
}; };
const onStartScreenPickerValueChange = (value: string) => { const onStartScreenPickerValueChange = (value: string) => {
if (value != null) { if (value != null) {
updatePreferences(PreferenceKeys.defaultStartScreen, value); generalPreferences.updatePreferences(
GeneralPreferenceKeys.defaultStartScreen,
value
);
} }
}; };
@ -150,12 +161,15 @@ function SettingsScreen() {
}; };
const onToggleNightMode = () => { const onToggleNightMode = () => {
updatePreferences(PreferenceKeys.nightMode, !nightMode); generalPreferences.updatePreferences(
GeneralPreferenceKeys.nightMode,
!nightMode
);
}; };
const onToggleNightModeFollowSystem = () => { const onToggleNightModeFollowSystem = () => {
updatePreferences( generalPreferences.updatePreferences(
PreferenceKeys.nightModeFollowSystem, GeneralPreferenceKeys.nightModeFollowSystem,
!nightModeFollowSystem !nightModeFollowSystem
); );
}; };
@ -220,12 +234,18 @@ function SettingsScreen() {
const onSelectWashValueChange = (value: string) => { const onSelectWashValueChange = (value: string) => {
if (value != null) { if (value != null) {
updatePreferences(PreferenceKeys.selectedWash, value); proxiwashPreferences.updatePreferences(
ProxiwashPreferenceKeys.selectedWash,
value
);
} }
}; };
const unlockDebugMode = () => { const unlockDebugMode = () => {
updatePreferences(PreferenceKeys.debugUnlocked, true); generalPreferences.updatePreferences(
GeneralPreferenceKeys.debugUnlocked,
true
);
}; };
return ( return (

View file

@ -28,8 +28,11 @@ import Urls from '../../constants/Urls';
import { readData } from '../../utils/WebData'; import { readData } from '../../utils/WebData';
import { useNavigation } from '@react-navigation/core'; import { useNavigation } from '@react-navigation/core';
import { useCachedPlanexGroups } from '../../context/cacheContext'; import { useCachedPlanexGroups } from '../../context/cacheContext';
import { usePreferences } from '../../context/preferencesContext'; import { usePlanexPreferences } from '../../context/preferencesContext';
import { getPreferenceObject, PreferenceKeys } from '../../utils/asyncStorage'; import {
getPreferenceObject,
PlanexPreferenceKeys,
} from '../../utils/asyncStorage';
export type PlanexGroupType = { export type PlanexGroupType = {
name: string; name: string;
@ -59,13 +62,13 @@ function sortName(
function GroupSelectionScreen() { function GroupSelectionScreen() {
const navigation = useNavigation(); const navigation = useNavigation();
const { preferences, updatePreferences } = usePreferences(); const { preferences, updatePreferences } = usePlanexPreferences();
const { groups, setGroups } = useCachedPlanexGroups(); const { groups, setGroups } = useCachedPlanexGroups();
const [currentSearchString, setCurrentSearchString] = useState(''); const [currentSearchString, setCurrentSearchString] = useState('');
const getFavoriteGroups = (): Array<PlanexGroupType> => { const getFavoriteGroups = (): Array<PlanexGroupType> => {
const data = getPreferenceObject( const data = getPreferenceObject(
PreferenceKeys.planexFavoriteGroups, PlanexPreferenceKeys.planexFavoriteGroups,
preferences preferences
); );
if (data) { if (data) {
@ -146,7 +149,7 @@ function GroupSelectionScreen() {
* @param item The article pressed * @param item The article pressed
*/ */
const onListItemPress = (item: PlanexGroupType) => { const onListItemPress = (item: PlanexGroupType) => {
updatePreferences(PreferenceKeys.planexCurrentGroup, item); updatePreferences(PlanexPreferenceKeys.planexCurrentGroup, item);
navigation.goBack(); navigation.goBack();
}; };
@ -158,7 +161,7 @@ function GroupSelectionScreen() {
const onListFavoritePress = useCallback( const onListFavoritePress = useCallback(
(group: PlanexGroupType) => { (group: PlanexGroupType) => {
const updateFavorites = (newValue: Array<PlanexGroupType>) => { const updateFavorites = (newValue: Array<PlanexGroupType>) => {
updatePreferences(PreferenceKeys.planexFavoriteGroups, newValue); updatePreferences(PlanexPreferenceKeys.planexFavoriteGroups, newValue);
}; };
const removeGroupFromFavorites = (g: PlanexGroupType) => { const removeGroupFromFavorites = (g: PlanexGroupType) => {

View file

@ -33,8 +33,12 @@ import { getPrettierPlanexGroupName } from '../../utils/Utils';
import GENERAL_STYLES from '../../constants/Styles'; import GENERAL_STYLES from '../../constants/Styles';
import PlanexWebview from '../../components/Screens/PlanexWebview'; import PlanexWebview from '../../components/Screens/PlanexWebview';
import PlanexBottomBar from '../../components/Animations/PlanexBottomBar'; import PlanexBottomBar from '../../components/Animations/PlanexBottomBar';
import { usePreferences } from '../../context/preferencesContext'; import {
import { getPreferenceString, PreferenceKeys } from '../../utils/asyncStorage'; getPreferenceString,
GeneralPreferenceKeys,
PlanexPreferenceKeys,
} from '../../utils/asyncStorage';
import { usePlanexPreferences } from '../../context/preferencesContext';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -50,7 +54,7 @@ const styles = StyleSheet.create({
function PlanexScreen() { function PlanexScreen() {
const navigation = useNavigation(); const navigation = useNavigation();
const theme = useTheme(); const theme = useTheme();
const { preferences } = usePreferences(); const { preferences } = usePlanexPreferences();
const [dialogContent, setDialogContent] = useState< const [dialogContent, setDialogContent] = useState<
| undefined | undefined
@ -64,7 +68,7 @@ function PlanexScreen() {
const getCurrentGroup: () => PlanexGroupType | undefined = useCallback(() => { const getCurrentGroup: () => PlanexGroupType | undefined = useCallback(() => {
let currentGroupString = getPreferenceString( let currentGroupString = getPreferenceString(
PreferenceKeys.planexCurrentGroup, PlanexPreferenceKeys.planexCurrentGroup,
preferences preferences
); );
let group: PlanexGroupType; let group: PlanexGroupType;
@ -184,7 +188,7 @@ function PlanexScreen() {
const showMascot = const showMascot =
getPreferenceString( getPreferenceString(
PreferenceKeys.defaultStartScreen, GeneralPreferenceKeys.defaultStartScreen,
preferences preferences
)?.toLowerCase() !== 'planex'; )?.toLowerCase() !== 'planex';

View file

@ -52,13 +52,13 @@ import { readData } from '../../utils/WebData';
import { useNavigation } from '@react-navigation/core'; import { useNavigation } from '@react-navigation/core';
import { setupMachineNotification } from '../../utils/Notifications'; import { setupMachineNotification } from '../../utils/Notifications';
import ProximoListHeader from '../../components/Lists/Proximo/ProximoListHeader'; import ProximoListHeader from '../../components/Lists/Proximo/ProximoListHeader';
import { usePreferences } from '../../context/preferencesContext';
import { import {
getPreferenceNumber, getPreferenceNumber,
getPreferenceObject, getPreferenceObject,
getPreferenceString, getPreferenceString,
PreferenceKeys, ProxiwashPreferenceKeys,
} from '../../utils/asyncStorage'; } from '../../utils/asyncStorage';
import { useProxiwashPreferences } from '../../context/preferencesContext';
const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds
const LIST_ITEM_HEIGHT = 64; const LIST_ITEM_HEIGHT = 64;
@ -97,26 +97,29 @@ const styles = StyleSheet.create({
function ProxiwashScreen() { function ProxiwashScreen() {
const navigation = useNavigation(); const navigation = useNavigation();
const theme = useTheme(); const theme = useTheme();
const { preferences, updatePreferences } = usePreferences(); const { preferences, updatePreferences } = useProxiwashPreferences();
const [ const [
modalCurrentDisplayItem, modalCurrentDisplayItem,
setModalCurrentDisplayItem, setModalCurrentDisplayItem,
] = useState<React.ReactElement | null>(null); ] = useState<React.ReactElement | null>(null);
const reminder = getPreferenceNumber( const reminder = getPreferenceNumber(
PreferenceKeys.proxiwashNotifications, ProxiwashPreferenceKeys.proxiwashNotifications,
preferences preferences
); );
const getMachinesWatched = () => { const getMachinesWatched = () => {
const data = getPreferenceObject( const data = getPreferenceObject(
PreferenceKeys.proxiwashWatchedMachines, ProxiwashPreferenceKeys.proxiwashWatchedMachines,
preferences preferences
) as Array<ProxiwashMachineType>; ) as Array<ProxiwashMachineType>;
return data ? (data as Array<ProxiwashMachineType>) : []; return data ? (data as Array<ProxiwashMachineType>) : [];
}; };
const getSelectedWash = () => { const getSelectedWash = () => {
const data = getPreferenceString(PreferenceKeys.selectedWash, preferences); const data = getPreferenceString(
ProxiwashPreferenceKeys.selectedWash,
preferences
);
if (data !== 'washinsa' && data !== 'tripodeB') { if (data !== 'washinsa' && data !== 'tripodeB') {
return 'washinsa'; return 'washinsa';
} else { } else {
@ -350,7 +353,10 @@ function ProxiwashScreen() {
...data.washers, ...data.washers,
]); ]);
if (cleanedList.length !== machinesWatched.length) { if (cleanedList.length !== machinesWatched.length) {
updatePreferences(PreferenceKeys.proxiwashWatchedMachines, cleanedList); updatePreferences(
ProxiwashPreferenceKeys.proxiwashWatchedMachines,
cleanedList
);
} }
return [ return [
{ {
@ -415,7 +421,7 @@ function ProxiwashScreen() {
}; };
const saveNewWatchedList = (list: Array<ProxiwashMachineType>) => { const saveNewWatchedList = (list: Array<ProxiwashMachineType>) => {
updatePreferences(PreferenceKeys.proxiwashWatchedMachines, list); updatePreferences(ProxiwashPreferenceKeys.proxiwashWatchedMachines, list);
}; };
const renderListHeaderComponent = ( const renderListHeaderComponent = (

View file

@ -1,15 +1,30 @@
import AsyncStorage from '@react-native-async-storage/async-storage'; import AsyncStorage from '@react-native-async-storage/async-storage';
import { SERVICES_KEY } from './Services'; import { SERVICES_KEY } from './Services';
export enum PreferenceKeys { export enum GeneralPreferenceKeys {
debugUnlocked = 'debugUnlocked', debugUnlocked = 'debugUnlocked',
showIntro = 'showIntro', showIntro = 'showIntro',
updateNumber = 'updateNumber', updateNumber = 'updateNumber',
proxiwashNotifications = 'proxiwashNotifications',
nightModeFollowSystem = 'nightModeFollowSystem', nightModeFollowSystem = 'nightModeFollowSystem',
nightMode = 'nightMode', nightMode = 'nightMode',
defaultStartScreen = 'defaultStartScreen', 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', servicesShowMascot = 'servicesShowMascot',
proxiwashShowMascot = 'proxiwashShowMascot', proxiwashShowMascot = 'proxiwashShowMascot',
homeShowMascot = 'homeShowMascot', homeShowMascot = 'homeShowMascot',
@ -19,68 +34,109 @@ export enum PreferenceKeys {
voteShowMascot = 'voteShowMascot', voteShowMascot = 'voteShowMascot',
equipmentShowMascot = 'equipmentShowMascot', equipmentShowMascot = 'equipmentShowMascot',
gameShowMascot = 'gameShowMascot', 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 } = { export type PreferencesType = { [key in PreferenceKeys]: string };
[PreferenceKeys.debugUnlocked]: '0', export type GeneralPreferencesType = { [key in GeneralPreferenceKeys]: string };
[PreferenceKeys.showIntro]: '1', export type PlanexPreferencesType = {
[PreferenceKeys.updateNumber]: '0', [key in PlanexPreferenceKeys]: string;
[PreferenceKeys.proxiwashNotifications]: '5', };
[PreferenceKeys.nightModeFollowSystem]: '1', export type ProxiwashPreferencesType = {
[PreferenceKeys.nightMode]: '1', [key in ProxiwashPreferenceKeys]: string;
[PreferenceKeys.defaultStartScreen]: 'home', };
[PreferenceKeys.servicesShowMascot]: '1', export type MascotPreferencesType = { [key in MascotPreferenceKeys]: string };
[PreferenceKeys.proxiwashShowMascot]: '1',
[PreferenceKeys.homeShowMascot]: '1', export const defaultPlanexPreferences: {
[PreferenceKeys.eventsShowMascot]: '1', [key in PlanexPreferenceKeys]: string;
[PreferenceKeys.planexShowMascot]: '1', } = {
[PreferenceKeys.loginShowMascot]: '1', [PlanexPreferenceKeys.planexCurrentGroup]: '',
[PreferenceKeys.voteShowMascot]: '1', [PlanexPreferenceKeys.planexFavoriteGroups]: '[]',
[PreferenceKeys.equipmentShowMascot]: '1', };
[PreferenceKeys.gameShowMascot]: '1',
[PreferenceKeys.proxiwashWatchedMachines]: '[]', export const defaultProxiwashPreferences: {
[PreferenceKeys.showAprilFoolsStart]: '1', [key in ProxiwashPreferenceKeys]: string;
[PreferenceKeys.planexCurrentGroup]: '', } = {
[PreferenceKeys.planexFavoriteGroups]: '[]', [ProxiwashPreferenceKeys.proxiwashNotifications]: '5',
[PreferenceKeys.dashboardItems]: JSON.stringify([ [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.EMAIL,
SERVICES_KEY.WASHERS, SERVICES_KEY.WASHERS,
SERVICES_KEY.PROXIMO, SERVICES_KEY.PROXIMO,
SERVICES_KEY.TUTOR_INSA, SERVICES_KEY.TUTOR_INSA,
SERVICES_KEY.RU, 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. * Set preferences object current values from AsyncStorage.
* This function should be called once on start. * This function should be called once on start.
* *
* @return {Promise<PreferencesType>} * @return {Promise<PreferencesType>}
*/ */
export function retrievePreferences( export function retrievePreferences<
keys: Array<PreferenceKeys>, Keys extends PreferenceKeys,
defaults: PreferencesType T extends Partial<PreferencesType>
): Promise<PreferencesType> { >(keys: Array<Keys>, defaults: T): Promise<T> {
return new Promise((resolve: (preferences: PreferencesType) => void) => { return new Promise((resolve: (preferences: T) => void) => {
AsyncStorage.multiGet(keys) AsyncStorage.multiGet(keys)
.then((result) => { .then((result) => {
const preferences = { ...defaults }; const preferences = { ...defaults };
result.forEach((item) => { result.forEach((item) => {
let [key, value] = item; let [key, value] = item;
if (value !== null) { if (value !== null) {
preferences[key as PreferenceKeys] = value; preferences[key as Keys] = value;
} }
}); });
resolve(preferences); resolve(preferences);
@ -96,11 +152,14 @@ export function retrievePreferences(
* @param key * @param key
* @param value * @param value
*/ */
export function setPreference( export function setPreference<
key: PreferenceKeys, Keys extends PreferenceKeys,
value: number | string | boolean | object | Array<any>, T extends Partial<PreferencesType>
prevPreferences: PreferencesType >(
): PreferencesType { key: Keys,
value: number | string | boolean | object | Array<any> | undefined,
prevPreferences: T
): T {
let convertedValue: string; let convertedValue: string;
if (typeof value === 'string') { if (typeof value === 'string') {
convertedValue = value; convertedValue = value;
@ -116,20 +175,16 @@ export function setPreference(
return prevPreferences; return prevPreferences;
} }
export function isValidPreferenceKey(key: string): key is PreferenceKeys {
return key in Object.values(PreferenceKeys);
}
/** /**
* Gets the boolean value of the given preference * Gets the boolean value of the given preference
* *
* @param key * @param key
* @returns {boolean} * @returns {boolean}
*/ */
export function getPreferenceString( export function getPreferenceString<
key: PreferenceKeys, Keys extends PreferenceKeys,
preferences: PreferencesType T extends Partial<PreferencesType>
): string | undefined { >(key: Keys, preferences: T): string | undefined {
return preferences[key]; return preferences[key];
} }
@ -139,10 +194,10 @@ export function getPreferenceString(
* @param key * @param key
* @returns {boolean} * @returns {boolean}
*/ */
export function getPreferenceBool( export function getPreferenceBool<
key: PreferenceKeys, Keys extends PreferenceKeys,
preferences: PreferencesType T extends Partial<PreferencesType>
): boolean | undefined { >(key: Keys, preferences: T): boolean | undefined {
const value = preferences[key]; const value = preferences[key];
return value ? value === '1' || value === 'true' : undefined; return value ? value === '1' || value === 'true' : undefined;
} }
@ -153,12 +208,12 @@ export function getPreferenceBool(
* @param key * @param key
* @returns {number} * @returns {number}
*/ */
export function getPreferenceNumber( export function getPreferenceNumber<
key: PreferenceKeys, Keys extends PreferenceKeys,
preferences: PreferencesType T extends Partial<PreferencesType>
): number | undefined { >(key: Keys, preferences: T): number | undefined {
const value = preferences[key]; const value = preferences[key] as string | undefined;
return value !== undefined ? parseFloat(value) : undefined; return value ? parseFloat(value) : undefined;
} }
/** /**
@ -167,10 +222,10 @@ export function getPreferenceNumber(
* @param key * @param key
* @returns {{...}} * @returns {{...}}
*/ */
export function getPreferenceObject( export function getPreferenceObject<
key: PreferenceKeys, Keys extends PreferenceKeys,
preferences: PreferencesType T extends Partial<PreferencesType>
): object | Array<any> | undefined { >(key: Keys, preferences: T): object | Array<any> | undefined {
const value = preferences[key]; const value = preferences[key] as string | undefined;
return value ? JSON.parse(value) : undefined; return value ? JSON.parse(value) : undefined;
} }