From e175c7ba3ca04dc613d9bf63450a40bf3aff4471 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Tue, 27 Dec 2022 12:47:42 -0800 Subject: [PATCH] Add edit/update support for calendar events (#14814) Co-authored-by: Bram Kragten --- src/data/calendar.ts | 11 ++- .../calendar/dialog-calendar-event-editor.ts | 73 ++++++++++++++++++- src/panels/calendar/ha-full-calendar.ts | 4 + src/translations/en.json | 6 ++ 4 files changed, 88 insertions(+), 6 deletions(-) diff --git a/src/data/calendar.ts b/src/data/calendar.ts index 0b856de9b9..c0b9d2b072 100644 --- a/src/data/calendar.ts +++ b/src/data/calendar.ts @@ -50,6 +50,7 @@ export enum RecurrenceRange { export const enum CalendarEntityFeature { CREATE_EVENT = 1, DELETE_EVENT = 2, + UPDATE_EVENT = 4, } export const fetchCalendarEvents = async ( @@ -161,12 +162,18 @@ export const createCalendarEvent = ( export const updateCalendarEvent = ( hass: HomeAssistant, entityId: string, - event: CalendarEventMutableParams + uid: string, + event: CalendarEventMutableParams, + recurrence_id?: string, + recurrence_range?: RecurrenceRange ) => hass.callWS({ type: "calendar/event/update", entity_id: entityId, - event: event, + uid, + recurrence_id, + recurrence_range, + event, }); export const deleteCalendarEvent = ( diff --git a/src/panels/calendar/dialog-calendar-event-editor.ts b/src/panels/calendar/dialog-calendar-event-editor.ts index 19965a748e..b7dacd23b3 100644 --- a/src/panels/calendar/dialog-calendar-event-editor.ts +++ b/src/panels/calendar/dialog-calendar-event-editor.ts @@ -25,6 +25,8 @@ import { CalendarEventMutableParams, createCalendarEvent, deleteCalendarEvent, + updateCalendarEvent, + RecurrenceRange, } from "../../data/calendar"; import { haStyleDialog } from "../../resources/styles"; import { HomeAssistant } from "../../types"; @@ -87,10 +89,10 @@ class DialogCalendarEventEditor extends LitElement { this._summary = entry.summary; this._rrule = entry.rrule; if (this._allDay) { - this._dtstart = new Date(entry.dtstart); + this._dtstart = new Date(entry.dtstart + "T00:00:00"); // Calendar event end dates are exclusive, but not shown that way in the UI. The // reverse happens when persisting the event. - this._dtend = addDays(new Date(entry.dtend), -1); + this._dtend = addDays(new Date(entry.dtend + "T00:00:00"), -1); } else { this._dtstart = new Date(entry.dtstart); this._dtend = new Date(entry.dtend); @@ -168,6 +170,7 @@ class DialogCalendarEventEditor extends LitElement { class="summary" name="summary" .label=${this.hass.localize("ui.components.calendar.event.summary")} + .value=${this._summary} required @change=${this._handleSummaryChanged} error-message=${this.hass.localize("ui.common.error_required")} @@ -179,6 +182,7 @@ class DialogCalendarEventEditor extends LitElement { .label=${this.hass.localize( "ui.components.calendar.event.description" )} + .value=${this._description} @change=${this._handleDescriptionChanged} autogrow > @@ -412,6 +416,13 @@ class DialogCalendarEventEditor extends LitElement { this._calendarId = ev.detail.value; } + private _isValidStartEnd(): boolean { + if (this._allDay) { + return this._dtend! >= this._dtstart!; + } + return this._dtend! > this._dtstart!; + } + private async _createEvent() { if (!this._summary || !this._calendarId) { this._error = this.hass.localize( @@ -420,7 +431,7 @@ class DialogCalendarEventEditor extends LitElement { return; } - if (this._dtend! <= this._dtstart!) { + if (!this._isValidStartEnd()) { this._error = this.hass.localize( "ui.components.calendar.event.invalid_duration" ); @@ -445,7 +456,61 @@ class DialogCalendarEventEditor extends LitElement { } private async _saveEvent() { - // to be implemented + if (!this._summary || !this._calendarId) { + this._error = this.hass.localize( + "ui.components.calendar.event.not_all_required_fields" + ); + return; + } + + if (!this._isValidStartEnd()) { + this._error = this.hass.localize( + "ui.components.calendar.event.invalid_duration" + ); + return; + } + + this._submitting = true; + const entry = this._params!.entry!; + let range: RecurrenceRange | undefined = RecurrenceRange.THISEVENT; + if (entry.recurrence_id) { + range = await showConfirmEventDialog(this, { + title: this.hass.localize( + "ui.components.calendar.event.confirm_update.update" + ), + text: this.hass.localize( + "ui.components.calendar.event.confirm_update.recurring_prompt" + ), + confirmText: this.hass.localize( + "ui.components.calendar.event.confirm_update.update_this" + ), + confirmFutureText: this.hass.localize( + "ui.components.calendar.event.confirm_update.update_future" + ), + }); + } + if (range === undefined) { + // Cancel + this._submitting = false; + return; + } + try { + await updateCalendarEvent( + this.hass!, + this._calendarId!, + entry.uid!, + this._calculateData(), + entry.recurrence_id || "", + range! + ); + } catch (err: any) { + this._error = err ? err.message : "Unknown error"; + return; + } finally { + this._submitting = false; + } + await this._params!.updated(); + this.closeDialog(); } private async _deleteEvent() { diff --git a/src/panels/calendar/ha-full-calendar.ts b/src/panels/calendar/ha-full-calendar.ts index e0e2a114d3..9e4f3ad5e0 100644 --- a/src/panels/calendar/ha-full-calendar.ts +++ b/src/panels/calendar/ha-full-calendar.ts @@ -302,6 +302,9 @@ export class HAFullCalendar extends LitElement { private _handleEventClick(info): void { const entityStateObj = this.hass.states[info.event.extendedProps.calendar]; + const canEdit = + entityStateObj && + supportsFeature(entityStateObj, CalendarEntityFeature.UPDATE_EVENT); const canDelete = entityStateObj && supportsFeature(entityStateObj, CalendarEntityFeature.DELETE_EVENT); @@ -312,6 +315,7 @@ export class HAFullCalendar extends LitElement { updated: () => { this._fireViewChanged(); }, + canEdit: canEdit, canDelete: canDelete, }); } diff --git a/src/translations/en.json b/src/translations/en.json index c828d219e0..f774926c70 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -623,6 +623,12 @@ "prompt": "Do you want to delete this event?", "recurring_prompt": "Do you want to delete only this event, or this and all future occurrences of the event?" }, + "confirm_update": { + "update": "Update Event", + "update_this": "Update Only This Event", + "update_future": "Update All Future Events", + "recurring_prompt": "Do you want to update only this event, or this and all future occurrences of the event?" + }, "repeat": { "label": "Repeat", "freq": {