mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Catch calender events fetch errors and keep UI working (#14517)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
0001cad423
commit
076a6c4459
@ -56,13 +56,14 @@ export const fetchCalendarEvents = async (
|
|||||||
start: Date,
|
start: Date,
|
||||||
end: Date,
|
end: Date,
|
||||||
calendars: Calendar[]
|
calendars: Calendar[]
|
||||||
): Promise<CalendarEvent[]> => {
|
): Promise<{ events: CalendarEvent[]; errors: string[] }> => {
|
||||||
const params = encodeURI(
|
const params = encodeURI(
|
||||||
`?start=${start.toISOString()}&end=${end.toISOString()}`
|
`?start=${start.toISOString()}&end=${end.toISOString()}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const calEvents: CalendarEvent[] = [];
|
const calEvents: CalendarEvent[] = [];
|
||||||
const promises: Promise<any>[] = [];
|
const errors: string[] = [];
|
||||||
|
const promises: Promise<CalendarEvent[]>[] = [];
|
||||||
|
|
||||||
calendars.forEach((cal) => {
|
calendars.forEach((cal) => {
|
||||||
promises.push(
|
promises.push(
|
||||||
@ -73,9 +74,15 @@ export const fetchCalendarEvents = async (
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const results = await Promise.all(promises);
|
for (const [idx, promise] of promises.entries()) {
|
||||||
|
let result: CalendarEvent[];
|
||||||
results.forEach((result, idx) => {
|
try {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
result = await promise;
|
||||||
|
} catch (err) {
|
||||||
|
errors.push(calendars[idx].entity_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const cal = calendars[idx];
|
const cal = calendars[idx];
|
||||||
result.forEach((ev) => {
|
result.forEach((ev) => {
|
||||||
const eventStart = getCalendarDate(ev.start);
|
const eventStart = getCalendarDate(ev.start);
|
||||||
@ -104,9 +111,9 @@ export const fetchCalendarEvents = async (
|
|||||||
|
|
||||||
calEvents.push(event);
|
calEvents.push(event);
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
return calEvents;
|
return { events: calEvents, errors };
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCalendarDate = (dateObj: any): string | undefined => {
|
const getCalendarDate = (dateObj: any): string | undefined => {
|
||||||
|
@ -97,6 +97,8 @@ export class HAFullCalendar extends LitElement {
|
|||||||
|
|
||||||
@property() public initialView: FullCalendarView = "dayGridMonth";
|
@property() public initialView: FullCalendarView = "dayGridMonth";
|
||||||
|
|
||||||
|
@property({ attribute: false }) public error?: string = undefined;
|
||||||
|
|
||||||
private calendar?: Calendar;
|
private calendar?: Calendar;
|
||||||
|
|
||||||
private _viewButtons?: ToggleButton[];
|
private _viewButtons?: ToggleButton[];
|
||||||
@ -116,6 +118,14 @@ export class HAFullCalendar extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
${this.calendar
|
${this.calendar
|
||||||
? html`
|
? html`
|
||||||
|
${this.error
|
||||||
|
? html`<ha-alert
|
||||||
|
alert-type="error"
|
||||||
|
dismissable
|
||||||
|
@alert-dismissed-clicked=${this._clearError}
|
||||||
|
>${this.error}</ha-alert
|
||||||
|
>`
|
||||||
|
: ""}
|
||||||
<div class="header">
|
<div class="header">
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html`
|
? html`
|
||||||
@ -380,6 +390,10 @@ export class HAFullCalendar extends LitElement {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
private _clearError() {
|
||||||
|
this.error = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
@ -449,6 +463,11 @@ export class HAFullCalendar extends LitElement {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ha-alert {
|
||||||
|
display: block;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
#calendar {
|
#calendar {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
background-color: var(
|
background-color: var(
|
||||||
|
@ -15,6 +15,7 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { LocalStorage } from "../../common/decorators/local-storage";
|
import { LocalStorage } from "../../common/decorators/local-storage";
|
||||||
import { HASSDomEvent } from "../../common/dom/fire_event";
|
import { HASSDomEvent } from "../../common/dom/fire_event";
|
||||||
|
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||||
import "../../components/ha-card";
|
import "../../components/ha-card";
|
||||||
import "../../components/ha-icon-button";
|
import "../../components/ha-icon-button";
|
||||||
import "../../components/ha-menu-button";
|
import "../../components/ha-menu-button";
|
||||||
@ -40,6 +41,8 @@ class PanelCalendar extends LitElement {
|
|||||||
|
|
||||||
@state() private _events: CalendarEvent[] = [];
|
@state() private _events: CalendarEvent[] = [];
|
||||||
|
|
||||||
|
@state() private _error?: string = undefined;
|
||||||
|
|
||||||
@LocalStorage("deSelectedCalendars", true)
|
@LocalStorage("deSelectedCalendars", true)
|
||||||
private _deSelectedCalendars: string[] = [];
|
private _deSelectedCalendars: string[] = [];
|
||||||
|
|
||||||
@ -101,6 +104,7 @@ class PanelCalendar extends LitElement {
|
|||||||
.calendars=${this._calendars}
|
.calendars=${this._calendars}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.error=${this._error}
|
||||||
@view-changed=${this._handleViewChanged}
|
@view-changed=${this._handleViewChanged}
|
||||||
></ha-full-calendar>
|
></ha-full-calendar>
|
||||||
</div>
|
</div>
|
||||||
@ -118,9 +122,9 @@ class PanelCalendar extends LitElement {
|
|||||||
start: Date,
|
start: Date,
|
||||||
end: Date,
|
end: Date,
|
||||||
calendars: Calendar[]
|
calendars: Calendar[]
|
||||||
): Promise<CalendarEvent[]> {
|
): Promise<{ events: CalendarEvent[]; errors: string[] }> {
|
||||||
if (!calendars.length) {
|
if (!calendars.length) {
|
||||||
return [];
|
return { events: [], errors: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetchCalendarEvents(this.hass, start, end, calendars);
|
return fetchCalendarEvents(this.hass, start, end, calendars);
|
||||||
@ -135,8 +139,9 @@ class PanelCalendar extends LitElement {
|
|||||||
const checked = ev.target.checked;
|
const checked = ev.target.checked;
|
||||||
|
|
||||||
if (checked) {
|
if (checked) {
|
||||||
const events = await this._fetchEvents(this._start!, this._end!, [cal]);
|
const result = await this._fetchEvents(this._start!, this._end!, [cal]);
|
||||||
this._events = [...this._events, ...events];
|
this._events = [...this._events, ...result.events];
|
||||||
|
this._handleErrors(result.errors);
|
||||||
this._deSelectedCalendars = this._deSelectedCalendars.filter(
|
this._deSelectedCalendars = this._deSelectedCalendars.filter(
|
||||||
(deCal) => deCal !== cal.entity_id
|
(deCal) => deCal !== cal.entity_id
|
||||||
);
|
);
|
||||||
@ -161,19 +166,40 @@ class PanelCalendar extends LitElement {
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this._start = ev.detail.start;
|
this._start = ev.detail.start;
|
||||||
this._end = ev.detail.end;
|
this._end = ev.detail.end;
|
||||||
this._events = await this._fetchEvents(
|
const result = await this._fetchEvents(
|
||||||
this._start,
|
this._start,
|
||||||
this._end,
|
this._end,
|
||||||
this._selectedCalendars
|
this._selectedCalendars
|
||||||
);
|
);
|
||||||
|
this._events = result.events;
|
||||||
|
this._handleErrors(result.errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _handleRefresh(): Promise<void> {
|
private async _handleRefresh(): Promise<void> {
|
||||||
this._events = await this._fetchEvents(
|
const result = await this._fetchEvents(
|
||||||
this._start!,
|
this._start!,
|
||||||
this._end!,
|
this._end!,
|
||||||
this._selectedCalendars
|
this._selectedCalendars
|
||||||
);
|
);
|
||||||
|
this._events = result.events;
|
||||||
|
this._handleErrors(result.errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleErrors(error_entity_ids: string[]) {
|
||||||
|
this._error = undefined;
|
||||||
|
if (error_entity_ids.length > 0) {
|
||||||
|
const nameList = error_entity_ids
|
||||||
|
.map((error_entity_id) =>
|
||||||
|
this.hass!.states[error_entity_id]
|
||||||
|
? computeStateName(this.hass!.states[error_entity_id])
|
||||||
|
: error_entity_id
|
||||||
|
)
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
this._error = `${this.hass!.localize(
|
||||||
|
"ui.components.calendar.event_retrieval_error"
|
||||||
|
)} ${nameList}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
@ -10,6 +10,7 @@ import { customElement, property, state, query } from "lit/decorators";
|
|||||||
import { getColorByIndex } from "../../../common/color/colors";
|
import { getColorByIndex } from "../../../common/color/colors";
|
||||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||||
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import { debounce } from "../../../common/util/debounce";
|
import { debounce } from "../../../common/util/debounce";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import {
|
import {
|
||||||
@ -69,6 +70,8 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
@state() private _veryNarrow = false;
|
@state() private _veryNarrow = false;
|
||||||
|
|
||||||
|
@state() private _error?: string = undefined;
|
||||||
|
|
||||||
@query("ha-full-calendar", true) private _calendar?: HAFullCalendar;
|
@query("ha-full-calendar", true) private _calendar?: HAFullCalendar;
|
||||||
|
|
||||||
private _startDate?: Date;
|
private _startDate?: Date;
|
||||||
@ -131,6 +134,7 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.views=${views}
|
.views=${views}
|
||||||
.initialView=${this._config.initial_view!}
|
.initialView=${this._config.initial_view!}
|
||||||
|
.error=${this._error}
|
||||||
@view-changed=${this._handleViewChanged}
|
@view-changed=${this._handleViewChanged}
|
||||||
></ha-full-calendar>
|
></ha-full-calendar>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
@ -169,12 +173,28 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._events = await fetchCalendarEvents(
|
this._error = undefined;
|
||||||
|
const result = await fetchCalendarEvents(
|
||||||
this.hass!,
|
this.hass!,
|
||||||
this._startDate,
|
this._startDate,
|
||||||
this._endDate,
|
this._endDate,
|
||||||
this._calendars
|
this._calendars
|
||||||
);
|
);
|
||||||
|
this._events = result.events;
|
||||||
|
|
||||||
|
if (result.errors.length > 0) {
|
||||||
|
const nameList = result.errors
|
||||||
|
.map((error_entity_id) =>
|
||||||
|
this.hass!.states[error_entity_id]
|
||||||
|
? computeStateName(this.hass!.states[error_entity_id])
|
||||||
|
: error_entity_id
|
||||||
|
)
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
this._error = `${this.hass!.localize(
|
||||||
|
"ui.components.calendar.event_retrieval_error"
|
||||||
|
)} ${nameList}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _measureCard() {
|
private _measureCard() {
|
||||||
|
@ -634,6 +634,7 @@
|
|||||||
"label": "Calendar",
|
"label": "Calendar",
|
||||||
"my_calendars": "My Calendars",
|
"my_calendars": "My Calendars",
|
||||||
"today": "Today",
|
"today": "Today",
|
||||||
|
"event_retrieval_error": "Could not retrieve events for calendars: ",
|
||||||
"event": {
|
"event": {
|
||||||
"add": "Add Event",
|
"add": "Add Event",
|
||||||
"delete": "Delete Event",
|
"delete": "Delete Event",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user