diff --git a/ReactNativeClient/lib/models/Alarm.js b/ReactNativeClient/lib/models/Alarm.js index 30e0fdc0ad..698762f3d8 100644 --- a/ReactNativeClient/lib/models/Alarm.js +++ b/ReactNativeClient/lib/models/Alarm.js @@ -26,7 +26,13 @@ class Alarm extends BaseModel { } static async makeNotification(alarm, note = null) { - if (!note) note = await Note.load(alarm.note_id); + if (!note) { + note = await Note.load(alarm.note_id); + } else if (!note.todo_due) { + this.logger().warn('Trying to create notification for note with todo_due property - reloading note object in case we are dealing with a partial note'); + note = await Note.load(alarm.note_id); + this.logger().warn('Reloaded note:', note); + } const output = { id: alarm.id, diff --git a/ReactNativeClient/lib/services/AlarmService.js b/ReactNativeClient/lib/services/AlarmService.js index 0134e8a404..11f439b065 100644 --- a/ReactNativeClient/lib/services/AlarmService.js +++ b/ReactNativeClient/lib/services/AlarmService.js @@ -43,65 +43,69 @@ class AlarmService { // When passing a note, make sure it has all the required properties // (better to pass a complete note or else just the ID) static async updateNoteNotification(noteOrId, isDeleted = false) { - let note = null; - let noteId = null; + try { + let note = null; + let noteId = null; - if (typeof noteOrId === 'object') { - note = noteOrId; - noteId = note.id; - } else { - note = await Note.load(noteOrId); - noteId = note ? note.id : null; - } - - if (!note && !isDeleted) return; - - const driver = this.driver(); - - let alarm = noteId ? await Alarm.byNoteId(noteId) : null; - let clearAlarm = false; - - if (isDeleted || - !Note.needAlarm(note) || - (alarm && alarm.trigger_time !== note.todo_due)) - { - clearAlarm = !!alarm; - } - - if (!clearAlarm && alarm) { // Alarm already exists and set at the right time - - // For persistent notifications (those that stay active after the app has been closed, like on mobile), if we have - // an alarm object we can be sure that the notification has already been set, so there's nothing to do. - // For non-persistent notifications however we need to check that the notification has been set because, for example, - // if the app has just started the notifications need to be set again. so we do this below. - if (!driver.hasPersistentNotifications() && !driver.notificationIsSet(alarm.id)) { - const notification = await Alarm.makeNotification(alarm, note); - this.logger().info('Scheduling (non-persistent) notification for note ' + note.id, notification); - driver.scheduleNotification(notification); + if (typeof noteOrId === 'object') { + note = noteOrId; + noteId = note.id; + } else { + note = await Note.load(noteOrId); + noteId = note ? note.id : null; } - return; + if (!note && !isDeleted) return; + + const driver = this.driver(); + + let alarm = noteId ? await Alarm.byNoteId(noteId) : null; + let clearAlarm = false; + + if (isDeleted || + !Note.needAlarm(note) || + (alarm && alarm.trigger_time !== note.todo_due)) + { + clearAlarm = !!alarm; + } + + if (!clearAlarm && alarm) { // Alarm already exists and set at the right time + + // For persistent notifications (those that stay active after the app has been closed, like on mobile), if we have + // an alarm object we can be sure that the notification has already been set, so there's nothing to do. + // For non-persistent notifications however we need to check that the notification has been set because, for example, + // if the app has just started the notifications need to be set again. so we do this below. + if (!driver.hasPersistentNotifications() && !driver.notificationIsSet(alarm.id)) { + const notification = await Alarm.makeNotification(alarm, note); + this.logger().info('Scheduling (non-persistent) notification for note ' + note.id, notification); + driver.scheduleNotification(notification); + } + + return; + } + + if (clearAlarm) { + this.logger().info('Clearing notification for note ' + noteId); + await driver.clearNotification(alarm.id); + await Alarm.delete(alarm.id); + } + + if (isDeleted || !Note.needAlarm(note)) return; + + await Alarm.save({ + note_id: note.id, + trigger_time: note.todo_due, + }); + + // Reload alarm to get its ID + alarm = await Alarm.byNoteId(note.id); + + const notification = await Alarm.makeNotification(alarm, note); + this.logger().info('Scheduling notification for note ' + note.id, notification); + await driver.scheduleNotification(notification); + } catch (error) { + this.logger().error('Could not update notification', error); } - - if (clearAlarm) { - this.logger().info('Clearing notification for note ' + noteId); - await driver.clearNotification(alarm.id); - await Alarm.delete(alarm.id); - } - - if (isDeleted || !Note.needAlarm(note)) return; - - await Alarm.save({ - note_id: note.id, - trigger_time: note.todo_due, - }); - - // Reload alarm to get its ID - alarm = await Alarm.byNoteId(note.id); - - const notification = await Alarm.makeNotification(alarm, note); - this.logger().info('Scheduling notification for note ' + note.id, notification); - await driver.scheduleNotification(notification); } static async updateAllNotifications() { diff --git a/ReactNativeClient/lib/services/AlarmServiceDriverNode.js b/ReactNativeClient/lib/services/AlarmServiceDriverNode.js index d93b2a4140..8427d4a55c 100644 --- a/ReactNativeClient/lib/services/AlarmServiceDriverNode.js +++ b/ReactNativeClient/lib/services/AlarmServiceDriverNode.js @@ -28,6 +28,10 @@ class AlarmServiceDriverNode { const interval = notification.date.getTime() - now; if (interval < 0) return; + if (isNaN(interval)) { + throw new Error('Trying to create a notification from an invalid object: ' + JSON.stringify(notification)); + } + const timeoutId = setTimeout(() => { const o = { appName: this.appName_,