diff --git a/App.tsx b/App.tsx
index 66a25d6..166f144 100644
--- a/App.tsx
+++ b/App.tsx
@@ -18,27 +18,21 @@
*/
import React from 'react';
-import { LogBox, Platform, SafeAreaView, View } from 'react-native';
-import { NavigationContainer } from '@react-navigation/native';
-import { Provider as PaperProvider } from 'react-native-paper';
+import { LogBox, Platform } from 'react-native';
import { setSafeBounceHeight } from 'react-navigation-collapsible';
import SplashScreen from 'react-native-splash-screen';
-import { OverflowMenuProvider } from 'react-navigation-header-buttons';
-import AsyncStorageManager from './src/managers/AsyncStorageManager';
-import CustomIntroSlider from './src/components/Overrides/CustomIntroSlider';
-import ThemeManager from './src/managers/ThemeManager';
-import MainNavigator from './src/navigation/MainNavigator';
-import AprilFoolsManager from './src/managers/AprilFoolsManager';
-import Update from './src/constants/Update';
import ConnectionManager from './src/managers/ConnectionManager';
import type { ParsedUrlDataType } from './src/utils/URLHandler';
import URLHandler from './src/utils/URLHandler';
-import { setupStatusBar } from './src/utils/Utils';
import initLocales from './src/utils/Locales';
import { NavigationContainerRef } from '@react-navigation/core';
-import GENERAL_STYLES from './src/constants/Styles';
-import CollapsibleProvider from './src/components/providers/CollapsibleProvider';
-import CacheProvider from './src/components/providers/CacheProvider';
+import {
+ defaultPreferences,
+ PreferenceKeys,
+ retrievePreferences,
+} from './src/utils/asyncStorage';
+import PreferencesProvider from './src/components/providers/PreferencesProvider';
+import MainApp from './src/screens/MainApp';
// Native optimizations https://reactnavigation.org/docs/react-native-screens
// Crashes app when navigating away from webview on android 9+
@@ -52,10 +46,6 @@ LogBox.ignoreLogs([
type StateType = {
isLoading: boolean;
- showIntro: boolean;
- showUpdate: boolean;
- showAprilFools: boolean;
- currentTheme: ReactNativePaper.Theme | undefined;
};
export default class App extends React.Component<{}, StateType> {
@@ -71,10 +61,6 @@ export default class App extends React.Component<{}, StateType> {
super(props);
this.state = {
isLoading: true,
- showIntro: true,
- showUpdate: true,
- showAprilFools: false,
- currentTheme: undefined,
};
initLocales();
this.navigatorRef = React.createRef();
@@ -114,67 +100,12 @@ export default class App extends React.Component<{}, StateType> {
}
};
- /**
- * Updates the current theme
- */
- onUpdateTheme = () => {
- this.setState({
- currentTheme: ThemeManager.getCurrentTheme(),
- });
- setupStatusBar();
- };
-
- /**
- * Callback when user ends the intro. Save in preferences to avoid showing back the introSlides
- */
- onIntroDone = () => {
- this.setState({
- showIntro: false,
- showUpdate: false,
- showAprilFools: false,
- });
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.showIntro.key,
- false
- );
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.updateNumber.key,
- Update.number
- );
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key,
- false
- );
- };
-
/**
* Async loading is done, finish processing startup data
*/
onLoadFinished = () => {
- // Only show intro if this is the first time starting the app
- ThemeManager.getInstance().setUpdateThemeCallback(this.onUpdateTheme);
- // Status bar goes dark if set too fast on ios
- if (Platform.OS === 'ios') {
- setTimeout(setupStatusBar, 1000);
- } else {
- setupStatusBar();
- }
-
this.setState({
isLoading: false,
- currentTheme: ThemeManager.getCurrentTheme(),
- showIntro: AsyncStorageManager.getBool(
- AsyncStorageManager.PREFERENCES.showIntro.key
- ),
- showUpdate:
- AsyncStorageManager.getNumber(
- AsyncStorageManager.PREFERENCES.updateNumber.key
- ) !== Update.number,
- showAprilFools:
- AprilFoolsManager.getInstance().isAprilFoolsEnabled() &&
- AsyncStorageManager.getBool(
- AsyncStorageManager.PREFERENCES.showAprilFoolsStart.key
- ),
});
SplashScreen.hide();
};
@@ -186,7 +117,7 @@ export default class App extends React.Component<{}, StateType> {
*/
loadAssetsAsync() {
Promise.all([
- AsyncStorageManager.getInstance().loadPreferences(),
+ retrievePreferences(Object.values(PreferenceKeys), defaultPreferences),
ConnectionManager.getInstance().recoverLogin(),
])
.then(this.onLoadFinished)
@@ -201,43 +132,14 @@ export default class App extends React.Component<{}, StateType> {
if (state.isLoading) {
return null;
}
- if (state.showIntro || state.showUpdate || state.showAprilFools) {
- return (
-
- );
- }
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
);
}
}
diff --git a/src/components/Lists/CardList/CardList.tsx b/src/components/Lists/CardList/CardList.tsx
index 3df77c0..e7a60c8 100644
--- a/src/components/Lists/CardList/CardList.tsx
+++ b/src/components/Lists/CardList/CardList.tsx
@@ -21,7 +21,7 @@ import * as React from 'react';
import { Animated, Dimensions, ViewStyle } from 'react-native';
import ImageListItem from './ImageListItem';
import CardListItem from './CardListItem';
-import type { ServiceItemType } from '../../../managers/ServicesManager';
+import { ServiceItemType } from '../../../utils/Services';
type PropsType = {
dataset: Array;
diff --git a/src/components/Lists/CardList/CardListItem.tsx b/src/components/Lists/CardList/CardListItem.tsx
index 155d868..ef48838 100644
--- a/src/components/Lists/CardList/CardListItem.tsx
+++ b/src/components/Lists/CardList/CardListItem.tsx
@@ -20,8 +20,8 @@
import * as React from 'react';
import { Caption, Card, Paragraph, TouchableRipple } from 'react-native-paper';
import { StyleSheet, View } from 'react-native';
-import type { ServiceItemType } from '../../../managers/ServicesManager';
import GENERAL_STYLES from '../../../constants/Styles';
+import { ServiceItemType } from '../../../utils/Services';
type PropsType = {
item: ServiceItemType;
diff --git a/src/components/Lists/CardList/ImageListItem.tsx b/src/components/Lists/CardList/ImageListItem.tsx
index 993b24c..dbb69f8 100644
--- a/src/components/Lists/CardList/ImageListItem.tsx
+++ b/src/components/Lists/CardList/ImageListItem.tsx
@@ -20,8 +20,8 @@
import * as React from 'react';
import { Text, TouchableRipple } from 'react-native-paper';
import { Image, StyleSheet, View } from 'react-native';
-import type { ServiceItemType } from '../../../managers/ServicesManager';
import GENERAL_STYLES from '../../../constants/Styles';
+import { ServiceItemType } from '../../../utils/Services';
type PropsType = {
item: ServiceItemType;
diff --git a/src/components/Lists/DashboardEdit/DashboardEditAccordion.tsx b/src/components/Lists/DashboardEdit/DashboardEditAccordion.tsx
index dc04a4d..41a1586 100644
--- a/src/components/Lists/DashboardEdit/DashboardEditAccordion.tsx
+++ b/src/components/Lists/DashboardEdit/DashboardEditAccordion.tsx
@@ -23,10 +23,7 @@ import { FlatList, Image, StyleSheet, View } from 'react-native';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import DashboardEditItem from './DashboardEditItem';
import AnimatedAccordion from '../../Animations/AnimatedAccordion';
-import type {
- ServiceCategoryType,
- ServiceItemType,
-} from '../../../managers/ServicesManager';
+import { ServiceCategoryType, ServiceItemType } from '../../../utils/Services';
type PropsType = {
item: ServiceCategoryType;
diff --git a/src/components/Lists/DashboardEdit/DashboardEditItem.tsx b/src/components/Lists/DashboardEdit/DashboardEditItem.tsx
index 265b381..f1f38a3 100644
--- a/src/components/Lists/DashboardEdit/DashboardEditItem.tsx
+++ b/src/components/Lists/DashboardEdit/DashboardEditItem.tsx
@@ -20,7 +20,7 @@
import * as React from 'react';
import { Image, StyleSheet } from 'react-native';
import { List, useTheme } from 'react-native-paper';
-import type { ServiceItemType } from '../../../managers/ServicesManager';
+import { ServiceItemType } from '../../../utils/Services';
type PropsType = {
item: ServiceItemType;
diff --git a/src/components/Mascot/MascotPopup.tsx b/src/components/Mascot/MascotPopup.tsx
index e909179..a3aa7e1 100644
--- a/src/components/Mascot/MascotPopup.tsx
+++ b/src/components/Mascot/MascotPopup.tsx
@@ -28,17 +28,17 @@ import {
View,
} from 'react-native';
import Mascot from './Mascot';
-import AsyncStorageManager from '../../managers/AsyncStorageManager';
import GENERAL_STYLES from '../../constants/Styles';
import MascotSpeechBubble, {
MascotSpeechBubbleProps,
} from './MascotSpeechBubble';
import { useMountEffect } from '../../utils/customHooks';
+import { useRoute } from '@react-navigation/core';
+import { useShouldShowMascot } from '../../context/preferencesContext';
type PropsType = MascotSpeechBubbleProps & {
emotion: number;
visible?: boolean;
- prefKey?: string;
};
const styles = StyleSheet.create({
@@ -61,13 +61,14 @@ const BUBBLE_HEIGHT = Dimensions.get('window').height / 3;
* Component used to display a popup with the mascot.
*/
function MascotPopup(props: PropsType) {
+ const route = useRoute();
+ const { shouldShow, setShouldShow } = useShouldShowMascot(route.name);
+
const isVisible = () => {
if (props.visible !== undefined) {
return props.visible;
- } else if (props.prefKey != null) {
- return AsyncStorageManager.getBool(props.prefKey);
} else {
- return false;
+ return shouldShow;
}
};
@@ -164,10 +165,8 @@ function MascotPopup(props: PropsType) {
};
const onDismiss = (callback?: () => void) => {
- if (props.prefKey != null) {
- AsyncStorageManager.set(props.prefKey, false);
- setDialogVisible(false);
- }
+ setShouldShow(false);
+ setDialogVisible(false);
if (callback) {
callback();
}
diff --git a/src/components/Overrides/CustomIntroSlider.tsx b/src/components/Overrides/CustomIntroSlider.tsx
index ca2ec57..7c59b5b 100644
--- a/src/components/Overrides/CustomIntroSlider.tsx
+++ b/src/components/Overrides/CustomIntroSlider.tsx
@@ -32,7 +32,6 @@ import LinearGradient from 'react-native-linear-gradient';
import * as Animatable from 'react-native-animatable';
import { Card } from 'react-native-paper';
import Update from '../../constants/Update';
-import ThemeManager from '../../managers/ThemeManager';
import Mascot, { MASCOT_STYLE } from '../Mascot/Mascot';
import MascotIntroWelcome from '../Intro/MascotIntroWelcome';
import IntroIcon from '../Intro/IconIntro';
@@ -289,9 +288,6 @@ export default class CustomIntroSlider extends React.Component<
onDone = () => {
const { props } = this;
- CustomIntroSlider.setStatusBarColor(
- ThemeManager.getCurrentTheme().colors.surface
- );
props.onDone();
};
diff --git a/src/components/Screens/PlanexWebview.tsx b/src/components/Screens/PlanexWebview.tsx
index 0925653..6614156 100644
--- a/src/components/Screens/PlanexWebview.tsx
+++ b/src/components/Screens/PlanexWebview.tsx
@@ -3,11 +3,11 @@ import { StyleSheet, View } from 'react-native';
import GENERAL_STYLES from '../../constants/Styles';
import Urls from '../../constants/Urls';
import DateManager from '../../managers/DateManager';
-import ThemeManager from '../../managers/ThemeManager';
import { PlanexGroupType } from '../../screens/Planex/GroupSelectionScreen';
import ErrorView from './ErrorView';
import WebViewScreen from './WebViewScreen';
import i18n from 'i18n-js';
+import { useTheme } from 'react-native-paper';
type Props = {
currentGroup?: PlanexGroupType;
@@ -86,7 +86,10 @@ const INJECT_STYLE_DARK = `$('head').append('')
*
* @param groupID The current group selected
*/
-const generateInjectedJS = (group: PlanexGroupType | undefined) => {
+const generateInjectedJS = (
+ group: PlanexGroupType | undefined,
+ darkMode: boolean
+) => {
let customInjectedJS = `$(document).ready(function() {
${OBSERVE_MUTATIONS_INJECTED}
${INJECT_STYLE}
@@ -97,7 +100,7 @@ const generateInjectedJS = (group: PlanexGroupType | undefined) => {
if (DateManager.isWeekend(new Date())) {
customInjectedJS += `calendar.next();`;
}
- if (ThemeManager.getNightMode()) {
+ if (darkMode) {
customInjectedJS += INJECT_STYLE_DARK;
}
customInjectedJS += 'removeAlpha();});true;'; // Prevents crash on ios
@@ -105,11 +108,12 @@ const generateInjectedJS = (group: PlanexGroupType | undefined) => {
};
function PlanexWebview(props: Props) {
+ const theme = useTheme();
return (
- ) => void;
- resetPreferences: () => void;
-};
-
-export const PreferencesContext = React.createContext({
- preferences: defaultPreferences,
- updatePreferences: () => undefined,
- resetPreferences: () => undefined,
-});
-
-export function usePreferences() {
- return useContext(PreferencesContext);
-}
diff --git a/src/context/preferencesContext.tsx b/src/context/preferencesContext.tsx
new file mode 100644
index 0000000..6fee694
--- /dev/null
+++ b/src/context/preferencesContext.tsx
@@ -0,0 +1,97 @@
+import { useNavigation } from '@react-navigation/core';
+import React, { useContext } from 'react';
+import { Appearance } from 'react-native-appearance';
+import {
+ defaultPreferences,
+ getPreferenceBool,
+ getPreferenceObject,
+ isValidPreferenceKey,
+ PreferenceKeys,
+ PreferencesType,
+} from '../utils/asyncStorage';
+import {
+ getAmicaleServices,
+ getINSAServices,
+ getSpecialServices,
+ getStudentServices,
+} from '../utils/Services';
+
+const colorScheme = Appearance.getColorScheme();
+
+export type PreferencesContextType = {
+ preferences: PreferencesType;
+ updatePreferences: (
+ key: PreferenceKeys,
+ value: number | string | boolean | object | Array
+ ) => void;
+ resetPreferences: () => void;
+};
+
+export const PreferencesContext = React.createContext({
+ preferences: defaultPreferences,
+ updatePreferences: () => undefined,
+ resetPreferences: () => undefined,
+});
+
+export function usePreferences() {
+ return useContext(PreferencesContext);
+}
+
+export function useShouldShowMascot(route: string) {
+ const { preferences, updatePreferences } = usePreferences();
+ const key = route + 'ShowMascot';
+ let shouldShow = false;
+ if (isValidPreferenceKey(key)) {
+ shouldShow = getPreferenceBool(key, preferences) !== false;
+ }
+
+ const setShouldShow = (show: boolean) => {
+ if (isValidPreferenceKey(key)) {
+ updatePreferences(key, show);
+ } else {
+ console.log('Invalid preference key: ' + key);
+ }
+ };
+
+ return { shouldShow, setShouldShow };
+}
+
+export function useDarkTheme() {
+ const { preferences } = usePreferences();
+ return (
+ (getPreferenceBool(PreferenceKeys.nightMode, preferences) !== false &&
+ (getPreferenceBool(PreferenceKeys.nightModeFollowSystem, preferences) ===
+ false ||
+ colorScheme === 'no-preference')) ||
+ (getPreferenceBool(PreferenceKeys.nightModeFollowSystem, preferences) !==
+ false &&
+ colorScheme === 'dark')
+ );
+}
+
+export function useCurrentDashboard() {
+ const { preferences, updatePreferences } = usePreferences();
+ const navigation = useNavigation();
+ const dashboardIdList = getPreferenceObject(
+ PreferenceKeys.dashboardItems,
+ preferences
+ ) as Array;
+
+ const updateCurrentDashboard = (newList: Array) => {
+ updatePreferences(PreferenceKeys.dashboardItems, newList);
+ };
+
+ const allDatasets = [
+ ...getAmicaleServices(navigation.navigate),
+ ...getStudentServices(navigation.navigate),
+ ...getINSAServices(navigation.navigate),
+ ...getSpecialServices(navigation.navigate),
+ ];
+ return {
+ currentDashboard: allDatasets.filter((item) =>
+ dashboardIdList.includes(item.key)
+ ),
+ currentDashboardIdList: dashboardIdList,
+ updateCurrentDashboard: updateCurrentDashboard,
+ };
+}
diff --git a/src/managers/AsyncStorageManager.ts b/src/managers/AsyncStorageManager.ts
deleted file mode 100644
index 8db73a0..0000000
--- a/src/managers/AsyncStorageManager.ts
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (c) 2019 - 2020 Arnaud Vergnet.
- *
- * This file is part of Campus INSAT.
- *
- * Campus INSAT is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Campus INSAT is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Campus INSAT. If not, see .
- */
-
-import AsyncStorage from '@react-native-async-storage/async-storage';
-import { SERVICES_KEY } from './ServicesManager';
-
-/**
- * Singleton used to manage preferences.
- * Preferences are fetched at the start of the app and saved in an instance object.
- * This allows for a synchronous access to saved data.
- */
-
-export default class AsyncStorageManager {
- static instance: AsyncStorageManager | null = null;
-
- static PREFERENCES: { [key: string]: { key: string; default: string } } = {
- debugUnlocked: {
- key: 'debugUnlocked',
- default: '0',
- },
- showIntro: {
- key: 'showIntro',
- default: '1',
- },
- updateNumber: {
- key: 'updateNumber',
- default: '0',
- },
- proxiwashNotifications: {
- key: 'proxiwashNotifications',
- default: '5',
- },
- nightModeFollowSystem: {
- key: 'nightModeFollowSystem',
- default: '1',
- },
- nightMode: {
- key: 'nightMode',
- default: '1',
- },
- defaultStartScreen: {
- key: 'defaultStartScreen',
- default: 'home',
- },
- servicesShowMascot: {
- key: 'servicesShowMascot',
- default: '1',
- },
- proxiwashShowMascot: {
- key: 'proxiwashShowMascot',
- default: '1',
- },
- homeShowMascot: {
- key: 'homeShowMascot',
- default: '1',
- },
- eventsShowMascot: {
- key: 'eventsShowMascot',
- default: '1',
- },
- planexShowMascot: {
- key: 'planexShowMascot',
- default: '1',
- },
- loginShowMascot: {
- key: 'loginShowMascot',
- default: '1',
- },
- voteShowMascot: {
- key: 'voteShowMascot',
- default: '1',
- },
- equipmentShowMascot: {
- key: 'equipmentShowMascot',
- default: '1',
- },
- gameStartMascot: {
- key: 'gameStartMascot',
- default: '1',
- },
- proxiwashWatchedMachines: {
- key: 'proxiwashWatchedMachines',
- default: '[]',
- },
- showAprilFoolsStart: {
- key: 'showAprilFoolsStart',
- default: '1',
- },
- planexCurrentGroup: {
- key: 'planexCurrentGroup',
- default: '',
- },
- planexFavoriteGroups: {
- key: 'planexFavoriteGroups',
- default: '[]',
- },
- dashboardItems: {
- key: 'dashboardItems',
- default: JSON.stringify([
- SERVICES_KEY.EMAIL,
- SERVICES_KEY.WASHERS,
- SERVICES_KEY.PROXIMO,
- SERVICES_KEY.TUTOR_INSA,
- SERVICES_KEY.RU,
- ]),
- },
- gameScores: {
- key: 'gameScores',
- default: '[]',
- },
- selectedWash: {
- key: 'selectedWash',
- default: 'washinsa',
- },
- };
-
- private currentPreferences: { [key: string]: string };
-
- constructor() {
- this.currentPreferences = {};
- }
-
- /**
- * Get this class instance or create one if none is found
- * @returns {AsyncStorageManager}
- */
- static getInstance(): AsyncStorageManager {
- if (AsyncStorageManager.instance == null) {
- AsyncStorageManager.instance = new AsyncStorageManager();
- }
- return AsyncStorageManager.instance;
- }
-
- /**
- * Saves the value associated to the given key to preferences.
- *
- * @param key
- * @param value
- */
- static set(
- key: string,
- value: number | string | boolean | object | Array
- ) {
- AsyncStorageManager.getInstance().setPreference(key, value);
- }
-
- /**
- * Gets the string value of the given preference
- *
- * @param key
- * @returns {string}
- */
- static getString(key: string): string {
- const value = AsyncStorageManager.getInstance().getPreference(key);
- return value != null ? value : '';
- }
-
- /**
- * Gets the boolean value of the given preference
- *
- * @param key
- * @returns {boolean}
- */
- static getBool(key: string): boolean {
- const value = AsyncStorageManager.getString(key);
- return value === '1' || value === 'true';
- }
-
- /**
- * Gets the number value of the given preference
- *
- * @param key
- * @returns {number}
- */
- static getNumber(key: string): number {
- return parseFloat(AsyncStorageManager.getString(key));
- }
-
- /**
- * Gets the object value of the given preference
- *
- * @param key
- * @returns {{...}}
- */
- static getObject(key: string): T {
- return JSON.parse(AsyncStorageManager.getString(key));
- }
-
- /**
- * Set preferences object current values from AsyncStorage.
- * This function should be called at the app's start.
- *
- * @return {Promise}
- */
- async loadPreferences() {
- return new Promise((resolve: (val: void) => void) => {
- const prefKeys: Array = [];
- // Get all available keys
- Object.keys(AsyncStorageManager.PREFERENCES).forEach((key: string) => {
- prefKeys.push(key);
- });
- // Get corresponding values
- AsyncStorage.multiGet(prefKeys).then((resultArray) => {
- // Save those values for later use
- resultArray.forEach((item: [string, string | null]) => {
- const key = item[0];
- let val = item[1];
- if (val === null) {
- val = AsyncStorageManager.PREFERENCES[key].default;
- }
- this.currentPreferences[key] = val;
- });
- resolve();
- });
- });
- }
-
- /**
- * Saves the value associated to the given key to preferences.
- * This updates the preferences object and saves it to AsyncStorage.
- *
- * @param key
- * @param value
- */
- setPreference(
- key: string,
- value: number | string | boolean | object | Array
- ) {
- if (AsyncStorageManager.PREFERENCES[key] != null) {
- let convertedValue;
- if (typeof value === 'string') {
- convertedValue = value;
- } else if (typeof value === 'boolean' || typeof value === 'number') {
- convertedValue = value.toString();
- } else {
- convertedValue = JSON.stringify(value);
- }
- this.currentPreferences[key] = convertedValue;
- AsyncStorage.setItem(key, convertedValue);
- }
- }
-
- /**
- * Gets the value at the given key.
- * If the key is not available, returns null
- *
- * @param key
- * @returns {string|null}
- */
- getPreference(key: string): string | null {
- return this.currentPreferences[key];
- }
-}
diff --git a/src/managers/DashboardManager.ts b/src/managers/DashboardManager.ts
deleted file mode 100644
index 34417c7..0000000
--- a/src/managers/DashboardManager.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2019 - 2020 Arnaud Vergnet.
- *
- * This file is part of Campus INSAT.
- *
- * Campus INSAT is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Campus INSAT is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Campus INSAT. If not, see .
- */
-
-import type { ServiceItemType } from './ServicesManager';
-import ServicesManager from './ServicesManager';
-import { getSublistWithIds } from '../utils/Services';
-import AsyncStorageManager from './AsyncStorageManager';
-
-export default class DashboardManager extends ServicesManager {
- getCurrentDashboard(): Array {
- const dashboardIdList = AsyncStorageManager.getObject>(
- AsyncStorageManager.PREFERENCES.dashboardItems.key
- );
- const allDatasets = [
- ...this.amicaleDataset,
- ...this.studentsDataset,
- ...this.insaDataset,
- ...this.specialDataset,
- ];
- return getSublistWithIds(dashboardIdList, allDatasets);
- }
-}
diff --git a/src/managers/ServicesManager.ts b/src/managers/ServicesManager.ts
deleted file mode 100644
index ff29d98..0000000
--- a/src/managers/ServicesManager.ts
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright (c) 2019 - 2020 Arnaud Vergnet.
- *
- * This file is part of Campus INSAT.
- *
- * Campus INSAT is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Campus INSAT is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Campus INSAT. If not, see .
- */
-
-import i18n from 'i18n-js';
-import { StackNavigationProp } from '@react-navigation/stack';
-import ConnectionManager from './ConnectionManager';
-import type { FullDashboardType } from '../screens/Home/HomeScreen';
-import getStrippedServicesList from '../utils/Services';
-import Urls from '../constants/Urls';
-
-const AMICALE_LOGO = require('../../assets/amicale.png');
-
-export const SERVICES_KEY = {
- CLUBS: 'clubs',
- PROFILE: 'profile',
- EQUIPMENT: 'equipment',
- AMICALE_WEBSITE: 'amicale_website',
- VOTE: 'vote',
- PROXIMO: 'proximo',
- WIKETUD: 'wiketud',
- ELUS_ETUDIANTS: 'elus_etudiants',
- TUTOR_INSA: 'tutor_insa',
- RU: 'ru',
- AVAILABLE_ROOMS: 'available_rooms',
- BIB: 'bib',
- EMAIL: 'email',
- ENT: 'ent',
- INSA_ACCOUNT: 'insa_account',
- WASHERS: 'washers',
- DRYERS: 'dryers',
-};
-
-export const SERVICES_CATEGORIES_KEY = {
- AMICALE: 'amicale',
- STUDENTS: 'students',
- INSA: 'insa',
- SPECIAL: 'special',
-};
-
-export type ServiceItemType = {
- key: string;
- title: string;
- subtitle: string;
- image: string | number;
- onPress: () => void;
- badgeFunction?: (dashboard: FullDashboardType) => number;
-};
-
-export type ServiceCategoryType = {
- key: string;
- title: string;
- subtitle: string;
- image: string | number;
- content: Array;
-};
-
-export default class ServicesManager {
- navigation: StackNavigationProp;
-
- amicaleDataset: Array;
-
- studentsDataset: Array;
-
- insaDataset: Array;
-
- specialDataset: Array;
-
- categoriesDataset: Array;
-
- constructor(nav: StackNavigationProp) {
- this.navigation = nav;
- this.amicaleDataset = [
- {
- key: SERVICES_KEY.CLUBS,
- title: i18n.t('screens.clubs.title'),
- subtitle: i18n.t('screens.services.descriptions.clubs'),
- image: Urls.images.clubs,
- onPress: (): void => this.onAmicaleServicePress('club-list'),
- },
- {
- key: SERVICES_KEY.PROFILE,
- title: i18n.t('screens.profile.title'),
- subtitle: i18n.t('screens.services.descriptions.profile'),
- image: Urls.images.profile,
- onPress: (): void => this.onAmicaleServicePress('profile'),
- },
- {
- key: SERVICES_KEY.EQUIPMENT,
- title: i18n.t('screens.equipment.title'),
- subtitle: i18n.t('screens.services.descriptions.equipment'),
- image: Urls.images.equipment,
- onPress: (): void => this.onAmicaleServicePress('equipment-list'),
- },
- {
- key: SERVICES_KEY.AMICALE_WEBSITE,
- title: i18n.t('screens.websites.amicale'),
- subtitle: i18n.t('screens.services.descriptions.amicaleWebsite'),
- image: Urls.images.amicale,
- onPress: (): void =>
- nav.navigate('website', {
- host: Urls.websites.amicale,
- title: i18n.t('screens.websites.amicale'),
- }),
- },
- {
- key: SERVICES_KEY.VOTE,
- title: i18n.t('screens.vote.title'),
- subtitle: i18n.t('screens.services.descriptions.vote'),
- image: Urls.images.vote,
- onPress: (): void => this.onAmicaleServicePress('vote'),
- },
- ];
- this.studentsDataset = [
- {
- key: SERVICES_KEY.PROXIMO,
- title: i18n.t('screens.proximo.title'),
- subtitle: i18n.t('screens.services.descriptions.proximo'),
- image: Urls.images.proximo,
- onPress: (): void => nav.navigate('proximo'),
- badgeFunction: (dashboard: FullDashboardType): number =>
- dashboard.proximo_articles,
- },
- {
- key: SERVICES_KEY.WIKETUD,
- title: 'Wiketud',
- subtitle: i18n.t('screens.services.descriptions.wiketud'),
- image: Urls.images.wiketud,
- onPress: (): void =>
- nav.navigate('website', {
- host: Urls.websites.wiketud,
- title: 'Wiketud',
- }),
- },
- {
- key: SERVICES_KEY.ELUS_ETUDIANTS,
- title: 'Élus Étudiants',
- subtitle: i18n.t('screens.services.descriptions.elusEtudiants'),
- image: Urls.images.elusEtudiants,
- onPress: (): void =>
- nav.navigate('website', {
- host: Urls.websites.elusEtudiants,
- title: 'Élus Étudiants',
- }),
- },
- {
- key: SERVICES_KEY.TUTOR_INSA,
- title: "Tutor'INSA",
- subtitle: i18n.t('screens.services.descriptions.tutorInsa'),
- image: Urls.images.tutorInsa,
- onPress: (): void =>
- nav.navigate('website', {
- host: Urls.websites.tutorInsa,
- title: "Tutor'INSA",
- }),
- badgeFunction: (dashboard: FullDashboardType): number =>
- dashboard.available_tutorials,
- },
- ];
- this.insaDataset = [
- {
- key: SERVICES_KEY.RU,
- title: i18n.t('screens.menu.title'),
- subtitle: i18n.t('screens.services.descriptions.self'),
- image: Urls.images.menu,
- onPress: (): void => nav.navigate('self-menu'),
- badgeFunction: (dashboard: FullDashboardType): number =>
- dashboard.today_menu.length,
- },
- {
- key: SERVICES_KEY.AVAILABLE_ROOMS,
- title: i18n.t('screens.websites.rooms'),
- subtitle: i18n.t('screens.services.descriptions.availableRooms'),
- image: Urls.images.availableRooms,
- onPress: (): void =>
- nav.navigate('website', {
- host: Urls.websites.availableRooms,
- title: i18n.t('screens.websites.rooms'),
- }),
- },
- {
- key: SERVICES_KEY.BIB,
- title: i18n.t('screens.websites.bib'),
- subtitle: i18n.t('screens.services.descriptions.bib'),
- image: Urls.images.bib,
- onPress: (): void =>
- nav.navigate('website', {
- host: Urls.websites.bib,
- title: i18n.t('screens.websites.bib'),
- }),
- },
- {
- key: SERVICES_KEY.EMAIL,
- title: i18n.t('screens.websites.mails'),
- subtitle: i18n.t('screens.services.descriptions.mails'),
- image: Urls.images.bluemind,
- onPress: (): void =>
- nav.navigate('website', {
- host: Urls.websites.bluemind,
- title: i18n.t('screens.websites.mails'),
- }),
- },
- {
- key: SERVICES_KEY.ENT,
- title: i18n.t('screens.websites.ent'),
- subtitle: i18n.t('screens.services.descriptions.ent'),
- image: Urls.images.ent,
- onPress: (): void =>
- nav.navigate('website', {
- host: Urls.websites.ent,
- title: i18n.t('screens.websites.ent'),
- }),
- },
- {
- key: SERVICES_KEY.INSA_ACCOUNT,
- title: i18n.t('screens.insaAccount.title'),
- subtitle: i18n.t('screens.services.descriptions.insaAccount'),
- image: Urls.images.insaAccount,
- onPress: (): void =>
- nav.navigate('website', {
- host: Urls.websites.insaAccount,
- title: i18n.t('screens.insaAccount.title'),
- }),
- },
- ];
- this.specialDataset = [
- {
- key: SERVICES_KEY.WASHERS,
- title: i18n.t('screens.proxiwash.washers'),
- subtitle: i18n.t('screens.services.descriptions.washers'),
- image: Urls.images.washer,
- onPress: (): void => nav.navigate('proxiwash'),
- badgeFunction: (dashboard: FullDashboardType): number =>
- dashboard.available_washers,
- },
- {
- key: SERVICES_KEY.DRYERS,
- title: i18n.t('screens.proxiwash.dryers'),
- subtitle: i18n.t('screens.services.descriptions.washers'),
- image: Urls.images.dryer,
- onPress: (): void => nav.navigate('proxiwash'),
- badgeFunction: (dashboard: FullDashboardType): number =>
- dashboard.available_dryers,
- },
- ];
- this.categoriesDataset = [
- {
- key: SERVICES_CATEGORIES_KEY.AMICALE,
- title: i18n.t('screens.services.categories.amicale'),
- subtitle: i18n.t('screens.services.more'),
- image: AMICALE_LOGO,
- content: this.amicaleDataset,
- },
- {
- key: SERVICES_CATEGORIES_KEY.STUDENTS,
- title: i18n.t('screens.services.categories.students'),
- subtitle: i18n.t('screens.services.more'),
- image: 'account-group',
- content: this.studentsDataset,
- },
- {
- key: SERVICES_CATEGORIES_KEY.INSA,
- title: i18n.t('screens.services.categories.insa'),
- subtitle: i18n.t('screens.services.more'),
- image: 'school',
- content: this.insaDataset,
- },
- {
- key: SERVICES_CATEGORIES_KEY.SPECIAL,
- title: i18n.t('screens.services.categories.special'),
- subtitle: i18n.t('screens.services.categories.special'),
- image: 'star',
- content: this.specialDataset,
- },
- ];
- }
-
- /**
- * Redirects the user to the login screen if he is not logged in
- *
- * @param route
- * @returns {null}
- */
- onAmicaleServicePress(route: string) {
- if (ConnectionManager.getInstance().isLoggedIn()) {
- this.navigation.navigate(route);
- } else {
- this.navigation.navigate('login', { nextScreen: route });
- }
- }
-
- /**
- * Gets the list of amicale's services
- *
- * @param excludedItems Ids of items to exclude from the returned list
- * @returns {Array}
- */
- getAmicaleServices(excludedItems?: Array): Array {
- if (excludedItems != null) {
- return getStrippedServicesList(excludedItems, this.amicaleDataset);
- }
- return this.amicaleDataset;
- }
-
- /**
- * Gets the list of students' services
- *
- * @param excludedItems Ids of items to exclude from the returned list
- * @returns {Array}
- */
- getStudentServices(excludedItems?: Array): Array {
- if (excludedItems != null) {
- return getStrippedServicesList(excludedItems, this.studentsDataset);
- }
- return this.studentsDataset;
- }
-
- /**
- * Gets the list of INSA's services
- *
- * @param excludedItems Ids of items to exclude from the returned list
- * @returns {Array}
- */
- getINSAServices(excludedItems?: Array): Array {
- if (excludedItems != null) {
- return getStrippedServicesList(excludedItems, this.insaDataset);
- }
- return this.insaDataset;
- }
-
- /**
- * Gets the list of special services
- *
- * @param excludedItems Ids of items to exclude from the returned list
- * @returns {Array}
- */
- getSpecialServices(excludedItems?: Array): Array {
- if (excludedItems != null) {
- return getStrippedServicesList(excludedItems, this.specialDataset);
- }
- return this.specialDataset;
- }
-
- /**
- * Gets all services sorted by category
- *
- * @param excludedItems Ids of categories to exclude from the returned list
- * @returns {Array}
- */
- getCategories(excludedItems?: Array): Array {
- if (excludedItems != null) {
- return getStrippedServicesList(excludedItems, this.categoriesDataset);
- }
- return this.categoriesDataset;
- }
-}
diff --git a/src/navigation/MainNavigator.tsx b/src/navigation/MainNavigator.tsx
index 9bfcd5b..7f9938d 100644
--- a/src/navigation/MainNavigator.tsx
+++ b/src/navigation/MainNavigator.tsx
@@ -46,16 +46,20 @@ import EquipmentConfirmScreen from '../screens/Amicale/Equipment/EquipmentConfir
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 IntroScreen from '../screens/Intro/IntroScreen';
export enum MainRoutes {
Main = 'main',
+ Intro = 'Intro',
Gallery = 'gallery',
Settings = 'settings',
DashboardEdit = 'dashboard-edit',
About = 'about',
Dependencies = 'dependencies',
Debug = 'debug',
- GameStart = 'game-start',
+ GameStart = 'game',
GameMain = 'game-main',
Login = 'login',
SelfMenu = 'self-menu',
@@ -66,11 +70,12 @@ export enum MainRoutes {
ClubList = 'club-list',
ClubInformation = 'club-information',
ClubAbout = 'club-about',
- EquipmentList = 'equipment-list',
+ EquipmentList = 'equipment',
EquipmentRent = 'equipment-rent',
EquipmentConfirm = 'equipment-confirm',
Vote = 'vote',
Feedback = 'feedback',
+ Website = 'website',
}
type DefaultParams = { [key in MainRoutes]: object | undefined };
@@ -96,13 +101,31 @@ export type MainStackParamsList = FullParamsList &
const MainStack = createStackNavigator();
+function getIntroScreens() {
+ return (
+ <>
+
+ >
+ );
+}
+
function MainStackComponent(props: {
+ showIntro: boolean;
createTabNavigator: () => React.ReactElement;
}) {
- const { createTabNavigator } = props;
+ const { showIntro, createTabNavigator } = props;
+ if (showIntro) {
+ return getIntroScreens();
+ }
return (
}
/>
);
diff --git a/src/navigation/TabNavigator.tsx b/src/navigation/TabNavigator.tsx
index 437a380..c5cc56e 100644
--- a/src/navigation/TabNavigator.tsx
+++ b/src/navigation/TabNavigator.tsx
@@ -31,7 +31,6 @@ import PlanningDisplayScreen from '../screens/Planning/PlanningDisplayScreen';
import ProxiwashScreen from '../screens/Proxiwash/ProxiwashScreen';
import ProxiwashAboutScreen from '../screens/Proxiwash/ProxiwashAboutScreen';
import PlanexScreen from '../screens/Planex/PlanexScreen';
-import AsyncStorageManager from '../managers/AsyncStorageManager';
import ClubDisplayScreen from '../screens/Amicale/Clubs/ClubDisplayScreen';
import ScannerScreen from '../screens/Home/ScannerScreen';
import FeedItemScreen from '../screens/Home/FeedItemScreen';
@@ -41,6 +40,8 @@ import WebsitesHomeScreen from '../screens/Services/ServicesScreen';
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';
const styles = StyleSheet.create({
header: {
@@ -56,6 +57,20 @@ const styles = StyleSheet.create({
},
});
+type DefaultParams = { [key in TabRoutes]: object | undefined };
+
+export type FullParamsList = DefaultParams & {
+ [TabRoutes.Home]: {
+ nextScreen: string;
+ data: Record;
+ };
+};
+
+// Don't know why but TS is complaining without this
+// See: https://stackoverflow.com/questions/63652687/interface-does-not-satisfy-the-constraint-recordstring-object-undefined
+export type TabStackParamsList = FullParamsList &
+ Record;
+
const ServicesStack = createStackNavigator();
function ServicesStackComponent() {
@@ -214,7 +229,7 @@ function PlanexStackComponent() {
);
}
-const Tab = createBottomTabNavigator();
+const Tab = createBottomTabNavigator();
type PropsType = {
defaultHomeRoute: string | null;
@@ -249,65 +264,70 @@ const ICONS: {
},
};
-export default class TabNavigator extends React.Component {
- defaultRoute: string;
- createHomeStackComponent: () => any;
-
- constructor(props: PropsType) {
- super(props);
- this.defaultRoute = 'home';
- if (!props.defaultHomeRoute) {
- this.defaultRoute = AsyncStorageManager.getString(
- AsyncStorageManager.PREFERENCES.defaultStartScreen.key
- ).toLowerCase();
- }
- this.createHomeStackComponent = () =>
- HomeStackComponent(props.defaultHomeRoute, props.defaultHomeData);
+export default function TabNavigator(props: PropsType) {
+ const { preferences } = usePreferences();
+ let defaultRoute = getPreferenceString(
+ PreferenceKeys.defaultStartScreen,
+ preferences
+ );
+ if (!defaultRoute) {
+ defaultRoute = 'home';
+ } else {
+ defaultRoute = defaultRoute.toLowerCase();
}
- render() {
- const LABELS: {
- [key: string]: string;
- } = {
- services: i18n.t('screens.services.title'),
- proxiwash: i18n.t('screens.proxiwash.title'),
- home: i18n.t('screens.home.title'),
- planning: i18n.t('screens.planning.title'),
- planex: i18n.t('screens.planex.title'),
- };
- return (
- (
-
- )}
- >
-
-
-
-
-
-
- );
- }
+ const createHomeStackComponent = () =>
+ HomeStackComponent(props.defaultHomeRoute, props.defaultHomeData);
+
+ const LABELS: {
+ [key: string]: string;
+ } = {
+ services: i18n.t('screens.services.title'),
+ proxiwash: i18n.t('screens.proxiwash.title'),
+ home: i18n.t('screens.home.title'),
+ planning: i18n.t('screens.planning.title'),
+ planex: i18n.t('screens.planex.title'),
+ };
+ return (
+ (
+
+ )}
+ >
+
+
+
+
+
+
+ );
+}
+
+export enum TabRoutes {
+ Services = 'services',
+ Proxiwash = 'proxiwash',
+ Home = 'home',
+ Planning = 'events',
+ Planex = 'planex',
}
diff --git a/src/screens/About/DebugScreen.tsx b/src/screens/About/DebugScreen.tsx
index c66a7a4..10ce98c 100644
--- a/src/screens/About/DebugScreen.tsx
+++ b/src/screens/About/DebugScreen.tsx
@@ -17,7 +17,7 @@
* along with Campus INSAT. If not, see .
*/
-import * as React from 'react';
+import React, { useRef, useState } from 'react';
import { StyleSheet, View } from 'react-native';
import {
Button,
@@ -25,12 +25,17 @@ import {
Subheading,
TextInput,
Title,
- withTheme,
+ useTheme,
} from 'react-native-paper';
import { Modalize } from 'react-native-modalize';
import CustomModal from '../../components/Overrides/CustomModal';
-import AsyncStorageManager from '../../managers/AsyncStorageManager';
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
+import { usePreferences } from '../../context/preferencesContext';
+import {
+ defaultPreferences,
+ isValidPreferenceKey,
+ PreferenceKeys,
+} from '../../utils/asyncStorage';
type PreferenceItemType = {
key: string;
@@ -38,15 +43,6 @@ type PreferenceItemType = {
current: string;
};
-type PropsType = {
- theme: ReactNativePaper.Theme;
-};
-
-type StateType = {
- modalCurrentDisplayItem: PreferenceItemType | null;
- currentPreferences: Array;
-};
-
const styles = StyleSheet.create({
container: {
flex: 1,
@@ -62,47 +58,35 @@ const styles = StyleSheet.create({
* Class defining the Debug screen.
* This screen allows the user to get and modify information on the app/device.
*/
-class DebugScreen extends React.Component {
- modalRef: { current: Modalize | null };
+function DebugScreen() {
+ const theme = useTheme();
+ const { preferences, updatePreferences } = usePreferences();
+ const modalRef = useRef(null);
- modalInputValue: string;
+ const [modalInputValue, setModalInputValue] = useState('');
+ const [
+ modalCurrentDisplayItem,
+ setModalCurrentDisplayItem,
+ ] = useState(null);
- /**
- * Copies user preferences to state for easier manipulation
- *
- * @param props
- */
- constructor(props: PropsType) {
- super(props);
- this.modalRef = React.createRef();
- this.modalInputValue = '';
- const currentPreferences: Array = [];
- Object.values(AsyncStorageManager.PREFERENCES).forEach((object: any) => {
- const newObject: PreferenceItemType = { ...object };
- newObject.current = AsyncStorageManager.getString(newObject.key);
- currentPreferences.push(newObject);
- });
- this.state = {
- modalCurrentDisplayItem: null,
- currentPreferences,
+ const currentPreferences: Array = [];
+ Object.values(PreferenceKeys).forEach((key) => {
+ const newObject: PreferenceItemType = {
+ key: key,
+ current: preferences[key],
+ default: defaultPreferences[key],
};
- }
+ currentPreferences.push(newObject);
+ });
- /**
- * Gets the edit modal content
- *
- * @return {*}
- */
- getModalContent() {
- const { props, state } = this;
+ const getModalContent = () => {
let key = '';
let defaultValue = '';
let current = '';
- if (state.modalCurrentDisplayItem) {
- key = state.modalCurrentDisplayItem.key;
- defaultValue = state.modalCurrentDisplayItem.default;
- defaultValue = state.modalCurrentDisplayItem.default;
- current = state.modalCurrentDisplayItem.current;
+ if (modalCurrentDisplayItem) {
+ key = modalCurrentDisplayItem.key;
+ defaultValue = modalCurrentDisplayItem.default;
+ current = modalCurrentDisplayItem.current;
}
return (
@@ -110,19 +94,14 @@ class DebugScreen extends React.Component {
{key}
Default: {defaultValue}
Current: {current}
- {
- this.modalInputValue = text;
- }}
- />
+
);
- }
+ };
- getRenderItem = ({ item }: { item: PreferenceItemType }) => {
+ const getRenderItem = ({ item }: { item: PreferenceItemType }) => {
return (
{
- this.showEditModal(item);
+ showEditModal(item);
}}
/>
);
};
- /**
- * Shows the edit modal
- *
- * @param item
- */
- showEditModal(item: PreferenceItemType) {
- this.setState({
- modalCurrentDisplayItem: item,
- });
- if (this.modalRef.current) {
- this.modalRef.current.open();
+ const showEditModal = (item: PreferenceItemType) => {
+ setModalCurrentDisplayItem(item);
+ if (modalRef.current) {
+ modalRef.current.open();
}
- }
+ };
- /**
- * Finds the index of the given key in the preferences array
- *
- * @param key THe key to find the index of
- * @returns {number}
- */
- findIndexOfKey(key: string): number {
- const { currentPreferences } = this.state;
- let index = -1;
- for (let i = 0; i < currentPreferences.length; i += 1) {
- if (currentPreferences[i].key === key) {
- index = i;
- break;
- }
+ const saveNewPrefs = (key: string, value: string) => {
+ if (isValidPreferenceKey(key)) {
+ updatePreferences(key, value);
}
- return index;
- }
-
- /**
- * Saves the new value of the given preference
- *
- * @param key The pref key
- * @param value The pref value
- */
- saveNewPrefs(key: string, value: string) {
- this.setState((prevState: StateType): {
- currentPreferences: Array;
- } => {
- const currentPreferences = [...prevState.currentPreferences];
- currentPreferences[this.findIndexOfKey(key)].current = value;
- return { currentPreferences };
- });
- AsyncStorageManager.set(key, value);
- if (this.modalRef.current) {
- this.modalRef.current.close();
+ if (modalRef.current) {
+ modalRef.current.close();
}
- }
+ };
- render() {
- const { state } = this;
- return (
-
- {this.getModalContent()}
-
-
- );
- }
+ return (
+
+ {getModalContent()}
+
+
+ );
}
-export default withTheme(DebugScreen);
+export default DebugScreen;
diff --git a/src/screens/Amicale/Equipment/EquipmentListScreen.tsx b/src/screens/Amicale/Equipment/EquipmentListScreen.tsx
index 99ba202..8dfe8af 100644
--- a/src/screens/Amicale/Equipment/EquipmentListScreen.tsx
+++ b/src/screens/Amicale/Equipment/EquipmentListScreen.tsx
@@ -25,7 +25,6 @@ import i18n from 'i18n-js';
import EquipmentListItem from '../../../components/Lists/Equipment/EquipmentListItem';
import MascotPopup from '../../../components/Mascot/MascotPopup';
import { MASCOT_STYLE } from '../../../components/Mascot/Mascot';
-import AsyncStorageManager from '../../../managers/AsyncStorageManager';
import GENERAL_STYLES from '../../../constants/Styles';
import ConnectionManager from '../../../managers/ConnectionManager';
import { ApiRejectType } from '../../../utils/WebData';
@@ -36,7 +35,7 @@ type PropsType = {
};
type StateType = {
- mascotDialogVisible: boolean;
+ mascotDialogVisible: boolean | undefined;
};
export type DeviceType = {
@@ -75,9 +74,7 @@ class EquipmentListScreen extends React.Component {
super(props);
this.userRents = null;
this.state = {
- mascotDialogVisible: AsyncStorageManager.getBool(
- AsyncStorageManager.PREFERENCES.equipmentShowMascot.key
- ),
+ mascotDialogVisible: undefined,
};
}
@@ -145,10 +142,6 @@ class EquipmentListScreen extends React.Component {
};
hideMascotDialog = () => {
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.equipmentShowMascot.key,
- false
- );
this.setState({ mascotDialogVisible: false });
};
diff --git a/src/screens/Amicale/LoginScreen.tsx b/src/screens/Amicale/LoginScreen.tsx
index aec0509..8bf3185 100644
--- a/src/screens/Amicale/LoginScreen.tsx
+++ b/src/screens/Amicale/LoginScreen.tsx
@@ -31,7 +31,6 @@ import { StackNavigationProp, StackScreenProps } from '@react-navigation/stack';
import LinearGradient from 'react-native-linear-gradient';
import ConnectionManager from '../../managers/ConnectionManager';
import ErrorDialog from '../../components/Dialogs/ErrorDialog';
-import AsyncStorageManager from '../../managers/AsyncStorageManager';
import { MASCOT_STYLE } from '../../components/Mascot/Mascot';
import MascotPopup from '../../components/Mascot/MascotPopup';
import CollapsibleScrollView from '../../components/Collapsible/CollapsibleScrollView';
@@ -56,7 +55,7 @@ type StateType = {
loading: boolean;
dialogVisible: boolean;
dialogError: ApiRejectType;
- mascotDialogVisible: boolean;
+ mascotDialogVisible: boolean | undefined;
};
const ICON_AMICALE = require('../../../assets/amicale.png');
@@ -118,9 +117,7 @@ class LoginScreen extends React.Component {
loading: false,
dialogVisible: false,
dialogError: { status: REQUEST_STATUS.SUCCESS },
- mascotDialogVisible: AsyncStorageManager.getBool(
- AsyncStorageManager.PREFERENCES.loginShowMascot.key
- ),
+ mascotDialogVisible: undefined,
};
}
@@ -321,10 +318,6 @@ class LoginScreen extends React.Component {
};
hideMascotDialog = () => {
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.loginShowMascot.key,
- false
- );
this.setState({ mascotDialogVisible: false });
};
@@ -357,10 +350,11 @@ class LoginScreen extends React.Component {
handleSuccess = () => {
const { navigation } = this.props;
// Do not show the home login banner again
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.homeShowMascot.key,
- false
- );
+ // TODO
+ // AsyncStorageManager.set(
+ // AsyncStorageManager.PREFERENCES.homeShowMascot.key,
+ // false
+ // );
if (this.nextScreen == null) {
navigation.goBack();
} else {
diff --git a/src/screens/Amicale/ProfileScreen.tsx b/src/screens/Amicale/ProfileScreen.tsx
index c329767..ed7004d 100644
--- a/src/screens/Amicale/ProfileScreen.tsx
+++ b/src/screens/Amicale/ProfileScreen.tsx
@@ -36,13 +36,16 @@ import MaterialHeaderButtons, {
} from '../../components/Overrides/CustomHeaderButton';
import CardList from '../../components/Lists/CardList/CardList';
import Mascot, { MASCOT_STYLE } from '../../components/Mascot/Mascot';
-import ServicesManager, { SERVICES_KEY } from '../../managers/ServicesManager';
import CollapsibleFlatList from '../../components/Collapsible/CollapsibleFlatList';
-import type { ServiceItemType } from '../../managers/ServicesManager';
import GENERAL_STYLES from '../../constants/Styles';
import Urls from '../../constants/Urls';
import RequestScreen from '../../components/Screens/RequestScreen';
import ConnectionManager from '../../managers/ConnectionManager';
+import {
+ getAmicaleServices,
+ ServiceItemType,
+ SERVICES_KEY,
+} from '../../utils/Services';
type PropsType = {
navigation: StackNavigationProp;
@@ -100,8 +103,9 @@ class ProfileScreen extends React.Component {
super(props);
this.data = undefined;
this.flatListData = [{ id: '0' }, { id: '1' }, { id: '2' }, { id: '3' }];
- const services = new ServicesManager(props.navigation);
- this.amicaleDataset = services.getAmicaleServices([SERVICES_KEY.PROFILE]);
+ this.amicaleDataset = getAmicaleServices(props.navigation.navigate, [
+ SERVICES_KEY.PROFILE,
+ ]);
this.state = {
dialogVisible: false,
};
diff --git a/src/screens/Amicale/VoteScreen.tsx b/src/screens/Amicale/VoteScreen.tsx
index 5e546e2..8309911 100644
--- a/src/screens/Amicale/VoteScreen.tsx
+++ b/src/screens/Amicale/VoteScreen.tsx
@@ -28,7 +28,6 @@ import VoteResults from '../../components/Amicale/Vote/VoteResults';
import VoteWait from '../../components/Amicale/Vote/VoteWait';
import { MASCOT_STYLE } from '../../components/Mascot/Mascot';
import MascotPopup from '../../components/Mascot/MascotPopup';
-import AsyncStorageManager from '../../managers/AsyncStorageManager';
import VoteNotAvailable from '../../components/Amicale/Vote/VoteNotAvailable';
import GENERAL_STYLES from '../../constants/Styles';
import ConnectionManager from '../../managers/ConnectionManager';
@@ -118,7 +117,7 @@ type PropsType = {};
type StateType = {
hasVoted: boolean;
- mascotDialogVisible: boolean;
+ mascotDialogVisible: boolean | undefined;
};
const styles = StyleSheet.create({
@@ -154,9 +153,7 @@ export default class VoteScreen extends React.Component {
this.dates = undefined;
this.state = {
hasVoted: false,
- mascotDialogVisible: AsyncStorageManager.getBool(
- AsyncStorageManager.PREFERENCES.voteShowMascot.key
- ),
+ mascotDialogVisible: undefined,
};
this.hasVoted = false;
this.today = new Date();
@@ -328,10 +325,6 @@ export default class VoteScreen extends React.Component {
};
hideMascotDialog = () => {
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.voteShowMascot.key,
- false
- );
this.setState({ mascotDialogVisible: false });
};
diff --git a/src/screens/Game/screens/GameMainScreen.tsx b/src/screens/Game/screens/GameMainScreen.tsx
index 5ef7f23..93b51d0 100644
--- a/src/screens/Game/screens/GameMainScreen.tsx
+++ b/src/screens/Game/screens/GameMainScreen.tsx
@@ -33,6 +33,7 @@ import MaterialHeaderButtons, {
import type { OptionsDialogButtonType } from '../../../components/Dialogs/OptionsDialog';
import OptionsDialog from '../../../components/Dialogs/OptionsDialog';
import GENERAL_STYLES from '../../../constants/Styles';
+import { MainRoutes } from '../../../navigation/MainNavigator';
type PropsType = {
navigation: StackNavigationProp;
@@ -200,7 +201,7 @@ class GameMainScreen extends React.Component {
gameScore: score,
});
if (!isRestart) {
- props.navigation.replace('game-start', {
+ props.navigation.replace(MainRoutes.GameStart, {
score: state.gameScore,
level: state.gameLevel,
time: state.gameTime,
diff --git a/src/screens/Game/screens/GameStartScreen.tsx b/src/screens/Game/screens/GameStartScreen.tsx
index 7ac4e0d..0067d37 100644
--- a/src/screens/Game/screens/GameStartScreen.tsx
+++ b/src/screens/Game/screens/GameStartScreen.tsx
@@ -35,7 +35,6 @@ import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityI
import LinearGradient from 'react-native-linear-gradient';
import Mascot, { MASCOT_STYLE } from '../../../components/Mascot/Mascot';
import MascotPopup from '../../../components/Mascot/MascotPopup';
-import AsyncStorageManager from '../../../managers/AsyncStorageManager';
import type { GridType } from '../components/GridComponent';
import GridComponent from '../components/GridComponent';
import GridManager from '../logic/GridManager';
@@ -152,9 +151,11 @@ class GameStartScreen extends React.Component {
super(props);
this.isHighScore = false;
this.gridManager = new GridManager(4, 4, props.theme);
- this.scores = AsyncStorageManager.getObject(
- AsyncStorageManager.PREFERENCES.gameScores.key
- );
+ // TODO
+ // this.scores = AsyncStorageManager.getObject(
+ // AsyncStorageManager.PREFERENCES.gameScores.key
+ // );
+ this.scores = [];
this.scores.sort((a: number, b: number): number => b - a);
if (props.route.params != null) {
this.recoverGameScore();
@@ -448,10 +449,11 @@ class GameStartScreen extends React.Component {
if (this.scores.length > 3) {
this.scores.splice(3, 1);
}
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.gameScores.key,
- this.scores
- );
+ // TODO
+ // AsyncStorageManager.set(
+ // AsyncStorageManager.PREFERENCES.gameScores.key,
+ // this.scores
+ // );
}
}
@@ -472,7 +474,6 @@ class GameStartScreen extends React.Component {
{this.getMainContent()}
.
*/
-import * as React from 'react';
+import React, { useLayoutEffect, useRef, useState } from 'react';
import {
FlatList,
NativeScrollEvent,
@@ -26,9 +26,13 @@ import {
StyleSheet,
} from 'react-native';
import i18n from 'i18n-js';
-import { Headline, withTheme } from 'react-native-paper';
-import { CommonActions } from '@react-navigation/native';
-import { StackNavigationProp } from '@react-navigation/stack';
+import { Headline, useTheme } from 'react-native-paper';
+import {
+ CommonActions,
+ useFocusEffect,
+ useNavigation,
+} from '@react-navigation/native';
+import { StackScreenProps } from '@react-navigation/stack';
import * as Animatable from 'react-native-animatable';
import { View } from 'react-native-animatable';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
@@ -44,16 +48,17 @@ import MaterialHeaderButtons, {
import AnimatedFAB from '../../components/Animations/AnimatedFAB';
import ConnectionManager from '../../managers/ConnectionManager';
import LogoutDialog from '../../components/Amicale/LogoutDialog';
-import AsyncStorageManager from '../../managers/AsyncStorageManager';
import { MASCOT_STYLE } from '../../components/Mascot/Mascot';
import MascotPopup from '../../components/Mascot/MascotPopup';
-import DashboardManager from '../../managers/DashboardManager';
-import type { ServiceItemType } from '../../managers/ServicesManager';
import { getDisplayEvent, getFutureEvents } from '../../utils/Home';
import type { PlanningEventType } from '../../utils/Planning';
import GENERAL_STYLES from '../../constants/Styles';
import Urls from '../../constants/Urls';
import { readData } from '../../utils/WebData';
+import { TabRoutes, TabStackParamsList } from '../../navigation/TabNavigator';
+import { ServiceItemType } from '../../utils/Services';
+import { useCurrentDashboard } from '../../context/preferencesContext';
+import { MainRoutes } from '../../navigation/MainNavigator';
const FEED_ITEM_HEIGHT = 500;
@@ -88,15 +93,7 @@ type RawDashboardType = {
dashboard: FullDashboardType;
};
-type PropsType = {
- navigation: StackNavigationProp;
- route: { params: { nextScreen: string; data: object } };
- theme: ReactNativePaper.Theme;
-};
-
-type StateType = {
- dialogVisible: boolean;
-};
+type Props = StackScreenProps;
const styles = StyleSheet.create({
dashboardRow: {
@@ -127,106 +124,94 @@ const styles = StyleSheet.create({
},
});
+const sortFeedTime = (a: FeedItemType, b: FeedItemType): number =>
+ b.time - a.time;
+
+const generateNewsFeed = (rawFeed: RawNewsFeedType): Array => {
+ const finalFeed: Array = [];
+ Object.keys(rawFeed).forEach((key: string) => {
+ const category: Array | null = rawFeed[key];
+ if (category != null && category.length > 0) {
+ finalFeed.push(...category);
+ }
+ });
+ finalFeed.sort(sortFeedTime);
+ return finalFeed;
+};
+
/**
* Class defining the app's home screen
*/
-class HomeScreen extends React.Component {
- static sortFeedTime = (a: FeedItemType, b: FeedItemType): number =>
- b.time - a.time;
+function HomeScreen(props: Props) {
+ const theme = useTheme();
+ const navigation = useNavigation();
- static generateNewsFeed(rawFeed: RawNewsFeedType): Array {
- const finalFeed: Array = [];
- Object.keys(rawFeed).forEach((key: string) => {
- const category: Array | null = rawFeed[key];
- if (category != null && category.length > 0) {
- finalFeed.push(...category);
+ const [dialogVisible, setDialogVisible] = useState(false);
+ const fabRef = useRef(null);
+
+ const [isLoggedIn, setIsLoggedIn] = useState(
+ ConnectionManager.getInstance().isLoggedIn()
+ );
+ const { currentDashboard } = useCurrentDashboard();
+
+ let homeDashboard: FullDashboardType | null = null;
+
+ useLayoutEffect(() => {
+ const getHeaderButton = () => {
+ let onPressLog = () =>
+ navigation.navigate('login', { nextScreen: 'profile' });
+ let logIcon = 'login';
+ let logColor = theme.colors.primary;
+ if (isLoggedIn) {
+ onPressLog = () => showDisconnectDialog();
+ logIcon = 'logout';
+ logColor = theme.colors.text;
}
- });
- finalFeed.sort(HomeScreen.sortFeedTime);
- return finalFeed;
- }
- isLoggedIn: boolean | null;
-
- fabRef: { current: null | AnimatedFAB };
-
- currentNewFeed: Array;
-
- currentDashboard: FullDashboardType | null;
-
- dashboardManager: DashboardManager;
-
- constructor(props: PropsType) {
- super(props);
- this.fabRef = React.createRef();
- this.dashboardManager = new DashboardManager(props.navigation);
- this.currentNewFeed = [];
- this.currentDashboard = null;
- this.isLoggedIn = ConnectionManager.getInstance().isLoggedIn();
- props.navigation.setOptions({
- headerRight: this.getHeaderButton,
- });
- this.state = {
- dialogVisible: false,
+ return (
+
+
+ - navigation.navigate(MainRoutes.Settings)}
+ />
+
+ );
};
- }
+ navigation.setOptions({
+ headerRight: getHeaderButton,
+ });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [navigation, isLoggedIn]);
- componentDidMount() {
- const { props } = this;
- props.navigation.addListener('focus', this.onScreenFocus);
- // Handle link open when home is focused
- props.navigation.addListener('state', this.handleNavigationParams);
- }
+ useFocusEffect(
+ React.useCallback(() => {
+ const handleNavigationParams = () => {
+ const { route } = props;
+ if (route.params != null) {
+ if (route.params.nextScreen != null) {
+ navigation.navigate(route.params.nextScreen, route.params.data);
+ // reset params to prevent infinite loop
+ navigation.dispatch(CommonActions.setParams({ nextScreen: null }));
+ }
+ }
+ };
- /**
- * Updates login state and navigation parameters on screen focus
- */
- onScreenFocus = () => {
- const { props } = this;
- if (ConnectionManager.getInstance().isLoggedIn() !== this.isLoggedIn) {
- this.isLoggedIn = ConnectionManager.getInstance().isLoggedIn();
- props.navigation.setOptions({
- headerRight: this.getHeaderButton,
- });
- }
- // handle link open when home is not focused or created
- this.handleNavigationParams();
- };
-
- /**
- * Gets header buttons based on login state
- *
- * @returns {*}
- */
- getHeaderButton = () => {
- const { props } = this;
- let onPressLog = (): void =>
- props.navigation.navigate('login', { nextScreen: 'profile' });
- let logIcon = 'login';
- let logColor = props.theme.colors.primary;
- if (this.isLoggedIn) {
- onPressLog = (): void => this.showDisconnectDialog();
- logIcon = 'logout';
- logColor = props.theme.colors.text;
- }
-
- const onPressSettings = (): void => props.navigation.navigate('settings');
- return (
-
-
-
-
- );
- };
+ if (ConnectionManager.getInstance().isLoggedIn() !== isLoggedIn) {
+ setIsLoggedIn(ConnectionManager.getInstance().isLoggedIn());
+ }
+ // handle link open when home is not focused or created
+ handleNavigationParams();
+ return () => {};
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isLoggedIn])
+ );
/**
* Gets the event dashboard render item.
@@ -235,7 +220,7 @@ class HomeScreen extends React.Component {
* @param content
* @return {*}
*/
- getDashboardEvent(content: Array) {
+ const getDashboardEvent = (content: Array) => {
const futureEvents = getFutureEvents(content);
const displayEvent = getDisplayEvent(futureEvents);
// const clickPreviewAction = () =>
@@ -246,15 +231,15 @@ class HomeScreen extends React.Component {
return (
);
- }
+ };
/**
* Gets a dashboard item with a row of shortcut buttons.
@@ -262,16 +247,16 @@ class HomeScreen extends React.Component {
* @param content
* @return {*}
*/
- getDashboardRow(content: Array) {
+ const getDashboardRow = (content: Array) => {
return (
);
- }
+ };
/**
* Gets a dashboard shortcut item
@@ -279,15 +264,19 @@ class HomeScreen extends React.Component {
* @param item
* @returns {*}
*/
- getDashboardRowRenderItem = ({ item }: { item: ServiceItemType | null }) => {
+ const getDashboardRowRenderItem = ({
+ item,
+ }: {
+ item: ServiceItemType | null;
+ }) => {
if (item != null) {
return (
@@ -296,29 +285,13 @@ class HomeScreen extends React.Component {
return ;
};
- /**
- * Gets a render item for the given feed object
- *
- * @param item The feed item to display
- * @return {*}
- */
- getFeedItem(item: FeedItemType) {
- return ;
- }
+ const getRenderItem = ({ item }: { item: FeedItemType }) => (
+
+ );
- /**
- * Gets a FlatList render item
- *
- * @param item The item to display
- * @param section The current section
- * @return {*}
- */
- getRenderItem = ({ item }: { item: FeedItemType }) => this.getFeedItem(item);
-
- getRenderSectionHeader = (data: {
+ const getRenderSectionHeader = (data: {
section: SectionListData;
}) => {
- const { props } = this;
const icon = data.section.icon;
if (data.section.data.length > 0) {
return (
@@ -330,7 +303,7 @@ class HomeScreen extends React.Component {
{data.section.title}
@@ -339,7 +312,7 @@ class HomeScreen extends React.Component {
) : null}
@@ -347,7 +320,7 @@ class HomeScreen extends React.Component {
);
};
- getListHeader = (fetchedData: RawDashboardType | undefined) => {
+ const getListHeader = (fetchedData: RawDashboardType | undefined) => {
let dashboard = null;
if (fetchedData != null) {
dashboard = fetchedData.dashboard;
@@ -355,41 +328,17 @@ class HomeScreen extends React.Component {
return (
- {this.getDashboardRow(this.dashboardManager.getCurrentDashboard())}
- {this.getDashboardEvent(
- dashboard == null ? [] : dashboard.today_events
- )}
+ {getDashboardRow(currentDashboard)}
+ {getDashboardEvent(dashboard == null ? [] : dashboard.today_events)}
);
};
- /**
- * Navigates to the a new screen if navigation parameters specify one
- */
- handleNavigationParams = () => {
- const { props } = this;
- if (props.route.params != null) {
- if (props.route.params.nextScreen != null) {
- props.navigation.navigate(
- props.route.params.nextScreen,
- props.route.params.data
- );
- // reset params to prevent infinite loop
- props.navigation.dispatch(
- CommonActions.setParams({ nextScreen: null })
- );
- }
- }
- };
+ const showDisconnectDialog = () => setDialogVisible(true);
- showDisconnectDialog = (): void => this.setState({ dialogVisible: true });
+ const hideDisconnectDialog = () => setDialogVisible(false);
- hideDisconnectDialog = (): void => this.setState({ dialogVisible: false });
-
- openScanner = () => {
- const { props } = this;
- props.navigation.navigate('scanner');
- };
+ const openScanner = () => navigation.navigate('scanner');
/**
* Creates the dataset to be used in the FlatList
@@ -398,7 +347,7 @@ class HomeScreen extends React.Component {
* @param isLoading
* @return {*}
*/
- createDataset = (
+ const createDataset = (
fetchedData: RawDashboardType | undefined,
isLoading: boolean
): Array<{
@@ -407,21 +356,20 @@ class HomeScreen extends React.Component {
icon?: string;
id: string;
}> => {
+ let currentNewFeed: Array = [];
if (fetchedData) {
if (fetchedData.news_feed) {
- this.currentNewFeed = HomeScreen.generateNewsFeed(
- fetchedData.news_feed
- );
+ currentNewFeed = generateNewsFeed(fetchedData.news_feed);
}
if (fetchedData.dashboard) {
- this.currentDashboard = fetchedData.dashboard;
+ homeDashboard = fetchedData.dashboard;
}
}
- if (this.currentNewFeed.length > 0) {
+ if (currentNewFeed.length > 0) {
return [
{
title: i18n.t('screens.home.feedTitle'),
- data: this.currentNewFeed,
+ data: currentNewFeed,
id: SECTIONS_ID[1],
},
];
@@ -438,14 +386,11 @@ class HomeScreen extends React.Component {
];
};
- onEventContainerClick = () => {
- const { props } = this;
- props.navigation.navigate('planning');
- };
+ const onEventContainerClick = () => navigation.navigate(TabRoutes.Planning);
- onScroll = (event: NativeSyntheticEvent) => {
- if (this.fabRef.current) {
- this.fabRef.current.onScroll(event);
+ const onScroll = (event: NativeSyntheticEvent) => {
+ if (fabRef.current) {
+ fabRef.current.onScroll(event);
}
};
@@ -453,63 +398,50 @@ class HomeScreen extends React.Component {
* Callback when pressing the login button on the banner.
* This hides the banner and takes the user to the login page.
*/
- onLogin = () => {
- const { props } = this;
- props.navigation.navigate('login', {
+ const onLogin = () =>
+ navigation.navigate(MainRoutes.Login, {
nextScreen: 'profile',
});
- };
- render() {
- const { props, state } = this;
- return (
-
-
- readData(Urls.app.dashboard)}
- createDataset={this.createDataset}
- autoRefreshTime={REFRESH_TIME}
- refreshOnFocus={true}
- renderItem={this.getRenderItem}
- itemHeight={FEED_ITEM_HEIGHT}
- onScroll={this.onScroll}
- renderSectionHeader={this.getRenderSectionHeader}
- renderListHeaderComponent={this.getListHeader}
- />
-
- {!this.isLoggedIn ? (
-
- ) : null}
-
-
+
+ readData(Urls.app.dashboard)}
+ createDataset={createDataset}
+ autoRefreshTime={REFRESH_TIME}
+ refreshOnFocus={true}
+ renderItem={getRenderItem}
+ itemHeight={FEED_ITEM_HEIGHT}
+ onScroll={onScroll}
+ renderSectionHeader={getRenderSectionHeader}
+ renderListHeaderComponent={getListHeader}
/>
- );
- }
+ {!isLoggedIn ? (
+
+ ) : null}
+
+
+
+ );
}
-export default withTheme(HomeScreen);
+export default HomeScreen;
diff --git a/src/screens/Intro/IntroScreen.tsx b/src/screens/Intro/IntroScreen.tsx
new file mode 100644
index 0000000..aeec19e
--- /dev/null
+++ b/src/screens/Intro/IntroScreen.tsx
@@ -0,0 +1,41 @@
+import React from 'react';
+import CustomIntroSlider from '../../components/Overrides/CustomIntroSlider';
+import Update from '../../constants/Update';
+import { usePreferences } from '../../context/preferencesContext';
+import AprilFoolsManager from '../../managers/AprilFoolsManager';
+import {
+ getPreferenceBool,
+ getPreferenceNumber,
+ PreferenceKeys,
+} 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);
+ };
+
+ const showIntro =
+ getPreferenceBool(PreferenceKeys.showIntro, preferences) !== false;
+
+ const isUpdate =
+ getPreferenceNumber(PreferenceKeys.updateNumber, preferences) !==
+ Update.number && !showIntro;
+
+ const isAprilFools =
+ AprilFoolsManager.getInstance().isAprilFoolsEnabled() &&
+ getPreferenceBool(PreferenceKeys.showAprilFoolsStart, preferences) !==
+ false &&
+ !showIntro;
+
+ return (
+
+ );
+}
diff --git a/src/screens/MainApp.tsx b/src/screens/MainApp.tsx
new file mode 100644
index 0000000..6002b86
--- /dev/null
+++ b/src/screens/MainApp.tsx
@@ -0,0 +1,61 @@
+import React, { Ref, useEffect } from 'react';
+import {
+ NavigationContainer,
+ NavigationContainerRef,
+} from '@react-navigation/native';
+import { Provider as PaperProvider } from 'react-native-paper';
+import GENERAL_STYLES from '../constants/Styles';
+import CollapsibleProvider from '../components/providers/CollapsibleProvider';
+import CacheProvider from '../components/providers/CacheProvider';
+import { OverflowMenuProvider } from 'react-navigation-header-buttons';
+import MainNavigator from '../navigation/MainNavigator';
+import { Platform, SafeAreaView, View } from 'react-native';
+import { useDarkTheme } from '../context/preferencesContext';
+import { CustomDarkTheme, CustomWhiteTheme } from '../utils/Themes';
+import { setupStatusBar } from '../utils/Utils';
+
+type Props = {
+ defaultHomeRoute: string | null;
+ defaultHomeData: { [key: string]: string };
+};
+
+function MainApp(props: Props, ref?: Ref) {
+ const darkTheme = useDarkTheme();
+ const theme = darkTheme ? CustomDarkTheme : CustomWhiteTheme;
+
+ useEffect(() => {
+ if (Platform.OS === 'ios') {
+ setTimeout(setupStatusBar, 1000);
+ } else {
+ setupStatusBar(theme);
+ }
+ }, [theme]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default React.forwardRef(MainApp);
diff --git a/src/screens/Other/Settings/DashboardEditScreen.tsx b/src/screens/Other/Settings/DashboardEditScreen.tsx
index ff485a7..d521c17 100644
--- a/src/screens/Other/Settings/DashboardEditScreen.tsx
+++ b/src/screens/Other/Settings/DashboardEditScreen.tsx
@@ -17,31 +17,21 @@
* along with Campus INSAT. If not, see .
*/
-import * as React from 'react';
-import { StackNavigationProp } from '@react-navigation/stack';
+import React, { useRef, useState } from 'react';
import { Button, Card, Paragraph } from 'react-native-paper';
import { FlatList, StyleSheet } from 'react-native';
import { View } from 'react-native-animatable';
import i18n from 'i18n-js';
-import type {
- ServiceCategoryType,
- ServiceItemType,
-} from '../../../managers/ServicesManager';
-import DashboardManager from '../../../managers/DashboardManager';
import DashboardEditAccordion from '../../../components/Lists/DashboardEdit/DashboardEditAccordion';
import DashboardEditPreviewItem from '../../../components/Lists/DashboardEdit/DashboardEditPreviewItem';
-import AsyncStorageManager from '../../../managers/AsyncStorageManager';
import CollapsibleFlatList from '../../../components/Collapsible/CollapsibleFlatList';
-
-type PropsType = {
- navigation: StackNavigationProp;
-};
-
-type StateType = {
- currentDashboard: Array;
- currentDashboardIdList: Array;
- activeItem: number;
-};
+import {
+ getCategories,
+ ServiceCategoryType,
+ ServiceItemType,
+} from '../../../utils/Services';
+import { useNavigation } from '@react-navigation/core';
+import { useCurrentDashboard } from '../../../context/preferencesContext';
const styles = StyleSheet.create({
dashboardContainer: {
@@ -71,85 +61,71 @@ const styles = StyleSheet.create({
/**
* Class defining the Settings screen. This screen shows controls to modify app preferences.
*/
-class DashboardEditScreen extends React.Component {
- content: Array;
+function DashboardEditScreen() {
+ const navigation = useNavigation();
- initialDashboard: Array;
+ const {
+ currentDashboard,
+ currentDashboardIdList,
+ updateCurrentDashboard,
+ } = useCurrentDashboard();
+ const initialDashboard = useRef(currentDashboardIdList);
+ const [activeItem, setActiveItem] = useState(0);
- initialDashboardIdList: Array;
-
- constructor(props: PropsType) {
- super(props);
- const dashboardManager = new DashboardManager(props.navigation);
- this.initialDashboardIdList = AsyncStorageManager.getObject(
- AsyncStorageManager.PREFERENCES.dashboardItems.key
- );
- this.initialDashboard = dashboardManager.getCurrentDashboard();
- this.state = {
- currentDashboard: [...this.initialDashboard],
- currentDashboardIdList: [...this.initialDashboardIdList],
- activeItem: 0,
- };
- this.content = dashboardManager.getCategories();
- }
-
- getDashboardRowRenderItem = ({
+ const getDashboardRowRenderItem = ({
item,
index,
}: {
item: ServiceItemType | null;
index: number;
}) => {
- const { activeItem } = this.state;
return (
{
- this.setState({ activeItem: index });
+ setActiveItem(index);
}}
isActive={activeItem === index}
/>
);
};
- getDashboard(content: Array) {
+ const getDashboard = (content: Array) => {
return (
);
- }
+ };
- getRenderItem = ({ item }: { item: ServiceCategoryType }) => {
- const { currentDashboardIdList } = this.state;
+ const getRenderItem = ({ item }: { item: ServiceCategoryType }) => {
return (
);
};
- getListHeader() {
- const { currentDashboard } = this.state;
+ const getListHeader = () => {
return (
{i18n.t('screens.settings.dashboardEdit.undo')}
- {this.getDashboard(currentDashboard)}
+ {getDashboard(currentDashboard)}
@@ -158,43 +134,28 @@ class DashboardEditScreen extends React.Component {
);
- }
+ };
- updateDashboard = (service: ServiceItemType) => {
- const { currentDashboard, currentDashboardIdList, activeItem } = this.state;
- currentDashboard[activeItem] = service;
- currentDashboardIdList[activeItem] = service.key;
- this.setState({
- currentDashboard,
- currentDashboardIdList,
- });
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.dashboardItems.key,
- currentDashboardIdList
+ const updateDashboard = (service: ServiceItemType) => {
+ updateCurrentDashboard(
+ currentDashboardIdList.map((id, index) =>
+ index === activeItem ? service.key : id
+ )
);
};
- undoDashboard = () => {
- this.setState({
- currentDashboard: [...this.initialDashboard],
- currentDashboardIdList: [...this.initialDashboardIdList],
- });
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.dashboardItems.key,
- this.initialDashboardIdList
- );
+ const undoDashboard = () => {
+ updateCurrentDashboard(initialDashboard.current);
};
- render() {
- return (
-
- );
- }
+ return (
+
+ );
}
export default DashboardEditScreen;
diff --git a/src/screens/Other/Settings/SettingsScreen.tsx b/src/screens/Other/Settings/SettingsScreen.tsx
index 631fef3..4a70e4d 100644
--- a/src/screens/Other/Settings/SettingsScreen.tsx
+++ b/src/screens/Other/Settings/SettingsScreen.tsx
@@ -26,28 +26,20 @@ import {
List,
Switch,
ToggleButton,
- withTheme,
+ useTheme,
} from 'react-native-paper';
import { Appearance } from 'react-native-appearance';
-import { StackNavigationProp } from '@react-navigation/stack';
-import ThemeManager from '../../../managers/ThemeManager';
-import AsyncStorageManager from '../../../managers/AsyncStorageManager';
import CustomSlider from '../../../components/Overrides/CustomSlider';
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
import GENERAL_STYLES from '../../../constants/Styles';
-
-type PropsType = {
- navigation: StackNavigationProp;
- theme: ReactNativePaper.Theme;
-};
-
-type StateType = {
- nightMode: boolean;
- nightModeFollowSystem: boolean;
- startScreenPickerSelected: string;
- selectedWash: string;
- isDebugUnlocked: boolean;
-};
+import { usePreferences } from '../../../context/preferencesContext';
+import { useNavigation } from '@react-navigation/core';
+import {
+ getPreferenceBool,
+ getPreferenceNumber,
+ getPreferenceString,
+ PreferenceKeys,
+} from '../../../utils/asyncStorage';
const styles = StyleSheet.create({
slider: {
@@ -66,98 +58,67 @@ const styles = StyleSheet.create({
/**
* Class defining the Settings screen. This screen shows controls to modify app preferences.
*/
-class SettingsScreen extends React.Component {
- savedNotificationReminder: number;
+function SettingsScreen() {
+ const navigation = useNavigation();
+ const theme = useTheme();
+ const { preferences, updatePreferences } = usePreferences();
- /**
- * Loads user preferences into state
- */
- constructor(props: PropsType) {
- super(props);
- const notifReminder = AsyncStorageManager.getString(
- AsyncStorageManager.PREFERENCES.proxiwashNotifications.key
- );
- this.savedNotificationReminder = parseInt(notifReminder, 10);
- if (Number.isNaN(this.savedNotificationReminder)) {
- this.savedNotificationReminder = 0;
- }
+ const nightMode = getPreferenceBool(
+ PreferenceKeys.nightMode,
+ preferences
+ ) as boolean;
+ const nightModeFollowSystem =
+ (getPreferenceBool(
+ PreferenceKeys.nightModeFollowSystem,
+ preferences
+ ) as boolean) && Appearance.getColorScheme() !== 'no-preference';
+ const startScreenPickerSelected = getPreferenceString(
+ PreferenceKeys.defaultStartScreen,
+ preferences
+ ) as string;
+ const selectedWash = getPreferenceString(
+ PreferenceKeys.selectedWash,
+ preferences
+ ) as string;
+ const isDebugUnlocked = getPreferenceBool(
+ PreferenceKeys.debugUnlocked,
+ preferences
+ ) as boolean;
+ const notif = getPreferenceNumber(
+ PreferenceKeys.proxiwashNotifications,
+ preferences
+ );
+ const savedNotificationReminder = !notif || Number.isNaN(notif) ? 0 : notif;
- this.state = {
- nightMode: ThemeManager.getNightMode(),
- nightModeFollowSystem:
- AsyncStorageManager.getBool(
- AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key
- ) && Appearance.getColorScheme() !== 'no-preference',
- startScreenPickerSelected: AsyncStorageManager.getString(
- AsyncStorageManager.PREFERENCES.defaultStartScreen.key
- ),
- selectedWash: AsyncStorageManager.getString(
- AsyncStorageManager.PREFERENCES.selectedWash.key
- ),
- isDebugUnlocked: AsyncStorageManager.getBool(
- AsyncStorageManager.PREFERENCES.debugUnlocked.key
- ),
- };
- }
-
- /**
- * Saves the value for the proxiwash reminder notification time
- *
- * @param value The value to store
- */
- onProxiwashNotifPickerValueChange = (value: number) => {
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.proxiwashNotifications.key,
- value
- );
+ const onProxiwashNotifPickerValueChange = (value: number) => {
+ updatePreferences(PreferenceKeys.proxiwashNotifications, value);
};
- /**
- * Saves the value for the proxiwash reminder notification time
- *
- * @param value The value to store
- */
- onStartScreenPickerValueChange = (value: string) => {
+ const onStartScreenPickerValueChange = (value: string) => {
if (value != null) {
- this.setState({ startScreenPickerSelected: value });
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.defaultStartScreen.key,
- value
- );
+ updatePreferences(PreferenceKeys.defaultStartScreen, value);
}
};
- /**
- * Returns a picker allowing the user to select the proxiwash reminder notification time
- *
- * @returns {React.Node}
- */
- getProxiwashNotifPicker() {
- const { theme } = this.props;
+ const getProxiwashNotifPicker = () => {
return (
);
- }
+ };
- /**
- * Returns a radio picker allowing the user to select the proxiwash
- *
- * @returns {React.Node}
- */
- getProxiwashChangePicker() {
- const { selectedWash } = this.state;
+ const getProxiwashChangePicker = () => {
return (
{
/>
);
- }
+ };
- /**
- * Returns a picker allowing the user to select the start screen
- *
- * @returns {React.Node}
- */
- getStartScreenPicker() {
- const { startScreenPickerSelected } = this.state;
+ const getStartScreenPicker = () => {
return (
@@ -192,30 +147,17 @@ class SettingsScreen extends React.Component {
);
- }
-
- /**
- * Toggles night mode and saves it to preferences
- */
- onToggleNightMode = () => {
- const { nightMode } = this.state;
- ThemeManager.getInstance().setNightMode(!nightMode);
- this.setState({ nightMode: !nightMode });
};
- onToggleNightModeFollowSystem = () => {
- const { nightModeFollowSystem } = this.state;
- const value = !nightModeFollowSystem;
- this.setState({ nightModeFollowSystem: value });
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key,
- value
+ const onToggleNightMode = () => {
+ updatePreferences(PreferenceKeys.nightMode, !nightMode);
+ };
+
+ const onToggleNightModeFollowSystem = () => {
+ updatePreferences(
+ PreferenceKeys.nightModeFollowSystem,
+ !nightModeFollowSystem
);
- if (value) {
- const nightMode = Appearance.getColorScheme() === 'dark';
- ThemeManager.getInstance().setNightMode(nightMode);
- this.setState({ nightMode });
- }
};
/**
@@ -228,13 +170,13 @@ class SettingsScreen extends React.Component {
* @param state The current state of the switch
* @returns {React.Node}
*/
- static getToggleItem(
+ const getToggleItem = (
onPressCallback: () => void,
icon: string,
title: string,
subtitle: string,
state: boolean
- ) {
+ ) => {
return (
{
right={() => }
/>
);
- }
+ };
- getNavigateItem(
+ const getNavigateItem = (
route: string,
icon: string,
title: string,
subtitle: string,
onLongPress?: () => void
- ) {
- const { navigation } = this.props;
+ ) => {
return (
{
onLongPress={onLongPress}
/>
);
- }
+ };
- /**
- * Saves the value for the proxiwash selected wash
- *
- * @param value The value to store
- */
- onSelectWashValueChange = (value: string) => {
+ const onSelectWashValueChange = (value: string) => {
if (value != null) {
- this.setState({ selectedWash: value });
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.selectedWash.key,
- value
- );
+ updatePreferences(PreferenceKeys.selectedWash, value);
}
};
- /**
- * Unlocks debug mode and saves its state to user preferences
- */
- unlockDebugMode = () => {
- this.setState({ isDebugUnlocked: true });
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.debugUnlocked.key,
- true
- );
+ const unlockDebugMode = () => {
+ updatePreferences(PreferenceKeys.debugUnlocked, true);
};
- render() {
- const { nightModeFollowSystem, nightMode, isDebugUnlocked } = this.state;
- return (
-
-
-
-
- {Appearance.getColorScheme() !== 'no-preference'
- ? SettingsScreen.getToggleItem(
- this.onToggleNightModeFollowSystem,
- 'theme-light-dark',
- i18n.t('screens.settings.nightModeAuto'),
- i18n.t('screens.settings.nightModeAutoSub'),
- nightModeFollowSystem
- )
- : null}
- {Appearance.getColorScheme() === 'no-preference' ||
- !nightModeFollowSystem
- ? SettingsScreen.getToggleItem(
- this.onToggleNightMode,
- 'theme-light-dark',
- i18n.t('screens.settings.nightMode'),
- nightMode
- ? i18n.t('screens.settings.nightModeSubOn')
- : i18n.t('screens.settings.nightModeSubOff'),
- nightMode
- )
- : null}
- (
-
- )}
- />
- {this.getStartScreenPicker()}
- {this.getNavigateItem(
- 'dashboard-edit',
- 'view-dashboard',
- i18n.t('screens.settings.dashboard'),
- i18n.t('screens.settings.dashboardSub')
+ return (
+
+
+
+
+ {Appearance.getColorScheme() !== 'no-preference'
+ ? getToggleItem(
+ onToggleNightModeFollowSystem,
+ 'theme-light-dark',
+ i18n.t('screens.settings.nightModeAuto'),
+ i18n.t('screens.settings.nightModeAutoSub'),
+ nightModeFollowSystem
+ )
+ : null}
+ {Appearance.getColorScheme() === 'no-preference' ||
+ !nightModeFollowSystem
+ ? getToggleItem(
+ onToggleNightMode,
+ 'theme-light-dark',
+ i18n.t('screens.settings.nightMode'),
+ nightMode
+ ? i18n.t('screens.settings.nightModeSubOn')
+ : i18n.t('screens.settings.nightModeSubOff'),
+ nightMode
+ )
+ : null}
+ (
+
)}
-
-
-
-
-
- (
-
- )}
- />
-
- {this.getProxiwashNotifPicker()}
-
- (
-
- )}
- />
-
- {this.getProxiwashChangePicker()}
-
-
-
-
-
-
- {isDebugUnlocked
- ? this.getNavigateItem(
- 'debug',
- 'bug-check',
- i18n.t('screens.debug.title'),
- ''
- )
- : null}
- {this.getNavigateItem(
- 'about',
- 'information',
- i18n.t('screens.about.title'),
- i18n.t('screens.about.buttonDesc'),
- this.unlockDebugMode
+ />
+ {getStartScreenPicker()}
+ {getNavigateItem(
+ 'dashboard-edit',
+ 'view-dashboard',
+ i18n.t('screens.settings.dashboard'),
+ i18n.t('screens.settings.dashboardSub')
+ )}
+
+
+
+
+
+ (
+
)}
- {this.getNavigateItem(
- 'feedback',
- 'comment-quote',
- i18n.t('screens.feedback.homeButtonTitle'),
- i18n.t('screens.feedback.homeButtonSubtitle')
+ />
+
+ {getProxiwashNotifPicker()}
+
+ (
+
)}
-
-
-
- );
- }
+ />
+
+ {getProxiwashChangePicker()}
+
+
+
+
+
+
+ {isDebugUnlocked
+ ? getNavigateItem(
+ 'debug',
+ 'bug-check',
+ i18n.t('screens.debug.title'),
+ ''
+ )
+ : null}
+ {getNavigateItem(
+ 'about',
+ 'information',
+ i18n.t('screens.about.title'),
+ i18n.t('screens.about.buttonDesc'),
+ unlockDebugMode
+ )}
+ {getNavigateItem(
+ 'feedback',
+ 'comment-quote',
+ i18n.t('screens.feedback.homeButtonTitle'),
+ i18n.t('screens.feedback.homeButtonSubtitle')
+ )}
+
+
+
+ );
}
-export default withTheme(SettingsScreen);
+export default SettingsScreen;
diff --git a/src/screens/Planex/GroupSelectionScreen.tsx b/src/screens/Planex/GroupSelectionScreen.tsx
index 73ee8cd..b41de5a 100644
--- a/src/screens/Planex/GroupSelectionScreen.tsx
+++ b/src/screens/Planex/GroupSelectionScreen.tsx
@@ -17,23 +17,19 @@
* along with Campus INSAT. If not, see .
*/
-import React, {
- useCallback,
- useEffect,
- useLayoutEffect,
- useState,
-} from 'react';
+import React, { useCallback, useLayoutEffect, useState } from 'react';
import { Platform } from 'react-native';
import i18n from 'i18n-js';
import { Searchbar } from 'react-native-paper';
import { stringMatchQuery } from '../../utils/Search';
import WebSectionList from '../../components/Screens/WebSectionList';
import GroupListAccordion from '../../components/Lists/PlanexGroups/GroupListAccordion';
-import AsyncStorageManager from '../../managers/AsyncStorageManager';
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';
export type PlanexGroupType = {
name: string;
@@ -63,13 +59,23 @@ function sortName(
function GroupSelectionScreen() {
const navigation = useNavigation();
+ const { preferences, updatePreferences } = usePreferences();
const { groups, setGroups } = useCachedPlanexGroups();
const [currentSearchString, setCurrentSearchString] = useState('');
- const [favoriteGroups, setFavoriteGroups] = useState>(
- AsyncStorageManager.getObject(
- AsyncStorageManager.PREFERENCES.planexFavoriteGroups.key
- )
- );
+
+ const getFavoriteGroups = (): Array => {
+ const data = getPreferenceObject(
+ PreferenceKeys.planexFavoriteGroups,
+ preferences
+ );
+ if (data) {
+ return data as Array;
+ } else {
+ return [];
+ }
+ };
+
+ const favoriteGroups = getFavoriteGroups();
useLayoutEffect(() => {
navigation.setOptions({
@@ -140,10 +146,8 @@ function GroupSelectionScreen() {
* @param item The article pressed
*/
const onListItemPress = (item: PlanexGroupType) => {
- navigation.navigate('planex', {
- screen: 'index',
- params: { group: item },
- });
+ updatePreferences(PreferenceKeys.planexCurrentGroup, item);
+ navigation.goBack();
};
/**
@@ -153,12 +157,16 @@ function GroupSelectionScreen() {
*/
const onListFavoritePress = useCallback(
(group: PlanexGroupType) => {
+ const updateFavorites = (newValue: Array) => {
+ updatePreferences(PreferenceKeys.planexFavoriteGroups, newValue);
+ };
+
const removeGroupFromFavorites = (g: PlanexGroupType) => {
- setFavoriteGroups(favoriteGroups.filter((f) => f.id !== g.id));
+ updateFavorites(favoriteGroups.filter((f) => f.id !== g.id));
};
const addGroupToFavorites = (g: PlanexGroupType) => {
- setFavoriteGroups([...favoriteGroups, g].sort(sortName));
+ updateFavorites([...favoriteGroups, g].sort(sortName));
};
if (favoriteGroups.some((f) => f.id === group.id)) {
@@ -167,16 +175,9 @@ function GroupSelectionScreen() {
addGroupToFavorites(group);
}
},
- [favoriteGroups]
+ [favoriteGroups, updatePreferences]
);
- useEffect(() => {
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.planexFavoriteGroups.key,
- favoriteGroups
- );
- }, [favoriteGroups]);
-
/**
* Generates the dataset to be used in the FlatList.
* This improves formatting of group names, sorts alphabetically the categories, and adds favorites at the top.
diff --git a/src/screens/Planex/PlanexScreen.tsx b/src/screens/Planex/PlanexScreen.tsx
index 2c3e62e..855e0d5 100644
--- a/src/screens/Planex/PlanexScreen.tsx
+++ b/src/screens/Planex/PlanexScreen.tsx
@@ -17,17 +17,12 @@
* along with Campus INSAT. If not, see .
*/
-import React, { useCallback, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
import { Title, useTheme } from 'react-native-paper';
import i18n from 'i18n-js';
import { StyleSheet, View } from 'react-native';
-import {
- CommonActions,
- useFocusEffect,
- useNavigation,
-} from '@react-navigation/native';
+import { useNavigation } from '@react-navigation/native';
import Autolink from 'react-native-autolink';
-import AsyncStorageManager from '../../managers/AsyncStorageManager';
import AlertDialog from '../../components/Dialogs/AlertDialog';
import { dateToString, getTimeOnlyString } from '../../utils/Planning';
import DateManager from '../../managers/DateManager';
@@ -38,6 +33,8 @@ 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';
const styles = StyleSheet.create({
container: {
@@ -50,17 +47,10 @@ const styles = StyleSheet.create({
},
});
-type Props = {
- route: {
- params: {
- group?: PlanexGroupType;
- };
- };
-};
-
-function PlanexScreen(props: Props) {
+function PlanexScreen() {
const navigation = useNavigation();
const theme = useTheme();
+ const { preferences } = usePreferences();
const [dialogContent, setDialogContent] = useState<
| undefined
@@ -72,12 +62,13 @@ function PlanexScreen(props: Props) {
>();
const [injectJS, setInjectJS] = useState('');
- const getCurrentGroup = (): PlanexGroupType | undefined => {
- let currentGroupString = AsyncStorageManager.getString(
- AsyncStorageManager.PREFERENCES.planexCurrentGroup.key
+ const getCurrentGroup: () => PlanexGroupType | undefined = useCallback(() => {
+ let currentGroupString = getPreferenceString(
+ PreferenceKeys.planexCurrentGroup,
+ preferences
);
let group: PlanexGroupType;
- if (currentGroupString !== '') {
+ if (currentGroupString) {
group = JSON.parse(currentGroupString);
navigation.setOptions({
title: getPrettierPlanexGroupName(group.name),
@@ -85,22 +76,10 @@ function PlanexScreen(props: Props) {
return group;
}
return undefined;
- };
+ }, [navigation, preferences]);
- const [currentGroup, setCurrentGroup] = useState(
- getCurrentGroup()
- );
+ const currentGroup = getCurrentGroup();
- useFocusEffect(
- useCallback(() => {
- if (props.route.params?.group) {
- // reset params to prevent infinite loop
- selectNewGroup(props.route.params.group);
- navigation.dispatch(CommonActions.setParams({ group: undefined }));
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [props.route.params])
- );
/**
* Gets the Webview, with an error view on top if no group is selected.
*
@@ -194,21 +173,20 @@ function PlanexScreen(props: Props) {
const hideDialog = () => setDialogContent(undefined);
- /**
- * Sends the webpage a message with the new group to select and save it to preferences
- *
- * @param group The group object selected
- */
- const selectNewGroup = (group: PlanexGroupType) => {
- sendMessage('setGroup', group.id.toString());
- setCurrentGroup(group);
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.planexCurrentGroup.key,
- group
- );
+ useEffect(() => {
+ const group = getCurrentGroup();
+ if (group) {
+ sendMessage('setGroup', group.id.toString());
+ navigation.setOptions({ title: getPrettierPlanexGroupName(group.name) });
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [getCurrentGroup, navigation]);
- navigation.setOptions({ title: getPrettierPlanexGroupName(group.name) });
- };
+ const showMascot =
+ getPreferenceString(
+ PreferenceKeys.defaultStartScreen,
+ preferences
+ )?.toLowerCase() !== 'planex';
return (
@@ -220,11 +198,8 @@ function PlanexScreen(props: Props) {
{getWebView()}
)}
- {AsyncStorageManager.getString(
- AsyncStorageManager.PREFERENCES.defaultStartScreen.key
- ).toLowerCase() !== 'planex' ? (
+ {showMascot ? (
{
}
/>
.
*/
-import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
+import React, { useLayoutEffect, useRef, useState } from 'react';
import {
SectionListData,
SectionListRenderItemInfo,
@@ -28,7 +28,6 @@ import i18n from 'i18n-js';
import { Avatar, Button, Card, Text, useTheme } from 'react-native-paper';
import { Modalize } from 'react-native-modalize';
import WebSectionList from '../../components/Screens/WebSectionList';
-import AsyncStorageManager from '../../managers/AsyncStorageManager';
import ProxiwashListItem from '../../components/Lists/Proxiwash/ProxiwashListItem';
import ProxiwashConstants, {
MachineStates,
@@ -50,9 +49,16 @@ import type { SectionListDataType } from '../../components/Screens/WebSectionLis
import type { LaundromatType } from './ProxiwashAboutScreen';
import GENERAL_STYLES from '../../constants/Styles';
import { readData } from '../../utils/WebData';
-import { useFocusEffect, useNavigation } from '@react-navigation/core';
+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,
+} from '../../utils/asyncStorage';
const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds
const LIST_ITEM_HEIGHT = 64;
@@ -91,23 +97,35 @@ const styles = StyleSheet.create({
function ProxiwashScreen() {
const navigation = useNavigation();
const theme = useTheme();
+ const { preferences, updatePreferences } = usePreferences();
const [
modalCurrentDisplayItem,
setModalCurrentDisplayItem,
] = useState(null);
- const [machinesWatched, setMachinesWatched] = useState<
- Array
- >(
- AsyncStorageManager.getObject(
- AsyncStorageManager.PREFERENCES.proxiwashWatchedMachines.key
- )
+ const reminder = getPreferenceNumber(
+ PreferenceKeys.proxiwashNotifications,
+ preferences
);
- const [selectedWash, setSelectedWash] = useState(
- AsyncStorageManager.getString(
- AsyncStorageManager.PREFERENCES.selectedWash.key
- ) as 'tripodeB' | 'washinsa'
- );
+ const getMachinesWatched = () => {
+ const data = getPreferenceObject(
+ PreferenceKeys.proxiwashWatchedMachines,
+ preferences
+ ) as Array;
+ return data ? (data as Array) : [];
+ };
+
+ const getSelectedWash = () => {
+ const data = getPreferenceString(PreferenceKeys.selectedWash, preferences);
+ if (data !== 'washinsa' && data !== 'tripodeB') {
+ return 'washinsa';
+ } else {
+ return data;
+ }
+ };
+
+ const machinesWatched: Array = getMachinesWatched();
+ const selectedWash: 'washinsa' | 'tripodeB' = getSelectedWash();
const modalStateStrings: { [key in MachineStates]: string } = {
[MachineStates.AVAILABLE]: i18n.t('screens.proxiwash.modal.ready'),
@@ -137,17 +155,6 @@ function ProxiwashScreen() {
});
}, [navigation]);
- useFocusEffect(
- useCallback(() => {
- const selected = AsyncStorageManager.getString(
- AsyncStorageManager.PREFERENCES.selectedWash.key
- ) as 'tripodeB' | 'washinsa';
- if (selected !== selectedWash) {
- setSelectedWash(selected);
- }
- }, [selectedWash])
- );
-
/**
* Callback used when the user clicks on enable notifications for a machine
*
@@ -293,6 +300,7 @@ function ProxiwashScreen() {
setupMachineNotification(
machine.number,
true,
+ reminder,
getMachineEndDate(machine)
);
saveNotificationToState(machine);
@@ -342,7 +350,7 @@ function ProxiwashScreen() {
...data.washers,
]);
if (cleanedList !== machinesWatched) {
- setMachinesWatched(machinesWatched);
+ updatePreferences(PreferenceKeys.proxiwashWatchedMachines, cleanedList);
}
return [
{
@@ -407,11 +415,7 @@ function ProxiwashScreen() {
};
const saveNewWatchedList = (list: Array) => {
- setMachinesWatched(list);
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.proxiwashWatchedMachines.key,
- list
- );
+ updatePreferences(PreferenceKeys.proxiwashWatchedMachines, list);
};
const renderListHeaderComponent = (
@@ -451,7 +455,6 @@ function ProxiwashScreen() {
/>
;
@@ -66,8 +66,7 @@ class ServicesScreen extends React.Component {
constructor(props: PropsType) {
super(props);
- const services = new ServicesManager(props.navigation);
- this.finalDataset = services.getCategories([
+ this.finalDataset = getCategories(props.navigation.navigate, [
SERVICES_CATEGORIES_KEY.SPECIAL,
]);
}
@@ -159,7 +158,6 @@ class ServicesScreen extends React.Component {
hasTab
/>
;
diff --git a/src/utils/Notifications.ts b/src/utils/Notifications.ts
index 17a441a..3ef0f26 100644
--- a/src/utils/Notifications.ts
+++ b/src/utils/Notifications.ts
@@ -18,7 +18,6 @@
*/
import i18n from 'i18n-js';
-import AsyncStorageManager from '../managers/AsyncStorageManager';
import PushNotificationIOS from '@react-native-community/push-notification-ios';
import PushNotification from 'react-native-push-notification';
import { Platform } from 'react-native';
@@ -79,11 +78,8 @@ PushNotification.configure({
* @param machineID The machine id to schedule notifications for. This is used as id and in the notification string.
* @param date The date to trigger the notification at
*/
-function createNotifications(machineID: string, date: Date) {
- const reminder = AsyncStorageManager.getNumber(
- AsyncStorageManager.PREFERENCES.proxiwashNotifications.key
- );
- if (!Number.isNaN(reminder) && reminder > 0) {
+function createNotifications(machineID: string, date: Date, reminder?: number) {
+ if (reminder && !Number.isNaN(reminder) && reminder > 0) {
const id = reminderIdFactor * parseInt(machineID, 10);
const reminderDate = new Date(date);
reminderDate.setMinutes(reminderDate.getMinutes() - reminder);
@@ -122,10 +118,11 @@ function createNotifications(machineID: string, date: Date) {
export function setupMachineNotification(
machineID: string,
isEnabled: boolean,
+ reminder?: number,
endDate?: Date | null
) {
if (isEnabled && endDate) {
- createNotifications(machineID, endDate);
+ createNotifications(machineID, endDate, reminder);
} else {
PushNotification.cancelLocalNotifications({ id: machineID });
const reminderId = reminderIdFactor * parseInt(machineID, 10);
diff --git a/src/utils/Services.ts b/src/utils/Services.ts
index bae30c3..8f91a59 100644
--- a/src/utils/Services.ts
+++ b/src/utils/Services.ts
@@ -16,6 +16,11 @@
* You should have received a copy of the GNU General Public License
* along with Campus INSAT. If not, see .
*/
+import i18n from 'i18n-js';
+import type { FullDashboardType } from '../screens/Home/HomeScreen';
+import Urls from '../constants/Urls';
+import { MainRoutes } from '../navigation/MainNavigator';
+import { TabRoutes } from '../navigation/TabNavigator';
/**
* Gets the given services list without items of the given ids
@@ -25,45 +30,296 @@
* @returns {[]}
*/
export default function getStrippedServicesList(
- idList: Array,
- sourceList: Array
+ sourceList: Array,
+ idList?: Array
) {
- const newArray: Array = [];
- sourceList.forEach((item: T) => {
- if (!idList.includes(item.key)) {
- newArray.push(item);
- }
- });
- return newArray;
+ if (idList) {
+ return sourceList.filter((item) => !idList.includes(item.key));
+ } else {
+ return sourceList;
+ }
}
-/**
- * Gets a sublist of the given list with items of the given ids only
- *
- * The given list must have a field id or key
- *
- * @param idList The ids of items to find
- * @param originalList The original list
- * @returns {[]}
- */
-export function getSublistWithIds(
- idList: Array,
- originalList: Array
-) {
- const subList: Array = [];
- for (let i = 0; i < idList.length; i += 1) {
- subList.push(null);
- }
- let itemsAdded = 0;
- for (let i = 0; i < originalList.length; i += 1) {
- const item = originalList[i];
- if (idList.includes(item.key)) {
- subList[idList.indexOf(item.key)] = item;
- itemsAdded += 1;
- if (itemsAdded === idList.length) {
- break;
- }
- }
- }
- return subList;
+const AMICALE_LOGO = require('../../assets/amicale.png');
+
+export const SERVICES_KEY = {
+ CLUBS: 'clubs',
+ PROFILE: 'profile',
+ EQUIPMENT: 'equipment',
+ AMICALE_WEBSITE: 'amicale_website',
+ VOTE: 'vote',
+ PROXIMO: 'proximo',
+ WIKETUD: 'wiketud',
+ ELUS_ETUDIANTS: 'elus_etudiants',
+ TUTOR_INSA: 'tutor_insa',
+ RU: 'ru',
+ AVAILABLE_ROOMS: 'available_rooms',
+ BIB: 'bib',
+ EMAIL: 'email',
+ ENT: 'ent',
+ INSA_ACCOUNT: 'insa_account',
+ WASHERS: 'washers',
+ DRYERS: 'dryers',
+};
+
+export const SERVICES_CATEGORIES_KEY = {
+ AMICALE: 'amicale',
+ STUDENTS: 'students',
+ INSA: 'insa',
+ SPECIAL: 'special',
+};
+
+export type ServiceItemType = {
+ key: string;
+ title: string;
+ subtitle: string;
+ image: string | number;
+ onPress: () => void;
+ badgeFunction?: (dashboard: FullDashboardType) => number;
+};
+
+export type ServiceCategoryType = {
+ key: string;
+ title: string;
+ subtitle: string;
+ image: string | number;
+ content: Array;
+};
+
+export function getAmicaleServices(
+ onPress: (route: string, params?: { [key: string]: any }) => void,
+ excludedItems?: Array
+): Array {
+ const amicaleDataset = [
+ {
+ key: SERVICES_KEY.CLUBS,
+ title: i18n.t('screens.clubs.title'),
+ subtitle: i18n.t('screens.services.descriptions.clubs'),
+ image: Urls.images.clubs,
+ onPress: () => onPress(MainRoutes.ClubList),
+ },
+ {
+ key: SERVICES_KEY.PROFILE,
+ title: i18n.t('screens.profile.title'),
+ subtitle: i18n.t('screens.services.descriptions.profile'),
+ image: Urls.images.profile,
+ onPress: () => onPress(MainRoutes.Profile),
+ },
+ {
+ key: SERVICES_KEY.EQUIPMENT,
+ title: i18n.t('screens.equipment.title'),
+ subtitle: i18n.t('screens.services.descriptions.equipment'),
+ image: Urls.images.equipment,
+ onPress: () => onPress(MainRoutes.EquipmentList),
+ },
+ {
+ key: SERVICES_KEY.AMICALE_WEBSITE,
+ title: i18n.t('screens.websites.amicale'),
+ subtitle: i18n.t('screens.services.descriptions.amicaleWebsite'),
+ image: Urls.images.amicale,
+ onPress: () =>
+ onPress(MainRoutes.Website, {
+ host: Urls.websites.amicale,
+ title: i18n.t('screens.websites.amicale'),
+ }),
+ },
+ {
+ key: SERVICES_KEY.VOTE,
+ title: i18n.t('screens.vote.title'),
+ subtitle: i18n.t('screens.services.descriptions.vote'),
+ image: Urls.images.vote,
+ onPress: () => onPress(MainRoutes.Vote),
+ },
+ ];
+ return getStrippedServicesList(amicaleDataset, excludedItems);
+}
+
+export function getStudentServices(
+ onPress: (route: string, params?: { [key: string]: any }) => void,
+ excludedItems?: Array
+): Array {
+ const studentsDataset = [
+ {
+ key: SERVICES_KEY.PROXIMO,
+ title: i18n.t('screens.proximo.title'),
+ subtitle: i18n.t('screens.services.descriptions.proximo'),
+ image: Urls.images.proximo,
+ onPress: () => onPress(MainRoutes.Proximo),
+ badgeFunction: (dashboard: FullDashboardType): number =>
+ dashboard.proximo_articles,
+ },
+ {
+ key: SERVICES_KEY.WIKETUD,
+ title: 'Wiketud',
+ subtitle: i18n.t('screens.services.descriptions.wiketud'),
+ image: Urls.images.wiketud,
+ onPress: () =>
+ onPress(MainRoutes.Website, {
+ host: Urls.websites.wiketud,
+ title: 'Wiketud',
+ }),
+ },
+ {
+ key: SERVICES_KEY.ELUS_ETUDIANTS,
+ title: 'Élus Étudiants',
+ subtitle: i18n.t('screens.services.descriptions.elusEtudiants'),
+ image: Urls.images.elusEtudiants,
+ onPress: () =>
+ onPress(MainRoutes.Website, {
+ host: Urls.websites.elusEtudiants,
+ title: 'Élus Étudiants',
+ }),
+ },
+ {
+ key: SERVICES_KEY.TUTOR_INSA,
+ title: "Tutor'INSA",
+ subtitle: i18n.t('screens.services.descriptions.tutorInsa'),
+ image: Urls.images.tutorInsa,
+ onPress: () =>
+ onPress(MainRoutes.Website, {
+ host: Urls.websites.tutorInsa,
+ title: "Tutor'INSA",
+ }),
+ badgeFunction: (dashboard: FullDashboardType): number =>
+ dashboard.available_tutorials,
+ },
+ ];
+ return getStrippedServicesList(studentsDataset, excludedItems);
+}
+
+export function getINSAServices(
+ onPress: (route: string, params?: { [key: string]: any }) => void,
+ excludedItems?: Array
+): Array {
+ const insaDataset = [
+ {
+ key: SERVICES_KEY.RU,
+ title: i18n.t('screens.menu.title'),
+ subtitle: i18n.t('screens.services.descriptions.self'),
+ image: Urls.images.menu,
+ onPress: () => onPress(MainRoutes.SelfMenu),
+ badgeFunction: (dashboard: FullDashboardType): number =>
+ dashboard.today_menu.length,
+ },
+ {
+ key: SERVICES_KEY.AVAILABLE_ROOMS,
+ title: i18n.t('screens.websites.rooms'),
+ subtitle: i18n.t('screens.services.descriptions.availableRooms'),
+ image: Urls.images.availableRooms,
+ onPress: () =>
+ onPress(MainRoutes.Website, {
+ host: Urls.websites.availableRooms,
+ title: i18n.t('screens.websites.rooms'),
+ }),
+ },
+ {
+ key: SERVICES_KEY.BIB,
+ title: i18n.t('screens.websites.bib'),
+ subtitle: i18n.t('screens.services.descriptions.bib'),
+ image: Urls.images.bib,
+ onPress: () =>
+ onPress(MainRoutes.Website, {
+ host: Urls.websites.bib,
+ title: i18n.t('screens.websites.bib'),
+ }),
+ },
+ {
+ key: SERVICES_KEY.EMAIL,
+ title: i18n.t('screens.websites.mails'),
+ subtitle: i18n.t('screens.services.descriptions.mails'),
+ image: Urls.images.bluemind,
+ onPress: () =>
+ onPress(MainRoutes.Website, {
+ host: Urls.websites.bluemind,
+ title: i18n.t('screens.websites.mails'),
+ }),
+ },
+ {
+ key: SERVICES_KEY.ENT,
+ title: i18n.t('screens.websites.ent'),
+ subtitle: i18n.t('screens.services.descriptions.ent'),
+ image: Urls.images.ent,
+ onPress: () =>
+ onPress(MainRoutes.Website, {
+ host: Urls.websites.ent,
+ title: i18n.t('screens.websites.ent'),
+ }),
+ },
+ {
+ key: SERVICES_KEY.INSA_ACCOUNT,
+ title: i18n.t('screens.insaAccount.title'),
+ subtitle: i18n.t('screens.services.descriptions.insaAccount'),
+ image: Urls.images.insaAccount,
+ onPress: () =>
+ onPress(MainRoutes.Website, {
+ host: Urls.websites.insaAccount,
+ title: i18n.t('screens.insaAccount.title'),
+ }),
+ },
+ ];
+ return getStrippedServicesList(insaDataset, excludedItems);
+}
+
+export function getSpecialServices(
+ onPress: (route: string, params?: { [key: string]: any }) => void,
+ excludedItems?: Array
+): Array {
+ const specialDataset = [
+ {
+ key: SERVICES_KEY.WASHERS,
+ title: i18n.t('screens.proxiwash.washers'),
+ subtitle: i18n.t('screens.services.descriptions.washers'),
+ image: Urls.images.washer,
+ onPress: () => onPress(TabRoutes.Proxiwash),
+ badgeFunction: (dashboard: FullDashboardType): number =>
+ dashboard.available_washers,
+ },
+ {
+ key: SERVICES_KEY.DRYERS,
+ title: i18n.t('screens.proxiwash.dryers'),
+ subtitle: i18n.t('screens.services.descriptions.washers'),
+ image: Urls.images.dryer,
+ onPress: () => onPress(TabRoutes.Proxiwash),
+ badgeFunction: (dashboard: FullDashboardType): number =>
+ dashboard.available_dryers,
+ },
+ ];
+ return getStrippedServicesList(specialDataset, excludedItems);
+}
+
+export function getCategories(
+ onPress: (route: string, params?: { [key: string]: any }) => void,
+ excludedItems?: Array
+): Array {
+ const categoriesDataset = [
+ {
+ key: SERVICES_CATEGORIES_KEY.AMICALE,
+ title: i18n.t('screens.services.categories.amicale'),
+ subtitle: i18n.t('screens.services.more'),
+ image: AMICALE_LOGO,
+ content: getAmicaleServices(onPress),
+ },
+ {
+ key: SERVICES_CATEGORIES_KEY.STUDENTS,
+ title: i18n.t('screens.services.categories.students'),
+ subtitle: i18n.t('screens.services.more'),
+ image: 'account-group',
+ content: getStudentServices(onPress),
+ },
+ {
+ key: SERVICES_CATEGORIES_KEY.INSA,
+ title: i18n.t('screens.services.categories.insa'),
+ subtitle: i18n.t('screens.services.more'),
+ image: 'school',
+ content: getINSAServices(onPress),
+ },
+ {
+ key: SERVICES_CATEGORIES_KEY.SPECIAL,
+ title: i18n.t('screens.services.categories.special'),
+ subtitle: i18n.t('screens.services.categories.special'),
+ image: 'star',
+ content: getSpecialServices(onPress),
+ },
+ ];
+ return getStrippedServicesList(categoriesDataset, excludedItems);
}
diff --git a/src/managers/ThemeManager.ts b/src/utils/Themes.ts
similarity index 53%
rename from src/managers/ThemeManager.ts
rename to src/utils/Themes.ts
index 9b4d341..14cecfe 100644
--- a/src/managers/ThemeManager.ts
+++ b/src/utils/Themes.ts
@@ -1,28 +1,4 @@
-/*
- * Copyright (c) 2019 - 2020 Arnaud Vergnet.
- *
- * This file is part of Campus INSAT.
- *
- * Campus INSAT is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Campus INSAT is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Campus INSAT. If not, see .
- */
-
import { DarkTheme, DefaultTheme } from 'react-native-paper';
-import { Appearance } from 'react-native-appearance';
-import AsyncStorageManager from './AsyncStorageManager';
-import AprilFoolsManager from './AprilFoolsManager';
-
-const colorScheme = Appearance.getColorScheme();
declare global {
namespace ReactNativePaper {
@@ -83,7 +59,7 @@ declare global {
}
}
-const CustomWhiteTheme: ReactNativePaper.Theme = {
+export const CustomWhiteTheme: ReactNativePaper.Theme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
@@ -142,7 +118,7 @@ const CustomWhiteTheme: ReactNativePaper.Theme = {
},
};
-const CustomDarkTheme: ReactNativePaper.Theme = {
+export const CustomDarkTheme: ReactNativePaper.Theme = {
...DarkTheme,
colors: {
...DarkTheme.colors,
@@ -200,99 +176,3 @@ const CustomDarkTheme: ReactNativePaper.Theme = {
mascotMessageArrow: '#323232',
},
};
-
-/**
- * Singleton class used to manage themes
- */
-export default class ThemeManager {
- static instance: ThemeManager | null = null;
-
- updateThemeCallback: null | (() => void);
-
- constructor() {
- this.updateThemeCallback = null;
- }
-
- /**
- * Get this class instance or create one if none is found
- *
- * @returns {ThemeManager}
- */
- static getInstance(): ThemeManager {
- if (ThemeManager.instance == null) {
- ThemeManager.instance = new ThemeManager();
- }
- return ThemeManager.instance;
- }
-
- /**
- * Gets night mode status.
- * If Follow System Preferences is enabled, will first use system theme.
- * If disabled or not available, will use value stored din preferences
- *
- * @returns {boolean} Night mode state
- */
- static getNightMode(): boolean {
- return (
- (AsyncStorageManager.getBool(
- AsyncStorageManager.PREFERENCES.nightMode.key
- ) &&
- (!AsyncStorageManager.getBool(
- AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key
- ) ||
- colorScheme === 'no-preference')) ||
- (AsyncStorageManager.getBool(
- AsyncStorageManager.PREFERENCES.nightModeFollowSystem.key
- ) &&
- colorScheme === 'dark')
- );
- }
-
- /**
- * Get the current theme based on night mode and events
- *
- * @returns {ReactNativePaper.Theme} The current theme
- */
- static getCurrentTheme(): ReactNativePaper.Theme {
- if (AprilFoolsManager.getInstance().isAprilFoolsEnabled()) {
- return AprilFoolsManager.getAprilFoolsTheme(CustomWhiteTheme);
- }
- return ThemeManager.getBaseTheme();
- }
-
- /**
- * Get the theme based on night mode
- *
- * @return {ReactNativePaper.Theme} The theme
- */
- static getBaseTheme(): ReactNativePaper.Theme {
- if (ThemeManager.getNightMode()) {
- return CustomDarkTheme;
- }
- return CustomWhiteTheme;
- }
-
- /**
- * Sets the function to be called when the theme is changed (allows for general reload of the app)
- *
- * @param callback Function to call after theme change
- */
- setUpdateThemeCallback(callback: () => void) {
- this.updateThemeCallback = callback;
- }
-
- /**
- * Set night mode and save it to preferences
- *
- * @param isNightMode True to enable night mode, false to disable
- */
- setNightMode(isNightMode: boolean) {
- AsyncStorageManager.set(
- AsyncStorageManager.PREFERENCES.nightMode.key,
- isNightMode
- );
- if (this.updateThemeCallback != null) {
- this.updateThemeCallback();
- }
- }
-}
diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts
index d8c9af4..6ff6085 100644
--- a/src/utils/Utils.ts
+++ b/src/utils/Utils.ts
@@ -18,23 +18,21 @@
*/
import { Platform, StatusBar } from 'react-native';
-import ThemeManager from '../managers/ThemeManager';
/**
* Updates status bar content color if on iOS only,
* as the android status bar is always set to black.
*/
-export function setupStatusBar() {
- if (ThemeManager.getNightMode()) {
- StatusBar.setBarStyle('light-content', true);
- } else {
- StatusBar.setBarStyle('dark-content', true);
- }
- if (Platform.OS === 'android') {
- StatusBar.setBackgroundColor(
- ThemeManager.getCurrentTheme().colors.surface,
- true
- );
+export function setupStatusBar(theme?: ReactNativePaper.Theme) {
+ if (theme) {
+ if (theme.dark) {
+ StatusBar.setBarStyle('light-content', true);
+ } else {
+ StatusBar.setBarStyle('dark-content', true);
+ }
+ if (Platform.OS === 'android') {
+ StatusBar.setBackgroundColor(theme.colors.surface, true);
+ }
}
}
diff --git a/src/utils/asyncStorage.ts b/src/utils/asyncStorage.ts
index 0755e68..0c19b9c 100644
--- a/src/utils/asyncStorage.ts
+++ b/src/utils/asyncStorage.ts
@@ -1,5 +1,5 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
-import { SERVICES_KEY } from '../managers/ServicesManager';
+import { SERVICES_KEY } from './Services';
export enum PreferenceKeys {
debugUnlocked = 'debugUnlocked',
@@ -9,6 +9,7 @@ export enum PreferenceKeys {
nightModeFollowSystem = 'nightModeFollowSystem',
nightMode = 'nightMode',
defaultStartScreen = 'defaultStartScreen',
+
servicesShowMascot = 'servicesShowMascot',
proxiwashShowMascot = 'proxiwashShowMascot',
homeShowMascot = 'homeShowMascot',
@@ -17,7 +18,8 @@ export enum PreferenceKeys {
loginShowMascot = 'loginShowMascot',
voteShowMascot = 'voteShowMascot',
equipmentShowMascot = 'equipmentShowMascot',
- gameStartMascot = 'gameStartMascot',
+ gameShowMascot = 'gameShowMascot',
+
proxiwashWatchedMachines = 'proxiwashWatchedMachines',
showAprilFoolsStart = 'showAprilFoolsStart',
planexCurrentGroup = 'planexCurrentGroup',
@@ -45,7 +47,7 @@ export const defaultPreferences: { [key in PreferenceKeys]: string } = {
[PreferenceKeys.loginShowMascot]: '1',
[PreferenceKeys.voteShowMascot]: '1',
[PreferenceKeys.equipmentShowMascot]: '1',
- [PreferenceKeys.gameStartMascot]: '1',
+ [PreferenceKeys.gameShowMascot]: '1',
[PreferenceKeys.proxiwashWatchedMachines]: '[]',
[PreferenceKeys.showAprilFoolsStart]: '1',
[PreferenceKeys.planexCurrentGroup]: '',
@@ -114,6 +116,10 @@ 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
*