diff --git a/web/app/app.module.ts b/web/app/app.module.ts index 413525d..e3e6f85 100644 --- a/web/app/app.module.ts +++ b/web/app/app.module.ts @@ -35,6 +35,7 @@ import { CustomWidgetConfigComponent } from "./configs/widget/custom/custom.widg import { ConfigScreenWidgetComponent } from "./configs/widget/config_screen/config_screen.widget.component"; import { EtherpadWidgetConfigComponent } from "./configs/widget/etherpad/etherpad.widget.component"; import { NameService } from "./shared/services/name.service"; +import { GoogleCalendarWidgetConfigComponent } from "./configs/widget/google_calendar/gcal.widget.component"; @NgModule({ imports: [ @@ -69,6 +70,7 @@ import { NameService } from "./shared/services/name.service"; ConfigScreenWidgetComponent, CustomWidgetConfigComponent, EtherpadWidgetConfigComponent, + GoogleCalendarWidgetConfigComponent, // Vendor ], diff --git a/web/app/app.routing.ts b/web/app/app.routing.ts index 34c2e03..9797839 100644 --- a/web/app/app.routing.ts +++ b/web/app/app.routing.ts @@ -8,6 +8,7 @@ import { GCalWidgetWrapperComponent } from "./widget_wrappers/gcal/gcal.componen import { RiotHomeComponent } from "./riot/riot-home/home.component"; import { CustomWidgetConfigComponent } from "./configs/widget/custom/custom.widget.component"; import { EtherpadWidgetConfigComponent } from "./configs/widget/etherpad/etherpad.widget.component"; +import { GoogleCalendarWidgetConfigComponent } from "./configs/widget/google_calendar/gcal.widget.component"; const routes: Routes = [ {path: "", component: HomeComponent}, @@ -34,6 +35,11 @@ const routes: Routes = [ component: EtherpadWidgetConfigComponent, data: {breadcrumb: "Etherpad Widgets", name: "Etherpad Widgets"} }, + { + path: "googlecalendar", + component: GoogleCalendarWidgetConfigComponent, + data: {breadcrumb: "Google Calendar Widgets", name: "Google Calendar Widgets"} + }, ], }, ], diff --git a/web/app/configs/widget/config_screen/config_screen.widget.component.ts b/web/app/configs/widget/config_screen/config_screen.widget.component.ts index 591351c..4f0b67b 100644 --- a/web/app/configs/widget/config_screen/config_screen.widget.component.ts +++ b/web/app/configs/widget/config_screen/config_screen.widget.component.ts @@ -1,4 +1,4 @@ -import { NewWidgetComponent } from "../widget.component"; +import { WidgetComponent } from "../widget.component"; import { Component, ContentChild, Input, TemplateRef } from "@angular/core"; @Component({ @@ -8,7 +8,7 @@ import { Component, ContentChild, Input, TemplateRef } from "@angular/core"; }) export class ConfigScreenWidgetComponent { - @Input() widgetComponent: NewWidgetComponent; + @Input() widgetComponent: WidgetComponent; @ContentChild(TemplateRef) widgetParamsTemplate: TemplateRef; constructor() { diff --git a/web/app/configs/widget/custom/custom.widget.component.ts b/web/app/configs/widget/custom/custom.widget.component.ts index 50d4687..15951f1 100644 --- a/web/app/configs/widget/custom/custom.widget.component.ts +++ b/web/app/configs/widget/custom/custom.widget.component.ts @@ -1,4 +1,4 @@ -import { NewWidgetComponent } from "../widget.component"; +import { WidgetComponent } from "../widget.component"; import { WIDGET_CUSTOM } from "../../../shared/models/widget"; import { Component } from "@angular/core"; @@ -6,7 +6,7 @@ import { Component } from "@angular/core"; templateUrl: "custom.widget.component.html", styleUrls: ["custom.widget.component.scss"], }) -export class CustomWidgetConfigComponent extends NewWidgetComponent { +export class CustomWidgetConfigComponent extends WidgetComponent { constructor() { super(WIDGET_CUSTOM, "Custom Widget", "generic"); } diff --git a/web/app/configs/widget/etherpad/etherpad.widget.component.ts b/web/app/configs/widget/etherpad/etherpad.widget.component.ts index 5e795b6..fed4143 100644 --- a/web/app/configs/widget/etherpad/etherpad.widget.component.ts +++ b/web/app/configs/widget/etherpad/etherpad.widget.component.ts @@ -1,4 +1,4 @@ -import { NewWidgetComponent } from "../widget.component"; +import { WidgetComponent } from "../widget.component"; import { EditableWidget, WIDGET_ETHERPAD } from "../../../shared/models/widget"; import { Component } from "@angular/core"; import { EtherpadWidget } from "../../../shared/models/integration"; @@ -9,7 +9,7 @@ import { NameService } from "../../../shared/services/name.service"; templateUrl: "etherpad.widget.component.html", styleUrls: ["etherpad.widget.component.scss"], }) -export class EtherpadWidgetConfigComponent extends NewWidgetComponent { +export class EtherpadWidgetConfigComponent extends WidgetComponent { private etherpadWidget: EtherpadWidget = SessionStorage.editIntegration; diff --git a/web/app/configs/widget/google_calendar/gcal.widget.component.html b/web/app/configs/widget/google_calendar/gcal.widget.component.html new file mode 100644 index 0000000..ae67f6b --- /dev/null +++ b/web/app/configs/widget/google_calendar/gcal.widget.component.html @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/web/app/configs/widget/google_calendar/gcal.widget.component.scss b/web/app/configs/widget/google_calendar/gcal.widget.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/app/configs/widget/google_calendar/gcal.widget.component.ts b/web/app/configs/widget/google_calendar/gcal.widget.component.ts new file mode 100644 index 0000000..0ce705a --- /dev/null +++ b/web/app/configs/widget/google_calendar/gcal.widget.component.ts @@ -0,0 +1,39 @@ +import { DISABLE_AUTOMATIC_WRAPPING, WidgetComponent } from "../widget.component"; +import { EditableWidget, WIDGET_GOOGLE_CALENDAR } from "../../../shared/models/widget"; +import { Component } from "@angular/core"; + +@Component({ + templateUrl: "gcal.widget.component.html", + styleUrls: ["gcal.widget.component.scss"], +}) +export class GoogleCalendarWidgetConfigComponent extends WidgetComponent { + constructor() { + super(WIDGET_GOOGLE_CALENDAR, "Google Calendar Widget", DISABLE_AUTOMATIC_WRAPPING, "googleCalendar"); + } + + protected OnWidgetsDiscovered(widgets: EditableWidget[]) { + for (const widget of widgets) { + if (widget.data.dimSrc && !widget.data.src) { + // Convert legacy Dimension widgets to new source + widget.data.src = widget.data.dimSrc; + } + } + } + + protected OnWidgetBeforeAdd(widget: EditableWidget) { + this.setCalendarUrl(widget); + } + + protected OnWidgetBeforeEdit(widget: EditableWidget) { + this.setCalendarUrl(widget); + } + + private setCalendarUrl(widget: EditableWidget) { + if (!widget.dimension.newData.src || widget.dimension.newData.src.trim().length === 0) { + throw new Error("Please enter a shared calendar ID"); + } + + const encodedId = encodeURIComponent(widget.dimension.newData.src); + widget.dimension.newUrl = window.location.origin + "/widget/gcal?calendarId=" + encodedId; + } +} \ No newline at end of file diff --git a/web/app/configs/widget/widget.component.ts b/web/app/configs/widget/widget.component.ts index 518bd32..191a7e0 100644 --- a/web/app/configs/widget/widget.component.ts +++ b/web/app/configs/widget/widget.component.ts @@ -12,7 +12,9 @@ const SCALAR_WIDGET_LINKS = [ "https://demo.riot.im/scalar/api/widgets/__TYPE__.html?url=", ]; -export class NewWidgetComponent implements OnInit { +export const DISABLE_AUTOMATIC_WRAPPING = ""; + +export class WidgetComponent implements OnInit { public isLoading = true; public isUpdating = false; @@ -223,6 +225,14 @@ export class NewWidgetComponent implements OnInit { * with a new widget. */ public addWidget(): Promise { + // Make sure we call "before add" before validating the URL + try { + this.OnWidgetBeforeAdd(this.newWidget); + } catch (error) { + this.toaster.pop("warning", error.message); + return; + } + if (!this.newWidget.dimension.newUrl || this.newWidget.dimension.newUrl.trim().length === 0) { this.toaster.pop("warning", "Please enter a URL for the widget"); return; @@ -231,7 +241,6 @@ export class NewWidgetComponent implements OnInit { this.packWidget(this.newWidget); this.isUpdating = true; - this.OnWidgetBeforeAdd(this.newWidget); return this.scalarApi.setWidget(SessionStorage.roomId, this.newWidget) .then(() => this.widgets.push(this.newWidget)) .then(() => { @@ -253,6 +262,14 @@ export class NewWidgetComponent implements OnInit { * @returns {Promise} Resolves when the widget has been updated in the room. */ public saveWidget(widget: EditableWidget): Promise { + // Make sure we call "before add" before validating the URL + try { + this.OnWidgetBeforeEdit(this.newWidget); + } catch (error) { + this.toaster.pop("warning", error.message); + return; + } + if (!widget.dimension.newUrl || widget.dimension.newUrl.trim().length === 0) { this.toaster.pop("warning", "Please enter a URL for the widget"); return; @@ -261,7 +278,6 @@ export class NewWidgetComponent implements OnInit { this.packWidget(widget); this.isUpdating = true; - this.OnWidgetBeforeEdit(widget); return this.scalarApi.setWidget(SessionStorage.roomId, widget) .then(() => { this.isUpdating = false; @@ -326,8 +342,8 @@ export class NewWidgetComponent implements OnInit { } /** - * Called before the widget is added to the room, but after the Dimension-specific - * settings have been copied over to the primary fields. + * Called before the widget is added to the room, and before the Dimension properties + * have been copied over. This is a good time to do validation. * @param {EditableWidget} _widget The widget that is about to be added */ protected OnWidgetBeforeAdd(_widget: EditableWidget): void { @@ -354,8 +370,8 @@ export class NewWidgetComponent implements OnInit { } /** - * Called before the given widget has been updated in the room, but after the - * Dimension-specific settings have been copied over to the primary fields. + * Called before the given widget has been updated in the room, and before the Dimension + * properties have been copied over. This is a good time to do validation. * This is not called for widgets being deleted. * @param {EditableWidget} _widget The widget about to be edited */