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

90
App.tsx
View file

@ -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<PreferencesType | void>) => {
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 (
<PreferencesProvider initialPreferences={this.state.initialPreferences}>
<GeneralPreferencesProvider
initialPreferences={this.state.initialPreferences.general}
>
<PlanexPreferencesProvider
initialPreferences={this.state.initialPreferences.planex}
>
<ProxiwashPreferencesProvider
initialPreferences={this.state.initialPreferences.proxiwash}
>
<MascotPreferencesProvider
initialPreferences={this.state.initialPreferences.mascot}
>
<MainApp
ref={this.navigatorRef}
defaultHomeData={this.defaultHomeData}
defaultHomeRoute={this.defaultHomeRoute}
/>
</PreferencesProvider>
</MascotPreferencesProvider>
</ProxiwashPreferencesProvider>
</PlanexPreferencesProvider>
</GeneralPreferencesProvider>
);
}
}

View file

@ -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;
};
export default function PreferencesProvider(props: Props) {
const updatePreferences = (
key: PreferenceKeys,
value: number | string | boolean | object | Array<any>
) => {
setPreferencesState((prevState) => {
function updateState<T extends Partial<PreferencesType>, K extends string>(
key: K,
value: number | string | boolean | object | Array<any>,
prevState: PreferencesContextType<T, K>
) {
const prevPreferences = { ...prevState.preferences };
const newPrefs = setPreference(key, value, prevPreferences);
const newPrefs = setPreference(key as PreferenceKeys, value, prevPreferences);
const newSate = {
...prevState,
preferences: { ...newPrefs },
};
return newSate;
});
};
}
const resetPreferences = () => {
setPreferencesState((prevState) => {
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 };
Object.values(PreferenceKeys).forEach((key) => {
newPreferences = setPreference(
key,
defaultPreferences[key],
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 = (
key: K,
value: number | string | boolean | object | Array<any>
) => {
setPreferencesState((prevState) => updateState(key, value, prevState));
};
const [
preferencesState,
setPreferencesState,
] = useState<PreferencesContextType>({
preferences: { ...props.initialPreferences },
const resetPreferences = () => {
setPreferencesState((prevState) => resetState(keys, defaults, prevState));
};
const [preferencesState, setPreferencesState] = useState<
PreferencesContextType<T, K>
>({
preferences: { ...initialPreferences },
updatePreferences: updatePreferences,
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 (
<PreferencesContext.Provider value={preferencesState}>
<Context.Provider value={preferencesState}>
{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 { 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<PreferencesType>,
K extends string
> = {
preferences: T;
updatePreferences: (
key: PreferenceKeys,
key: K,
value: number | string | boolean | object | Array<any>
) => 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,
updatePreferences: () => 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() {
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<string>;
const updateCurrentDashboard = (newList: Array<string>) => {
updatePreferences(PreferenceKeys.dashboardItems, newList);
updatePreferences(GeneralPreferenceKeys.dashboardItems, newList);
};
const allDatasets = [

View file

@ -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 = () => <TabNavigator {...props} />;
return (
<MainStackComponent

View file

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

View file

@ -33,8 +33,8 @@ import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatLis
import { usePreferences } from '../../context/preferencesContext';
import {
defaultPreferences,
isValidPreferenceKey,
PreferenceKeys,
GeneralPreferenceKeys,
isValidGeneralPreferenceKey,
} from '../../utils/asyncStorage';
type PreferenceItemType = {
@ -70,7 +70,7 @@ function DebugScreen() {
] = useState<PreferenceItemType | null>(null);
const currentPreferences: Array<PreferenceItemType> = [];
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) {

View file

@ -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 (

View file

@ -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 (

View file

@ -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<PlanexGroupType> => {
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<PlanexGroupType>) => {
updatePreferences(PreferenceKeys.planexFavoriteGroups, newValue);
updatePreferences(PlanexPreferenceKeys.planexFavoriteGroups, newValue);
};
const removeGroupFromFavorites = (g: PlanexGroupType) => {

View file

@ -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';

View file

@ -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<React.ReactElement | null>(null);
const reminder = getPreferenceNumber(
PreferenceKeys.proxiwashNotifications,
ProxiwashPreferenceKeys.proxiwashNotifications,
preferences
);
const getMachinesWatched = () => {
const data = getPreferenceObject(
PreferenceKeys.proxiwashWatchedMachines,
ProxiwashPreferenceKeys.proxiwashWatchedMachines,
preferences
) as Array<ProxiwashMachineType>;
return data ? (data as Array<ProxiwashMachineType>) : [];
};
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<ProxiwashMachineType>) => {
updatePreferences(PreferenceKeys.proxiwashWatchedMachines, list);
updatePreferences(ProxiwashPreferenceKeys.proxiwashWatchedMachines, list);
};
const renderListHeaderComponent = (

View file

@ -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<PreferencesType>}
*/
export function retrievePreferences(
keys: Array<PreferenceKeys>,
defaults: PreferencesType
): Promise<PreferencesType> {
return new Promise((resolve: (preferences: PreferencesType) => void) => {
export function retrievePreferences<
Keys extends PreferenceKeys,
T extends Partial<PreferencesType>
>(keys: Array<Keys>, defaults: T): Promise<T> {
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<any>,
prevPreferences: PreferencesType
): PreferencesType {
export function setPreference<
Keys extends PreferenceKeys,
T extends Partial<PreferencesType>
>(
key: Keys,
value: number | string | boolean | object | Array<any> | 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<PreferencesType>
>(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<PreferencesType>
>(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<PreferencesType>
>(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<any> | undefined {
const value = preferences[key];
export function getPreferenceObject<
Keys extends PreferenceKeys,
T extends Partial<PreferencesType>
>(key: Keys, preferences: T): object | Array<any> | undefined {
const value = preferences[key] as string | undefined;
return value ? JSON.parse(value) : undefined;
}