diff --git a/src/components/AppNavigation/ListItemCalendar.vue b/src/components/AppNavigation/ListItemCalendar.vue index 36700f83e..e5116b95d 100644 --- a/src/components/AppNavigation/ListItemCalendar.vue +++ b/src/components/AppNavigation/ListItemCalendar.vue @@ -552,12 +552,6 @@ $color-error: #e9322d; display: none; position: relative; - &.error input.edit { - color: var(--color-error); - border-color: var(--color-error) !important; - box-shadow: 0 0 6px transparentize( $color-error, .7 ); - } - form { display: flex; diff --git a/src/components/AppSidebar/Alarm/AlarmList.vue b/src/components/AppSidebar/Alarm/AlarmList.vue index 029ca5ec7..ec3780479 100644 --- a/src/components/AppSidebar/Alarm/AlarmList.vue +++ b/src/components/AppSidebar/Alarm/AlarmList.vue @@ -9,7 +9,7 @@
-
+ + + diff --git a/src/models/alarm.js b/src/models/alarm.js index b10543976..2f932e6bc 100644 --- a/src/models/alarm.js +++ b/src/models/alarm.js @@ -8,6 +8,7 @@ import { getDateFromDateTimeValue, } from '../utils/alarms.js' import { AlarmComponent } from '@nextcloud/calendar-js' +import ICAL from 'ical.js' /** * Creates a complete alarm object based on given props @@ -97,6 +98,22 @@ const mapAlarmComponentToAlarmObject = (alarmComponent) => { } } +/** + * @param {Array} alarms ICAL.js alarms + */ +export function mapICALAlarmsToAlarmObjects(alarms) { + return alarms.map((alarm) => { + try { + return mapAlarmComponentToAlarmObject(AlarmComponent.fromICALJs(alarm)) + } catch (e) { + // Instead of breaking the whole page when parsing an invalid alarm, + // we just print a warning on the console. + console.warn(e) + return false + } + }).filter(Boolean) +} + /** * @param {number} time Total amount of seconds for the trigger * @param {boolean} relatedToStart If the alarm is related to the start of the event diff --git a/src/models/task.js b/src/models/task.js index dedbc0667..e3652b25b 100644 --- a/src/models/task.js +++ b/src/models/task.js @@ -626,18 +626,21 @@ export default class Task { /** * Remove an alarm * - * @param {number} index The index of the alarm-list + * @param {number[]} indexes The indexes of the alarm-list */ - removeAlarm(index) { + removeAlarm(indexes) { const valarms = this.vtodo.getAllSubcomponents('valarm') - const valarmToDelete = valarms[index] - if (valarmToDelete) { - this.vtodo.removeSubcomponent(valarms[index]) + for (const index of indexes) { + const valarmToDelete = valarms[index] - this.updateLastModified() - this._alarms = this.getAlarms() + if (valarmToDelete) { + this.vtodo.removeSubcomponent(valarmToDelete) + } } + + this.updateLastModified() + this._alarms = this.getAlarms() } /** diff --git a/src/store/tasks.js b/src/store/tasks.js index 362d0afff..4b33b7dc4 100644 --- a/src/store/tasks.js +++ b/src/store/tasks.js @@ -502,10 +502,10 @@ const mutations = { * @param {object} state The store data * @param {object} data Destructuring object * @param {Task} data.task The task - * @param {number} data.index The index of the alarm-item to remove + * @param {number[]} data.indexes The indexes of the alarm-items to remove */ - removeAlarm(state, { task, index }) { - task.removeAlarm(index) + removeAlarm(state, { task, indexes }) { + task.removeAlarm(indexes) }, /** @@ -1282,10 +1282,10 @@ const actions = { * Removes an alarm from a task * * @param {object} context The store context - * @param {Task} task The task to update + * @param {Task} task The task to remove */ - async removeAlarm(context, { task, index }) { - context.commit('removeAlarm', { task, index }) + async removeAlarm(context, { task, indexes }) { + context.commit('removeAlarm', { task, indexes }) context.dispatch('updateTask', task) }, diff --git a/src/utils/alarms.js b/src/utils/alarms.js index 88bbab968..6acbeda8f 100644 --- a/src/utils/alarms.js +++ b/src/utils/alarms.js @@ -258,3 +258,14 @@ export function getDateFromDateTimeValue(dateTimeValue) { 0, ) } + +/** + * Takes the related date and the relative trigger of an alarm and + * calculates the absolute date-time when the alarm will trigger. + * + * @param {Date} relatedDate Related date + * @param {number} relativeTrigger Relative trigger in seconds + */ +export function calculateAbsoluteDateFromRelativeTrigger(relatedDate, relativeTrigger) { + return new Date(((relatedDate.valueOf() / 1000) + relativeTrigger) * 1000) +} diff --git a/src/views/AppSidebar.vue b/src/views/AppSidebar.vue index 5e2e43019..ca93aff9e 100644 --- a/src/views/AppSidebar.vue +++ b/src/views/AppSidebar.vue @@ -234,14 +234,15 @@ License along with this library. If not, see . icon="TagMultiple" @add-tag="updateTag" @set-tags="updateTags" /> - + @remove-alarm="removeAlarmItem" + @restore-date="restoreDate"> @@ -282,7 +283,7 @@ import TagsItem from '../components/AppSidebar/TagsItem.vue' import TextItem from '../components/AppSidebar/TextItem.vue' import NotesItem from '../components/AppSidebar/NotesItem.vue' import TaskCheckbox from '../components/TaskCheckbox.vue' -// import TaskStatusDisplay from '../components/TaskStatusDisplay' +import { mapICALAlarmsToAlarmObjects } from '../models/alarm.js' import Task from '../models/task.js' import { startDateString, dueDateString } from '../utils/dateStrings.js' @@ -354,7 +355,6 @@ export default { CalendarPickerItem, NotesItem, TaskCheckbox, - // TaskStatusDisplay, }, /** * Before we navigate to a new task, we save possible edits to the task summary. @@ -410,6 +410,9 @@ export default { task() { return this.getTaskByRoute(this.$route) }, + alarms() { + return mapICALAlarmsToAlarmObjects(this.task.alarms) + }, summary() { return this.task ? this.task.summary : '' }, @@ -601,12 +604,6 @@ export default { return !!this.$store.state.settings.settings.allDay } }, - hasStartDate() { - return !!this.task.start - }, - hasDueDate() { - return !!this.task.due - }, showInCalendar() { // Only tasks with a due date show up in the calendar return !!this.showTaskInCalendar && this.task.dueMoment.isValid() @@ -916,12 +913,30 @@ export default { }, /** - * Removes an alarm + * Removes one or more alarms * - * @param {number} index The index of the alarm-item to remove + * @param {number[]} indexes The indexes of the alarm-items to remove */ - removeAlarmItem(index) { - this.removeAlarm({ task: this.task, index }) + removeAlarmItem(indexes) { + this.removeAlarm({ task: this.task, indexes }) + }, + + /** + * Restores the start or due-date + * + * This can happen if a start or due-date is deleted while there are still + * alarms relating to it. In case the user cancels the triggered modal, we + * have to restore the date. + * + * @param {Date} date The date previously deleted + * @param {boolean} isRelatedToStart True if the date is a start-date + */ + restoreDate(date, isRelatedToStart) { + if (isRelatedToStart) { + this.setStartDate({ task: this.task, value: date }) + } else { + this.setDueDate({ task: this.task, value: date }) + } }, async changeCalendar(calendar) {