From 0cbf60e6cc823c6ee9271b7d0496f1583dc55d83 Mon Sep 17 00:00:00 2001 From: keplyx Date: Wed, 31 Jul 2019 14:22:36 +0200 Subject: [PATCH] Abstracted async storage and fixed key warning in proxiwash tab view --- App.js | 9 +- components/CustomMaterialIcon.js | 4 +- components/FetchedDataSectionList.js | 16 +-- screens/HomeScreen.js | 3 +- screens/Proximo/ProximoMainScreen.js | 1 + screens/ProxiwashScreen.js | 142 ++++++++++++++++----------- screens/SettingsScreen.js | 25 +---- utils/AsyncStorageManager.js | 66 +++++++++++++ utils/NotificationsManager.js | 2 +- utils/ThemeManager.js | 35 ++----- 10 files changed, 178 insertions(+), 125 deletions(-) create mode 100644 utils/AsyncStorageManager.js diff --git a/App.js b/App.js index 2fec25f..14f6053 100644 --- a/App.js +++ b/App.js @@ -10,6 +10,7 @@ import * as Font from 'expo-font'; // https://github.com/GeekyAnts/theme/pull/5/files/91f67c55ca6e65fe3af779586b506950c9f331be#diff-4cfc2dd4d5dae7954012899f2268a422 // to allow for dynamic theme switching import {clearThemeCache} from 'native-base-shoutem-theme'; +import AsyncStorageManager from "./utils/AsyncStorageManager"; type Props = {}; @@ -39,11 +40,11 @@ export default class App extends React.Component { 'Roboto': require('native-base/Fonts/Roboto.ttf'), 'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'), }); + await AsyncStorageManager.getInstance().loadPreferences(); ThemeManager.getInstance().setUpdateThemeCallback(() => this.updateTheme()); - await ThemeManager.getInstance().getDataFromPreferences(); this.setState({ isLoading: false, - currentTheme: ThemeManager.getInstance().getCurrentTheme() + currentTheme: ThemeManager.getCurrentTheme() }); } @@ -53,9 +54,9 @@ export default class App extends React.Component { updateTheme() { // console.log('update theme called'); this.setState({ - currentTheme: ThemeManager.getInstance().getCurrentTheme() + currentTheme: ThemeManager.getCurrentTheme() }); - clearThemeCache(); + // clearThemeCache(); } /** diff --git a/components/CustomMaterialIcon.js b/components/CustomMaterialIcon.js index a3d81f8..7b99881 100644 --- a/components/CustomMaterialIcon.js +++ b/components/CustomMaterialIcon.js @@ -41,8 +41,8 @@ export default class CustomMaterialIcon extends React.Component { this.props.color !== undefined ? this.props.color : this.props.active ? - ThemeManager.getInstance().getCurrentThemeVariables().brandPrimary : - ThemeManager.getInstance().getCurrentThemeVariables().customMaterialIconColor, + ThemeManager.getCurrentThemeVariables().brandPrimary : + ThemeManager.getCurrentThemeVariables().customMaterialIconColor, fontSize: this.props.fontSize, width: this.props.width }} diff --git a/components/FetchedDataSectionList.js b/components/FetchedDataSectionList.js index 96fe20b..eee9c50 100644 --- a/components/FetchedDataSectionList.js +++ b/components/FetchedDataSectionList.js @@ -83,15 +83,6 @@ export default class FetchedDataSectionList extends React.Component this.getKeyExtractor(item)} refreshControl={ ) { let tabbedView = []; for (let i = 0; i < dataset.length; i++) { - tabbedView.push( + tabbedView.push( {dataset[i].title} - }> + } + key={dataset[i].title}> {this.getSectionList( [ @@ -137,6 +128,7 @@ export default class FetchedDataSectionList extends React.Component} - */ - async componentWillMount() { - let dataString = await AsyncStorage.getItem(WATCHED_MACHINES_PREFKEY); - if (dataString === null) - dataString = '[]'; - this.setState({ - machinesWatched: JSON.parse(dataString) - }); + getDryersKeyExtractor(item: Object) { + console.log(item !== undefined ? "dryer" + item.number : undefined); + return item !== undefined ? "dryer" + item.number : undefined; + } + + getWashersKeyExtractor(item: Object) { + console.log(item !== undefined ? "washer" + item.number : undefined); + return item !== undefined ? "washer" + item.number : undefined; } /** @@ -111,10 +107,8 @@ export default class ProxiwashScreen extends FetchedDataSectionList { static getRemainingTime(startString: string, endString: string, percentDone: string): number { let startArray = startString.split(':'); let endArray = endString.split(':'); - let startDate = new Date(); - startDate.setHours(parseInt(startArray[0]), parseInt(startArray[1]), 0, 0); - let endDate = new Date(); - endDate.setHours(parseInt(endArray[0]), parseInt(endArray[1]), 0, 0); + let startDate = new Date().setHours(parseInt(startArray[0]), parseInt(startArray[1]), 0, 0); + let endDate = new Date().setHours(parseInt(endArray[0]), parseInt(endArray[1]), 0, 0); // Convert milliseconds into minutes let time: string = (((100 - parseFloat(percentDone)) / 100) * (endDate - startDate) / (60 * 1000)).toFixed(0); return parseInt(time); @@ -131,35 +125,41 @@ export default class ProxiwashScreen extends FetchedDataSectionList { */ async setupNotifications(machineId: string, remainingTime: number) { if (!this.isMachineWatched(machineId)) { - let endNotifID = await NotificationsManager.scheduleNotification( + let endNotificationID = await NotificationsManager.scheduleNotification( i18n.t('proxiwashScreen.notifications.machineFinishedTitle'), i18n.t('proxiwashScreen.notifications.machineFinishedBody', {number: machineId}), new Date().getTime() + remainingTime * (60 * 1000) // Convert back to milliseconds ); - let reminderNotifID = undefined; - let val = await AsyncStorage.getItem('proxiwashNotifKey'); - if (val === null) - val = "5"; - if (val !== "never") - reminderNotifTime = parseInt(val); - else - reminderNotifTime = -1; - console.log(reminderNotifTime); - if (remainingTime > reminderNotifTime && reminderNotifTime > 0) { - reminderNotifID = await NotificationsManager.scheduleNotification( - i18n.t('proxiwashScreen.notifications.machineRunningTitle', {time: reminderNotifTime}), - i18n.t('proxiwashScreen.notifications.machineRunningBody', {number: machineId}), - new Date().getTime() + (remainingTime - reminderNotifTime) * (60 * 1000) // Convert back to milliseconds - ); - } - let data = this.state.machinesWatched; - data.push({machineNumber: machineId, endNotifID: endNotifID, reminderNotifID: reminderNotifID}); - this.setState({machinesWatched: data}); - AsyncStorage.setItem(WATCHED_MACHINES_PREFKEY, JSON.stringify(data)); + let reminderNotificationID = await ProxiwashScreen.setupReminderNotification(machineId, remainingTime); + this.saveNotificationToPrefs(machineId, endNotificationID, reminderNotificationID); } else this.disableNotification(machineId); } + static async setupReminderNotification(machineId: string, remainingTime: number): Promise { + let reminderNotificationID: string | null = null; + let reminderNotificationTime = ProxiwashScreen.getReminderNotificationTime(); + if (remainingTime > reminderNotificationTime && reminderNotificationTime > 0) { + reminderNotificationID = await NotificationsManager.scheduleNotification( + i18n.t('proxiwashScreen.notifications.machineRunningTitle', {time: reminderNotificationTime}), + i18n.t('proxiwashScreen.notifications.machineRunningBody', {number: machineId}), + new Date().getTime() + (remainingTime - reminderNotificationTime) * (60 * 1000) // Convert back to milliseconds + ); + } + return reminderNotificationID; + } + + static getReminderNotificationTime(): number { + let val = AsyncStorageManager.getInstance().preferences.proxiwashNotifications.current; + if (val !== "never") + reminderNotifTime = parseInt(val); + else + reminderNotifTime = -1; + console.log(reminderNotifTime); + return reminderNotifTime; + } + + /** * Stop scheduled notifications for the machine of the given ID. * This will also remove the notification if it was already shown. @@ -173,15 +173,37 @@ export default class ProxiwashScreen extends FetchedDataSectionList { return elem.machineNumber === machineId }); let arrayIndex = data.indexOf(elem); - NotificationsManager.cancelScheduledNotification(data[arrayIndex].endNotifID); - if (data[arrayIndex].reminderNotifID !== undefined) - NotificationsManager.cancelScheduledNotification(data[arrayIndex].reminderNotifID); - data.splice(arrayIndex, 1); - this.setState({machinesWatched: data}); - AsyncStorage.setItem(WATCHED_MACHINES_PREFKEY, JSON.stringify(data)); + if (arrayIndex !== -1) { + NotificationsManager.cancelScheduledNotification(data[arrayIndex].endNotificationID); + if (data[arrayIndex].reminderNotificationID !== null) + NotificationsManager.cancelScheduledNotification(data[arrayIndex].reminderNotificationID); + this.removeNotificationFromPrefs(arrayIndex); + } } } + saveNotificationToPrefs(machineId: string, endNotificationID: string, reminderNotificationID: string | null) { + let data = this.state.machinesWatched; + data.push({ + machineNumber: machineId, + endNotificationID: endNotificationID, + reminderNotificationID: reminderNotificationID + }); + this.updateNotificationPrefs(data); + } + + removeNotificationFromPrefs(index: number) { + let data = this.state.machinesWatched; + data.splice(index, 1); + this.updateNotificationPrefs(data); + } + + updateNotificationPrefs(data: Object) { + this.setState({machinesWatched: data}); + let prefKey = AsyncStorageManager.getInstance().preferences.proxiwashWatchedMachines.key; + AsyncStorageManager.getInstance().savePref(prefKey, JSON.stringify(data)); + } + /** * Checks whether the machine of the given ID has scheduled notifications * @@ -200,13 +222,15 @@ export default class ProxiwashScreen extends FetchedDataSectionList { title: i18n.t('proxiwashScreen.dryers'), icon: 'tumble-dryer', data: fetchedData.dryers === undefined ? [] : fetchedData.dryers, - extraData: super.state + extraData: super.state, + keyExtractor: this.getDryersKeyExtractor }, { title: i18n.t('proxiwashScreen.washers'), icon: 'washing-machine', data: fetchedData.washers === undefined ? [] : fetchedData.washers, - extraData: super.state + extraData: super.state, + keyExtractor: this.getWashersKeyExtractor }, ]; } @@ -215,7 +239,7 @@ export default class ProxiwashScreen extends FetchedDataSectionList { return true; } - showAlert(title : string, item : Object, remainingTime: number) { + showAlert(title: string, item: Object, remainingTime: number) { let buttons = [{text: i18n.t("proxiwashScreen.modal.ok")}]; let message = modalStateStrings[MACHINE_STATES[item.state]]; if (MACHINE_STATES[item.state] === MACHINE_STATES.FONCTIONNE) { @@ -278,7 +302,7 @@ export default class ProxiwashScreen extends FetchedDataSectionList { position: 'absolute', right: 0, width: item.donePercent !== '' ? (100 - parseInt(item.donePercent)).toString() + '%' : 0, - backgroundColor: ThemeManager.getInstance().getCurrentThemeVariables().containerBgColor + backgroundColor: ThemeManager.getCurrentThemeVariables().containerBgColor }}/> this.showAlert(machineName, item, remainingTime)} @@ -302,7 +326,7 @@ export default class ProxiwashScreen extends FetchedDataSectionList { {this.isMachineWatched(item.number) ? : ''} diff --git a/screens/SettingsScreen.js b/screens/SettingsScreen.js index e54175a..d6665ca 100644 --- a/screens/SettingsScreen.js +++ b/screens/SettingsScreen.js @@ -20,9 +20,7 @@ import ThemeManager from '../utils/ThemeManager'; import i18n from "i18n-js"; import {NavigationActions, StackActions} from "react-navigation"; import CustomMaterialIcon from "../components/CustomMaterialIcon"; -import {AsyncStorage} from 'react-native' - -const proxiwashNotifKey = "proxiwashNotifKey"; +import AsyncStorageManager from "../utils/AsyncStorageManager"; type Props = { navigation: Object, @@ -38,31 +36,18 @@ type State = { */ export default class SettingsScreen extends React.Component { state = { - nightMode: ThemeManager.getInstance().getNightMode(), - proxiwashNotifPickerSelected: "5" + nightMode: ThemeManager.getNightMode(), + proxiwashNotifPickerSelected: AsyncStorageManager.getInstance().preferences.proxiwashNotifications.current, }; - /** - * Gets FetchedData from preferences before rendering components - * - * @returns {Promise} - */ - async componentWillMount() { - let val = await AsyncStorage.getItem(proxiwashNotifKey); - if (val === null) - val = "5"; - this.setState({ - proxiwashNotifPickerSelected: val, - }); - } - /** * Save the value for the proxiwash reminder notification time * * @param value The value to store */ onProxiwashNotifPickerValueChange(value: string) { - AsyncStorage.setItem(proxiwashNotifKey, value); + let key = AsyncStorageManager.getInstance().preferences.proxiwashNotifications.key; + AsyncStorageManager.getInstance().savePref(key, value); this.setState({ proxiwashNotifPickerSelected: value }); diff --git a/utils/AsyncStorageManager.js b/utils/AsyncStorageManager.js new file mode 100644 index 0000000..9bc605a --- /dev/null +++ b/utils/AsyncStorageManager.js @@ -0,0 +1,66 @@ +// @flow + +import {AsyncStorage} from "react-native"; + +/** + * Static class used to manage preferences + */ + +export default class AsyncStorageManager { + + static instance: AsyncStorageManager | null = null; + + /** + * Get this class instance or create one if none is found + * @returns {ThemeManager} + */ + static getInstance(): AsyncStorageManager { + return AsyncStorageManager.instance === null ? + AsyncStorageManager.instance = new AsyncStorageManager() : + AsyncStorageManager.instance; + } + + + preferences = { + proxiwashNotifications: { + key: 'proxiwashNotifications', + default: '5', + current : '', + }, + proxiwashWatchedMachines : { + key: 'proxiwashWatchedMachines', + default: '[]', + current : '', + }, + nightMode: { + key: 'nightMode', + default : '0', + current : '', + } + }; + + async loadPreferences() { + let prefKeys = []; + // Get all available keys + for (let [key, value] of Object.entries(this.preferences)) { + prefKeys.push(value.key); + } + // Get corresponding values + let resultArray : Array> = await AsyncStorage.multiGet(prefKeys); + // Save those values for later use + for (let i = 0; i < resultArray.length; i++) { + let key : string = resultArray[i][0]; + let val : string | null = resultArray[i][1]; + if (val === null) + val = this.preferences[key].default; + this.preferences[key].current = val; + } + console.log(this.preferences); + } + + savePref(key : string, val : string) { + this.preferences[key].current = val; + AsyncStorage.setItem(key, val); + } + +} diff --git a/utils/NotificationsManager.js b/utils/NotificationsManager.js index 608343e..978fcd9 100644 --- a/utils/NotificationsManager.js +++ b/utils/NotificationsManager.js @@ -46,7 +46,7 @@ export default class NotificationsManager { * @param time Time at which we should send the notification * @returns {Promise} Notification Id */ - static async scheduleNotification(title: string, body: string, time: number): Promise { + static async scheduleNotification(title: string, body: string, time: number): Promise { await NotificationsManager.askPermissions(); return Notifications.scheduleLocalNotificationAsync( { diff --git a/utils/ThemeManager.js b/utils/ThemeManager.js index 2ef4289..18252cf 100644 --- a/utils/ThemeManager.js +++ b/utils/ThemeManager.js @@ -1,23 +1,18 @@ // @flow -import {AsyncStorage} from 'react-native' import platform from '../native-base-theme/variables/platform'; import platformDark from '../native-base-theme/variables/platformDark'; import getTheme from '../native-base-theme/components'; - -const nightModeKey = 'nightMode'; - +import AsyncStorageManager from "./AsyncStorageManager"; /** * Singleton class used to manage themes */ export default class ThemeManager { static instance: ThemeManager | null = null; - nightMode: boolean; updateThemeCallback: Function; constructor() { - this.nightMode = false; this.updateThemeCallback = null; } @@ -39,26 +34,14 @@ export default class ThemeManager { this.updateThemeCallback = callback; } - /** - * Read async storage to get preferences - * @returns {Promise} - */ - async getDataFromPreferences(): Promise { - let result: string = await AsyncStorage.getItem(nightModeKey); - - if (result === '1') - this.nightMode = true; - // console.log('nightmode: ' + this.nightMode); - } - /** * Set night mode and save it to preferences * * @param isNightMode Whether to enable night mode */ setNightMode(isNightMode: boolean) { - this.nightMode = isNightMode; - AsyncStorage.setItem(nightModeKey, isNightMode ? '1' : '0'); + let nightModeKey = AsyncStorageManager.getInstance().preferences.nightMode.key; + AsyncStorageManager.getInstance().savePref(nightModeKey, isNightMode ? '1' : '0'); if (this.updateThemeCallback !== null) this.updateThemeCallback(); } @@ -66,16 +49,16 @@ export default class ThemeManager { /** * @returns {boolean} Night mode state */ - getNightMode(): boolean { - return this.nightMode; + static getNightMode(): boolean { + return AsyncStorageManager.getInstance().preferences.nightMode.current === '1'; } /** * Get the current theme based on night mode * @returns {Object} */ - getCurrentTheme(): Object { - if (this.nightMode) + static getCurrentTheme(): Object { + if (ThemeManager.getNightMode()) return getTheme(platformDark); else return getTheme(platform); @@ -85,8 +68,8 @@ export default class ThemeManager { * Get the variables contained in the current theme * @returns {Object} */ - getCurrentThemeVariables(): Object { - return this.getCurrentTheme().variables; + static getCurrentThemeVariables(): Object { + return ThemeManager.getCurrentTheme().variables; } };