diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 1917fa4..44bbc1d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,7 +8,6 @@ - - - - - - - + + + + + Change the resource name to your App's accent color - or any other color you want + + + - + + + + android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerService" + android:exported="false" > - + - - + diff --git a/android/app/src/main/java/fr/amicaleinsat/application/MainActivity.java b/android/app/src/main/java/fr/amicaleinsat/application/MainActivity.java index d0b0cbf..e853f40 100644 --- a/android/app/src/main/java/fr/amicaleinsat/application/MainActivity.java +++ b/android/app/src/main/java/fr/amicaleinsat/application/MainActivity.java @@ -5,22 +5,11 @@ import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; import com.facebook.react.ReactRootView; import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView; -import android.content.Intent; -import android.content.res.Configuration; import org.devio.rn.splashscreen.SplashScreen; public class MainActivity extends ReactActivity { - // Added automatically by Expo Config - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - Intent intent = new Intent("onConfigurationChanged"); - intent.putExtra("newConfig", newConfig); - sendBroadcast(intent); - } - @Override protected void onCreate(Bundle savedInstanceState) { SplashScreen.show(this, R.style.SplashScreenTheme); diff --git a/android/app/src/release/AndroidManifest.xml b/android/app/src/release/AndroidManifest.xml index f51a0e2..a476d1a 100644 --- a/android/app/src/release/AndroidManifest.xml +++ b/android/app/src/release/AndroidManifest.xml @@ -21,7 +21,7 @@ - + diff --git a/package-lock.json b/package-lock.json index 14ddce0..0843a64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2530,6 +2530,12 @@ "@types/xdate": "*" } }, + "@types/react-native-push-notification": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/react-native-push-notification/-/react-native-push-notification-7.2.0.tgz", + "integrity": "sha512-4kErWFa0qit8qzPB6Nbp7kG9NiwDyKu5XxrNlrCIc1zoFxu48ABeofVvNCKv2RtlmFvCftibtykeysRZCeuT8A==", + "dev": true + }, "@types/react-native-vector-icons": { "version": "6.4.6", "resolved": "https://registry.npmjs.org/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.6.tgz", @@ -10684,12 +10690,9 @@ "integrity": "sha512-8xiEnU29qHZcT05XXwhPHiLChTt82Pn5Z/nFdDOYGNFZ+IYSbYeGmIxFpratCRO6dgLptNaDFDPiyw2X7UZTeg==" }, "react-native-push-notification": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-native-push-notification/-/react-native-push-notification-5.1.1.tgz", - "integrity": "sha512-CJmKqzM2P/s+a9PImoaiUN4TP1+K4YfmG1B0uUbavgFdGhTtRPTLLwDfFk2h3J6VmTXNak82rUz2iGwyptHm5w==", - "requires": { - "@react-native-community/push-notification-ios": "^1.4.0" - } + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/react-native-push-notification/-/react-native-push-notification-7.3.0.tgz", + "integrity": "sha512-Ofy8dYAhIkFJKxQDvAn7BnXxwtun1SMnqLjZUhRTRzhPEqN0tW7TmIjfyYanNf/lnggRYZqFO+b14Ul3nhlMGw==" }, "react-native-reanimated": { "version": "1.13.2", diff --git a/package.json b/package.json index f159022..beaa3fa 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "react-native-modalize": "2.0.8", "react-native-paper": "4.8.1", "react-native-permissions": "3.0.3", - "react-native-push-notification": "5.1.1", + "react-native-push-notification": "7.3.0", "react-native-reanimated": "1.13.2", "react-native-render-html": "5.1.0", "react-native-safe-area-context": "3.2.0", @@ -65,6 +65,7 @@ "@types/react": "17.0.3", "@types/react-native": "0.64.4", "@types/react-native-calendars": "1.20.10", + "@types/react-native-push-notification": "^7.2.0", "@types/react-native-vector-icons": "6.4.6", "@types/react-test-renderer": "17.0.1", "@typescript-eslint/eslint-plugin": "4.22.1", @@ -94,7 +95,9 @@ "rules": { "no-undef": 0, "no-shadow": "off", - "@typescript-eslint/no-shadow": ["error"], + "@typescript-eslint/no-shadow": [ + "error" + ], "prettier/prettier": [ "error", { diff --git a/src/components/Overrides/CustomModal.tsx b/src/components/Overrides/CustomModal.tsx index 571d4ee..4da03f2 100644 --- a/src/components/Overrides/CustomModal.tsx +++ b/src/components/Overrides/CustomModal.tsx @@ -17,27 +17,28 @@ * along with Campus INSAT. If not, see . */ -import * as React from 'react'; +import React, { Ref } from 'react'; import { useTheme } from 'react-native-paper'; import { Modalize } from 'react-native-modalize'; import { View } from 'react-native-animatable'; import { TAB_BAR_HEIGHT } from '../Tabbar/CustomTabBar'; +type Props = { + children?: React.ReactChild | null; +}; + /** * Abstraction layer for Modalize component, using custom configuration * * @param props Props to pass to the element. Must specify an onRef prop to get an Modalize ref. * @return {*} */ -function CustomModal(props: { - onRef: (re: Modalize) => void; - children?: React.ReactNode; -}) { +function CustomModal(props: Props, ref?: Ref) { const theme = useTheme(); - const { onRef, children } = props; + const { children } = props; return ( . */ -import * as React from 'react'; +import React, { useCallback, useLayoutEffect, useRef, useState } from 'react'; import { - Alert, SectionListData, SectionListRenderItemInfo, StyleSheet, View, } from 'react-native'; import i18n from 'i18n-js'; -import { Avatar, Button, Card, Text, withTheme } from 'react-native-paper'; -import { StackNavigationProp } from '@react-navigation/stack'; +import { Avatar, Button, Card, Text, useTheme } from 'react-native-paper'; import { Modalize } from 'react-native-modalize'; import WebSectionList from '../../components/Screens/WebSectionList'; -import * as Notifications from '../../utils/Notifications'; import AsyncStorageManager from '../../managers/AsyncStorageManager'; import ProxiwashListItem from '../../components/Lists/Proxiwash/ProxiwashListItem'; import ProxiwashConstants, { @@ -53,6 +50,8 @@ 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 { setupMachineNotification } from '../../utils/Notifications'; const REFRESH_TIME = 1000 * 10; // Refresh every 10 seconds const LIST_ITEM_HEIGHT = 64; @@ -68,17 +67,6 @@ export type ProxiwashMachineType = { program: string; }; -type PropsType = { - navigation: StackNavigationProp; - theme: ReactNativePaper.Theme; -}; - -type StateType = { - modalCurrentDisplayItem: React.ReactNode; - machinesWatched: Array; - selectedWash: string; -}; - type FetchedDataType = { dryers: Array; washers: Array; @@ -99,22 +87,28 @@ const styles = StyleSheet.create({ }, }); -/** - * Class defining the app's proxiwash screen. This screen shows information about washing machines and - * dryers, taken from a scrapper reading proxiwash website - */ -class ProxiwashScreen extends React.Component { - /** - * Shows a warning telling the user notifications are disabled for the app - */ - static showNotificationsDisabledWarning() { - Alert.alert( - i18n.t('screens.proxiwash.modal.notificationErrorTitle'), - i18n.t('screens.proxiwash.modal.notificationErrorDescription') - ); - } +function ProxiwashScreen() { + const navigation = useNavigation(); + const theme = useTheme(); + const [ + modalCurrentDisplayItem, + setModalCurrentDisplayItem, + ] = useState(null); + const [machinesWatched, setMachinesWatched] = useState< + Array + >( + AsyncStorageManager.getObject( + AsyncStorageManager.PREFERENCES.proxiwashWatchedMachines.key + ) + ); - modalStateStrings: { [key in MachineStates]: string } = { + const [selectedWash, setSelectedWash] = useState( + AsyncStorageManager.getString( + AsyncStorageManager.PREFERENCES.selectedWash.key + ) + ); + + const modalStateStrings: { [key in MachineStates]: string } = { [MachineStates.AVAILABLE]: i18n.t('screens.proxiwash.modal.ready'), [MachineStates.RUNNING]: i18n.t('screens.proxiwash.modal.running'), [MachineStates.RUNNING_NOT_STARTED]: i18n.t( @@ -126,95 +120,48 @@ class ProxiwashScreen extends React.Component { [MachineStates.UNKNOWN]: i18n.t('screens.proxiwash.modal.unknown'), }; - modalRef: null | Modalize; + const modalRef = useRef(null); - fetchedData: { - dryers: Array; - washers: Array; - }; - - /** - * Creates machine state parameters using current theme and translations - */ - constructor(props: PropsType) { - super(props); - this.modalRef = null; - this.fetchedData = { dryers: [], washers: [] }; - this.state = { - modalCurrentDisplayItem: null, - machinesWatched: AsyncStorageManager.getObject( - AsyncStorageManager.PREFERENCES.proxiwashWatchedMachines.key - ), - selectedWash: AsyncStorageManager.getString( - AsyncStorageManager.PREFERENCES.selectedWash.key - ), - }; - } - - /** - * Setup notification channel for android and add listeners to detect notifications fired - */ - componentDidMount() { - const { navigation } = this.props; + useLayoutEffect(() => { navigation.setOptions({ headerRight: () => ( navigation.navigate('settings')} + title={'switch'} + iconName={'swap-horizontal'} + onPress={() => navigation.navigate('settings')} /> navigation.navigate('proxiwash-about')} /> ), }); - navigation.addListener('focus', this.onScreenFocus); - } + }, [navigation]); - onScreenFocus = () => { - const { state } = this; - const selected = AsyncStorageManager.getString( - AsyncStorageManager.PREFERENCES.selectedWash.key - ); - if (selected !== state.selectedWash) { - this.setState({ - selectedWash: selected, - }); - } - }; - - /** - * Callback used when pressing the about button. - * This will open the ProxiwashAboutScreen. - */ - onAboutPress = () => { - const { navigation } = this.props; - navigation.navigate('proxiwash-about'); - }; + useFocusEffect( + useCallback(() => { + const selected = AsyncStorageManager.getString( + AsyncStorageManager.PREFERENCES.selectedWash.key + ); + if (selected !== selectedWash) { + setSelectedWash(selected); + } + }, [selectedWash]) + ); /** * Callback used when the user clicks on enable notifications for a machine * * @param machine The machine to set notifications for */ - onSetupNotificationsPress(machine: ProxiwashMachineType) { - if (this.modalRef) { - this.modalRef.close(); + const onSetupNotificationsPress = (machine: ProxiwashMachineType) => { + if (modalRef.current) { + modalRef.current.close(); } - this.setupNotifications(machine); - } - - /** - * Callback used when receiving modal ref - * - * @param ref - */ - onModalRef = (ref: Modalize) => { - this.modalRef = ref; + setupNotifications(machine); }; /** @@ -226,11 +173,14 @@ class ProxiwashScreen extends React.Component { * @param isDryer True if the given item is a dryer * @return {*} */ - getModalContent(title: string, item: ProxiwashMachineType, isDryer: boolean) { - const { props, state } = this; + const getModalContent = ( + title: string, + item: ProxiwashMachineType, + isDryer: boolean + ) => { let button: { text: string; icon: string; onPress: () => void } | undefined; - let message = this.modalStateStrings[item.state]; - const onPress = () => this.onSetupNotificationsPress(item); + let message = modalStateStrings[item.state]; + const onPress = () => onSetupNotificationsPress(item); if (item.state === MachineStates.RUNNING) { let remainingTime = parseInt(item.remainingTime, 10); if (remainingTime < 0) { @@ -238,7 +188,7 @@ class ProxiwashScreen extends React.Component { } button = { - text: isMachineWatched(item, state.machinesWatched) + text: isMachineWatched(item, machinesWatched) ? i18n.t('screens.proxiwash.modal.disableNotifications') : i18n.t('screens.proxiwash.modal.enableNotifications'), icon: '', @@ -258,7 +208,7 @@ class ProxiwashScreen extends React.Component { left={() => ( )} @@ -281,7 +231,7 @@ class ProxiwashScreen extends React.Component { ) : null} ); - } + }; /** * Gets the section render item @@ -289,13 +239,13 @@ class ProxiwashScreen extends React.Component { * @param section The section to render * @return {*} */ - getRenderSectionHeader = ({ + const getRenderSectionHeader = ({ section, }: { section: SectionListData; }) => { const isDryer = section.title === i18n.t('screens.proxiwash.dryers'); - const nbAvailable = this.getMachineAvailableNumber(isDryer); + const nbAvailable = getMachineAvailableNumber(section.data); return ( { * @param section The object describing the current SectionList section * @returns {React.Node} */ - getRenderItem = (data: SectionListRenderItemInfo) => { - const { machinesWatched } = this.state; + const getRenderItem = ( + data: SectionListRenderItemInfo + ) => { const isDryer = data.section.title === i18n.t('screens.proxiwash.dryers'); return ( { * @param item The item to extract the key from * @return {*} The extracted key */ - getKeyExtractor = (item: ProxiwashMachineType): string => item.number; + const getKeyExtractor = (item: ProxiwashMachineType): string => item.number; /** * Setups notifications for the machine with the given ID. @@ -341,28 +292,19 @@ class ProxiwashScreen extends React.Component { * * @param machine The machine to watch */ - setupNotifications(machine: ProxiwashMachineType) { - const { machinesWatched } = this.state; + const setupNotifications = (machine: ProxiwashMachineType) => { if (!isMachineWatched(machine, machinesWatched)) { - Notifications.setupMachineNotification( + setupMachineNotification( machine.number, true, getMachineEndDate(machine) - ) - .then(() => { - this.saveNotificationToState(machine); - }) - .catch(() => { - ProxiwashScreen.showNotificationsDisabledWarning(); - }); - } else { - Notifications.setupMachineNotification(machine.number, false, null).then( - () => { - this.removeNotificationFromState(machine); - } ); + saveNotificationToState(machine); + } else { + setupMachineNotification(machine.number, false); + removeNotificationFromState(machine); } - } + }; /** * Gets the number of machines available @@ -370,13 +312,9 @@ class ProxiwashScreen extends React.Component { * @param isDryer True if we are only checking for dryer, false for washers * @return {number} The number of machines available */ - getMachineAvailableNumber(isDryer: boolean): number { - let data; - if (isDryer) { - data = this.fetchedData.dryers; - } else { - data = this.fetchedData.washers; - } + const getMachineAvailableNumber = ( + data: ReadonlyArray + ): number => { let count = 0; data.forEach((machine: ProxiwashMachineType) => { if (machine.state === MachineStates.AVAILABLE) { @@ -384,7 +322,7 @@ class ProxiwashScreen extends React.Component { } }); return count; - } + }; /** * Creates the dataset to be used by the FlatList @@ -392,10 +330,9 @@ class ProxiwashScreen extends React.Component { * @param fetchedData * @return {*} */ - createDataset = ( + const createDataset = ( fetchedData: FetchedDataType | undefined ): SectionListDataType => { - const { state } = this; if (fetchedData) { let data = fetchedData; if (AprilFoolsManager.getInstance().isAprilFoolsEnabled()) { @@ -403,24 +340,26 @@ class ProxiwashScreen extends React.Component { AprilFoolsManager.getNewProxiwashDryerOrderedList(data.dryers); AprilFoolsManager.getNewProxiwashWasherOrderedList(data.washers); } - this.fetchedData = data; - // TODO dirty, should be refactored - this.state.machinesWatched = getCleanedMachineWatched( - state.machinesWatched, - [...data.dryers, ...data.washers] - ); + fetchedData = data; + const cleanedList = getCleanedMachineWatched(machinesWatched, [ + ...data.dryers, + ...data.washers, + ]); + if (cleanedList !== machinesWatched) { + setMachinesWatched(machinesWatched); + } return [ { title: i18n.t('screens.proxiwash.dryers'), icon: 'tumble-dryer', data: data.dryers === undefined ? [] : data.dryers, - keyExtractor: this.getKeyExtractor, + keyExtractor: getKeyExtractor, }, { title: i18n.t('screens.proxiwash.washers'), icon: 'washing-machine', data: data.washers === undefined ? [] : data.washers, - keyExtractor: this.getKeyExtractor, + keyExtractor: getKeyExtractor, }, ]; } else { @@ -428,15 +367,6 @@ class ProxiwashScreen extends React.Component { } }; - /** - * Callback used when the user clicks on the navigate to settings button. - * This will hide the banner and open the SettingsScreen - */ - onGoToSettings = () => { - const { navigation } = this.props; - navigation.navigate('settings'); - }; - /** * Shows a modal for the given item * @@ -444,12 +374,14 @@ class ProxiwashScreen extends React.Component { * @param item The item to display information for in the modal * @param isDryer True if the given item is a dryer */ - showModal = (title: string, item: ProxiwashMachineType, isDryer: boolean) => { - this.setState({ - modalCurrentDisplayItem: this.getModalContent(title, item, isDryer), - }); - if (this.modalRef) { - this.modalRef.open(); + const showModal = ( + title: string, + item: ProxiwashMachineType, + isDryer: boolean + ) => { + setModalCurrentDisplayItem(getModalContent(title, item, isDryer)); + if (modalRef.current) { + modalRef.current.open(); } }; @@ -458,87 +390,76 @@ class ProxiwashScreen extends React.Component { * * @param machine */ - saveNotificationToState(machine: ProxiwashMachineType) { - const { machinesWatched } = this.state; - const data = machinesWatched; + const saveNotificationToState = (machine: ProxiwashMachineType) => { + let data = [...machinesWatched]; data.push(machine); - this.saveNewWatchedList(data); - } + saveNewWatchedList(data); + }; /** * Removes the given index from the watchlist array and saves it to preferences * * @param selectedMachine */ - removeNotificationFromState(selectedMachine: ProxiwashMachineType) { - const { machinesWatched } = this.state; - const newList = [...machinesWatched]; - machinesWatched.forEach((machine: ProxiwashMachineType, index: number) => { - if ( - machine.number === selectedMachine.number && - machine.endTime === selectedMachine.endTime - ) { - newList.splice(index, 1); - } - }); - this.saveNewWatchedList(newList); - } + const removeNotificationFromState = ( + selectedMachine: ProxiwashMachineType + ) => { + const newList = machinesWatched.filter( + (m) => m.number !== selectedMachine.number + ); + saveNewWatchedList(newList); + }; - saveNewWatchedList(list: Array) { - this.setState({ machinesWatched: list }); + const saveNewWatchedList = (list: Array) => { + setMachinesWatched(list); AsyncStorageManager.set( AsyncStorageManager.PREFERENCES.proxiwashWatchedMachines.key, list ); - } + }; - render() { - const { state } = this; - let data: LaundromatType; - switch (state.selectedWash) { - case 'tripodeB': - data = ProxiwashConstants.tripodeB; - break; - default: - data = ProxiwashConstants.washinsa; - } - return ( - - - readData(data.url)} - createDataset={this.createDataset} - renderItem={this.getRenderItem} - renderSectionHeader={this.getRenderSectionHeader} - autoRefreshTime={REFRESH_TIME} - refreshOnFocus={true} - extraData={state.machinesWatched.length} - /> - - - - {state.modalCurrentDisplayItem} - - - ); + let data: LaundromatType; + switch (selectedWash) { + case 'tripodeB': + data = ProxiwashConstants.tripodeB; + break; + default: + data = ProxiwashConstants.washinsa; } + return ( + + + readData(data.url)} + createDataset={createDataset} + renderItem={getRenderItem} + renderSectionHeader={getRenderSectionHeader} + autoRefreshTime={REFRESH_TIME} + refreshOnFocus={true} + extraData={machinesWatched.length} + /> + + navigation.navigate('settings'), + }, + cancel: { + message: i18n.t('screens.proxiwash.mascotDialog.cancel'), + icon: 'close', + }, + }} + emotion={MASCOT_STYLE.NORMAL} + /> + {modalCurrentDisplayItem} + + ); } -export default withTheme(ProxiwashScreen); +export default ProxiwashScreen; diff --git a/src/utils/Notifications.ts b/src/utils/Notifications.ts index 6262797..17a441a 100644 --- a/src/utils/Notifications.ts +++ b/src/utils/Notifications.ts @@ -17,44 +17,59 @@ * along with Campus INSAT. If not, see . */ -import { - checkNotifications, - requestNotifications, - RESULTS, -} from 'react-native-permissions'; import i18n from 'i18n-js'; import AsyncStorageManager from '../managers/AsyncStorageManager'; - -const PushNotification = require('react-native-push-notification'); +import PushNotificationIOS from '@react-native-community/push-notification-ios'; +import PushNotification from 'react-native-push-notification'; +import { Platform } from 'react-native'; // Used to multiply the normal notification id to create the reminder one. It allows to find it back easily const reminderIdFactor = 100; -/** - * Async function asking permission to send notifications to the user. - * Used on ios. - * - * @returns {Promise} - */ -export async function askPermissions(): Promise { - return new Promise((resolve: () => void, reject: () => void) => { - checkNotifications().then(({ status }: { status: string }) => { - if (status === RESULTS.GRANTED) { - resolve(); - } else if (status === RESULTS.BLOCKED) { - reject(); - } else { - requestNotifications([]).then((result: { status: string }) => { - if (result.status === RESULTS.GRANTED) { - resolve(); - } else { - reject(); - } - }); - } - }); - }); -} +PushNotification.createChannel( + { + channelId: 'reminders', // (required) + channelName: 'Reminders', // (required) + channelDescription: 'Get laundry reminders', // (optional) default: undefined. + playSound: true, // (optional) default: true + soundName: 'default', // (optional) See `soundName` parameter of `localNotification` function + importance: 4, // (optional) default: 4. Int value of the Android notification importance + vibrate: true, // (optional) default: true. Creates the default vibration patten if true. + }, + (created) => console.log(`createChannel returned '${created}'`) // (optional) callback returns whether the channel was created, false means it already existed. +); + +PushNotification.configure({ + // (required) Called when a remote is received or opened, or local notification is opened + onNotification: function (notification) { + console.log('NOTIFICATION:', notification); + + // process the notification + + // (required) Called when a remote is received or opened, or local notification is opened + notification.finish(PushNotificationIOS.FetchResult.NoData); + }, + + // IOS ONLY (optional): default: all - Permissions to register. + permissions: { + alert: true, + badge: true, + sound: true, + }, + + // Should the initial notification be popped automatically + // default: true + popInitialNotification: true, + + /** + * (optional) default: true + * - Specified if permissions (ios) and token (android and ios) will requested or not, + * - if not, you must call PushNotificationsHandler.requestPermissions() later + * - if you are not using remote notification or do not have Firebase installed, use this: + * requestPermissions: Platform.OS === 'ios' + */ + requestPermissions: Platform.OS === 'ios', +}); /** * Creates a notification for the given machine id at the given date. @@ -79,7 +94,7 @@ function createNotifications(machineID: string, date: Date) { message: i18n.t('screens.proxiwash.notifications.machineRunningBody', { number: machineID, }), - id: id.toString(), + id: id, date: reminderDate, }); } @@ -89,7 +104,7 @@ function createNotifications(machineID: string, date: Date) { message: i18n.t('screens.proxiwash.notifications.machineFinishedBody', { number: machineID, }), - id: machineID, + id: parseInt(machineID, 10), date, }); } @@ -104,26 +119,16 @@ function createNotifications(machineID: string, date: Date) { * @param isEnabled True to enable notifications, false to disable * @param endDate The trigger date, or null if disabling notifications */ -export async function setupMachineNotification( +export function setupMachineNotification( machineID: string, isEnabled: boolean, - endDate: Date | null -): Promise { - return new Promise((resolve: () => void, reject: () => void) => { - if (isEnabled && endDate != null) { - askPermissions() - .then(() => { - createNotifications(machineID, endDate); - resolve(); - }) - .catch(() => { - reject(); - }); - } else { - PushNotification.cancelLocalNotifications({ id: machineID }); - const reminderId = reminderIdFactor * parseInt(machineID, 10); - PushNotification.cancelLocalNotifications({ id: reminderId.toString() }); - resolve(); - } - }); + endDate?: Date | null +) { + if (isEnabled && endDate) { + createNotifications(machineID, endDate); + } else { + PushNotification.cancelLocalNotifications({ id: machineID }); + const reminderId = reminderIdFactor * parseInt(machineID, 10); + PushNotification.cancelLocalNotifications({ id: reminderId.toString() }); + } }