React Native Alarm Notification: Alarms set successfully but notification is comes only once for multiple alarms

Viewed 1192

Basically, I am working on an application which reminds a patient the time schedule for his eye drops. A user can set multiple alarms for a given day or multiple days. Alarms should be called noOfDays*howOftenPerDay times. In my code

  • When a user sets one alarm, we can get it from the ReactNativeAN.getScheduledAlarms(), the alarm rings and it works fine
  • When a user sets two alarms, we can get it from the ReactNativeAN.getScheduledAlarms(), the second alarm rings only but not the first one
  • When a user sets three alarms, we can get it from the ReactNativeAN.getScheduledAlarms(), the 3rd alarm only rings but not the first two
  • I tried using

channel: Math.floor(Math.random() * (i + 1)),

I thought it maybe a channel issue but it didn't work

Can someone please help me fix this issue? Any help would be highly appreciated and here is the full piece of code:

import React, { Component } from 'react'
import { Picker, Text, View, Image, Button, Alert } from 'react-native'
import CheckBox from '@react-native-community/checkbox'
import DatePicker from 'react-native-datepicker'
import { v4 as uuidv4 } from 'uuid';
import DateTimePickerModal from "react-native-modal-datetime-picker";
import ReactNativeAN from 'react-native-alarm-notification';
import moment from 'moment'

import styles from './style'

export default class AddDropForm extends Component {
    constructor(props) {
        super(props)
        this.state = {
            date: new Date(),
            mode: 'date',
            show: false,
            alarmComponent: [],
            isDatePickerVisible: false,
            time: new Date(),
            dayPickerShow: false,
            oftenPickerShow: false,
            taperPickerShow: false,
            noOfDays: 1,
            howOften: 1,
            taper: 1,
            isLeftSelected: false,
            isRightSelected: false,
            isBothSelected: false,
            timeArray: []
        }
    }


    updateNoOfDays = (count) => {
        this.setState({ noOfDays: count })
    }
    updateHowOften = (count) => {
        this.setState({ howOften: count })
    }
    updateTaper = (count) => {
        this.setState({ taper: count })
    }

    showDatePicker = () => {
        this.setState({ isDatePickerVisible: true })
    };

    hideDatePicker = () => {
        this.setState({ isDatePickerVisible: false })
    };

    handleConfirm = (date) => {
        console.log("A date has been picked: ", date);
        this.setState({ timeArray: [...this.state.timeArray, date] })
        this.setState({ time: date })
        this.hideDatePicker();
    };

    async setAlarm(name) {
        let setTime = ''
        for (let i = 0; i < this.state.noOfDays; i++) {
            this.state.timeArray.forEach(async t => {
                let alarmData = {
                    title: `${name}`,
                    message: `Reminder for ${name}`,
                    channel: Math.floor(Math.random() * (i + 1)),
                    small_icon: "ic_launcher",
                    auto_cancel: true,
                    schedule_type: "repeat",
                    sound_name: 'alarm_quartz.mp3',
                    has_button: true
                };
                console.log("The set time is: " + setTime)
                setTime = moment(t).set({ second: 0, millisecond: 0 }).add(i, 'days').format('DD-MM-yyyy HH:mm:ss')
                await ReactNativeAN.scheduleAlarm({ ...alarmData, fire_date: setTime });
            })
        }
        Alert.alert('Alarms Set')
        this.props.navigation.navigate('Schedule')
    }

    formatDate() {
        let date = this.state.time
        var hours = date.getHours();
        var minutes = date.getMinutes();
        var ampm = hours >= 12 ? 'pm' : 'am';
        hours = hours % 12;
        hours = hours ? hours : 12;
        minutes = minutes < 10 ? '0' + minutes : minutes;
        var strTime = hours + ':' + minutes + ' ' + ampm;
        return strTime;
    }

    render() {
        const { dropName } = this.props.route.params
        let array = []
        for (let i = 1; i <= this.state.howOften; i++) {
            array.push(
                <View style={{ flex: 7 }}>
                    <Button title={"Set Time"} onPress={() => this.showDatePicker()} />
                    <DateTimePickerModal
                        isVisible={this.state.isDatePickerVisible}
                        mode="time"
                        onConfirm={this.handleConfirm}
                        onCancel={this.hideDatePicker}
                    />
                </View>
            )
        }
        // const { dropName } = this.props
        return (
            <View style={styles.container}>
                <View style={styles.topTitleContainer}>
                    <View style={{ flex: 2 }}>
                        <Image style={{ width: 70, height: 60 }} source={require('../../../assets/addDrop.png')} />
                    </View>
                    <View style={{ flex: 7 }}>
                        <Text style={styles.titleText}>
                            {dropName.name}
                        </Text>
                    </View>
                </View>
                <View style={styles.tilesContainer}>
                    <View style={{ flex: 2 }}>
                        <Text style={styles.subTitle}>Which Eye?</Text>
                    </View>
                    <View style={styles.checkBoxContainer}>
                        <Text style={styles.subTitle}>Left</Text>
                        <CheckBox
                            value={this.state.isLeftSelected}
                            onValueChange={(newValue) => this.setState({ isLeftSelected: newValue, isBothSelected: false, isRightSelected: false })
                            }
                            style={styles.checkbox}
                        />
                        <Text style={styles.subTitle}>Both</Text>
                        <CheckBox
                            value={this.state.isBothSelected}
                            onValueChange={(newValue) => this.setState({
                                isBothSelected: newValue, isLeftSelected: false,
                                isRightSelected: false
                            })
                            }
                            style={styles.checkbox}
                        />
                        <Text style={styles.subTitle}>Right</Text>
                        <CheckBox
                            value={this.state.isRightSelected}
                            onValueChange={(newValue) => this.setState({ isRightSelected: newValue, isLeftSelected: false, isBothSelected: false })
                            }
                            style={styles.checkbox}
                        />
                    </View>
                </View>
                <View style={styles.tilesContainer}>
                    <View style={{ flex: 2 }}>
                        <Text style={styles.subTitle}>Start Date?</Text>
                    </View>
                    <View style={{ flex: 7 }}>
                        <DatePicker
                            style={{ width: '100%' }}
                            date={this.state.date}
                            mode="date"
                            placeholder="select date"
                            format="YYYY-MM-DD"
                            minDate={new Date()}
                            confirmBtnText="Confirm"
                            cancelBtnText="Cancel"
                            customStyles={{
                                dateIcon: {
                                    position: 'absolute',
                                    left: 0,
                                    top: 4,
                                    marginLeft: 0
                                },
                                dateInput: {
                                    marginLeft: 36
                                }
                            }}
                            onDateChange={(date) => { this.setState({ date: date }) }}
                        />
                    </View>
                </View>
                <View style={styles.tilesContainer}>
                    <View style={{ flex: 2 }}>
                        <Text style={styles.subTitle}>Number of Days?</Text>
                    </View>
                    <View style={{ flex: 7 }}>
                        <Picker selectedValue={this.state.noOfDays} onValueChange={this.updateNoOfDays}>
                            ...
                        </Picker>
                    </View>
                </View>
                <View style={styles.tilesContainer}>
                    <View style={{ flex: 2 }}>
                        <Text style={styles.subTitle}>How Often?</Text>
                    </View>
                    <View style={{ flex: 7 }}>
                        <Picker selectedValue={this.state.howOften} onValueChange={this.updateHowOften}>
                           ...
                        </Picker>
                    </View>
                </View>
                <View style={styles.tilesContainer}>
                    <View style={{ flex: 2 }}>
                        <Text style={styles.subTitle}>Taper Drops?</Text>
                    </View>
                    <View style={{ flex: 7 }}>
                        <Picker selectedValue={this.state.taper} onValueChange={this.updateTaper}>
                           ...
                           
                        </Picker>
                    </View>
                </View>
                <View style={styles.tilesContainer}>
                    <View style={{ flex: 2 }}>
                        <Text style={styles.subTitle}>Alarm</Text>
                    </View>
                    {array}
                </View>
                <View style={{ justifyContent: 'center', alignItems: 'center' }}>
                    <View style={{ width: 260 }}>
                        <Button title={"Add"} color='#57E874' onPress={() => this.setAlarm(dropName.name)} />
                    </View>

                </View>
            </View>
        )
    }
}

3 Answers

i guess issue is in your channel, are you sure it must be different per alarm

I don't think the way you're using async await is optimal might be why it's causing you issues, You need to utilize the Promise.all method to take an array of promises and return an array with your scheduled Alarms once each individual call for ReactNativeAN.scheduleAlarm finishes.

Look at this example

Also I agree with the other comment I'm not sure the channel should be different for each alarm in the documentation it says Specifies the channel the notification should be delivered on.. so if the notifications are for the same user I think it should be the same id.

I believe I have found the issue. The library you are using is setting the alarmId to a timestamp.

When you run your for loop and you call ReactNativeAN.scheduleAlarm and the native function runs, NSDate.date.timeIntervalSince1970 returns the same value for each iteration which means each (previous) Alarm is getting overwritten.

Here's a simple function to simulate this theory.

const items = [1,2,3,4,5];

items.forEach(item => {
  console.log(Date.now()); // Same timestamp logged and therefore alarmId is not unique.
});

Frustratingly the library doesn't support alarmId as a property, you could forcibly add a wait so that the timestamp is different.

const items = [1,2,3,4,5];

items.forEach((item, index) => {
  setTimeout(() => {
    console.log(Date.now()); // alarmId will now be unique.
    // Code to call ReactNativeAN.scheduleAlarm
  }, index + 1);
});

For sanity sake, you can assign the value of ReactNativeAN.scheduleAlarm to a variable and log it out to make sure its the value you're expecting.

const alarm = await ReactNativeAN.scheduleAlarm(...);

console.log(alarm);
Related