From c9571576fe8d8f07f4d7db3ee3b0a2a56e5c3382 Mon Sep 17 00:00:00 2001 From: turt2live Date: Mon, 28 Aug 2017 22:08:32 -0600 Subject: [PATCH] Support custom widgets in the frontend Adds #91 --- web/app/app.module.ts | 3 + .../custom_widget-config.component.html | 65 ++++++++++ .../custom_widget-config.component.scss | 1 + .../custom_widget-config.component.ts | 113 ++++++++++++++++++ web/app/configs/widget/widget.component.ts | 23 ++++ .../integration/integration.component.html | 2 +- web/app/riot/riot.component.ts | 6 + web/app/shared/integration.service.ts | 11 +- web/app/shared/models/scalar_responses.ts | 15 +++ web/app/shared/models/widget.ts | 42 +++++++ web/app/shared/scalar.service.ts | 34 +++++- web/public/img/avatars/customwidget.png | Bin 0 -> 28561 bytes 12 files changed, 311 insertions(+), 4 deletions(-) create mode 100644 web/app/configs/widget/custom_widget/custom_widget-config.component.html create mode 100644 web/app/configs/widget/custom_widget/custom_widget-config.component.scss create mode 100644 web/app/configs/widget/custom_widget/custom_widget-config.component.ts create mode 100644 web/app/configs/widget/widget.component.ts create mode 100644 web/app/shared/models/widget.ts create mode 100644 web/public/img/avatars/customwidget.png diff --git a/web/app/app.module.ts b/web/app/app.module.ts index 1c6dd2b..5ca3988 100644 --- a/web/app/app.module.ts +++ b/web/app/app.module.ts @@ -22,6 +22,7 @@ import { RssConfigComponent } from "./configs/rss/rss-config.component"; import { IrcConfigComponent } from "./configs/irc/irc-config.component"; import { IrcApiService } from "./shared/irc-api.service"; import { TravisCiConfigComponent } from "./configs/travisci/travisci-config.component"; +import { CustomWidgetConfigComponent } from "./configs/widget/custom_widget/custom_widget-config.component"; @NgModule({ imports: [ @@ -45,6 +46,7 @@ import { TravisCiConfigComponent } from "./configs/travisci/travisci-config.comp RssConfigComponent, IrcConfigComponent, TravisCiConfigComponent, + CustomWidgetConfigComponent, // Vendor ], @@ -61,6 +63,7 @@ import { TravisCiConfigComponent } from "./configs/travisci/travisci-config.comp RssConfigComponent, TravisCiConfigComponent, IrcConfigComponent, + CustomWidgetConfigComponent, ] }) export class AppModule { diff --git a/web/app/configs/widget/custom_widget/custom_widget-config.component.html b/web/app/configs/widget/custom_widget/custom_widget-config.component.html new file mode 100644 index 0000000..d7a5e93 --- /dev/null +++ b/web/app/configs/widget/custom_widget/custom_widget-config.component.html @@ -0,0 +1,65 @@ +
+ +
+ +

Configure custom widgets

+
+
+
+
+

Loading widgets...

+
+
+
+
+
+
+
+
+ + + + +
+
+
+ {{ widget.name || widget.url }} (added by {{ widget.ownerId }}) + + +
+ + + + +
+
+
+
+
+
\ No newline at end of file diff --git a/web/app/configs/widget/custom_widget/custom_widget-config.component.scss b/web/app/configs/widget/custom_widget/custom_widget-config.component.scss new file mode 100644 index 0000000..12e299a --- /dev/null +++ b/web/app/configs/widget/custom_widget/custom_widget-config.component.scss @@ -0,0 +1 @@ +// component styles are encapsulated and only applied to their components diff --git a/web/app/configs/widget/custom_widget/custom_widget-config.component.ts b/web/app/configs/widget/custom_widget/custom_widget-config.component.ts new file mode 100644 index 0000000..2c9c81d --- /dev/null +++ b/web/app/configs/widget/custom_widget/custom_widget-config.component.ts @@ -0,0 +1,113 @@ +import { Component } from "@angular/core"; +import { ModalComponent, DialogRef } from "ngx-modialog"; +import { WidgetComponent } from "../widget.component"; +import { ScalarService } from "../../../shared/scalar.service"; +import { ConfigModalContext } from "../../../integration/integration.component"; +import { ToasterService } from "angular2-toaster"; +import { Widget, WIDGET_DIM_CUSTOM, WIDGET_SCALAR_CUSTOM } from "../../../shared/models/widget"; + +// TODO: A lot of this can probably be abstracted out for other widgets (even the UI), possibly even for other integrations + +@Component({ + selector: "my-customwidget-config", + templateUrl: "custom_widget-config.component.html", + styleUrls: ["custom_widget-config.component.scss", "./../../config.component.scss"], +}) +export class CustomWidgetConfigComponent extends WidgetComponent implements ModalComponent { + + public isLoading = true; + public isUpdating = false; + public widgets: Widget[]; + public widgetUrl = ""; + + private toggledWidgets: string[] = []; + + constructor(public dialog: DialogRef, + private toaster: ToasterService, + scalarService: ScalarService) { + super(scalarService, dialog.context.roomId); + + this.getWidgetsOfType(WIDGET_DIM_CUSTOM, WIDGET_SCALAR_CUSTOM).then(widgets => { + this.widgets = widgets; + this.isLoading = false; + this.isUpdating = false; + }); + } + + public addWidget() { + let constructedWidget: Widget = { + id: "dimension-" + (new Date().getTime()), + url: this.widgetUrl, + type: WIDGET_DIM_CUSTOM, + name: "Custom Widget", + }; + + this.isUpdating = true; + this.scalarApi.setWidget(this.roomId, constructedWidget) + .then(() => this.widgets.push(constructedWidget)) + .then(() => { + this.isUpdating = false; + this.widgetUrl = ""; + this.toaster.pop("success", "Widget added!"); + }) + .catch(err => { + this.toaster.pop("error", err.json().error); + console.error(err); + this.isUpdating = false; + }); + } + + public saveWidget(widget: Widget) { + if (widget.newUrl.trim().length === 0) { + this.toaster.pop("warning", "Please enter a URL for the widget"); + return; + } + + widget.name = widget.newName || "Custom Widget"; + widget.url = widget.newUrl; + + this.isUpdating = true; + this.scalarApi.setWidget(this.roomId, widget) + .then(() => this.toggleWidget(widget)) + .then(() => { + this.isUpdating = false; + this.toaster.pop("success", "Widget updated!"); + }) + .catch(err => { + this.toaster.pop("error", err.json().error); + console.error(err); + this.isUpdating = false; + }); + } + + public removeWidget(widget: Widget) { + this.isUpdating = true; + this.scalarApi.deleteWidget(this.roomId, widget) + .then(() => this.widgets.splice(this.widgets.indexOf(widget), 1)) + .then(() => { + this.isUpdating = false; + this.toaster.pop("success", "Widget deleted!"); + }) + .catch(err => { + this.toaster.pop("error", err.json().error); + console.error(err); + this.isUpdating = false; + }); + } + + public editWidget(widget: Widget) { + widget.newName = widget.name || "Custom Widget"; + widget.newUrl = widget.url; + this.toggleWidget(widget); + } + + public toggleWidget(widget: Widget) { + let idx = this.toggledWidgets.indexOf(widget.id); + if (idx === -1) this.toggledWidgets.push(widget.id); + else this.toggledWidgets.splice(idx, 1); + } + + public isWidgetToggled(widget: Widget) { + return this.toggledWidgets.indexOf(widget.id) !== -1; + } +} diff --git a/web/app/configs/widget/widget.component.ts b/web/app/configs/widget/widget.component.ts new file mode 100644 index 0000000..0cc1098 --- /dev/null +++ b/web/app/configs/widget/widget.component.ts @@ -0,0 +1,23 @@ +import { ScalarService } from "../../shared/scalar.service"; +import { Widget, ScalarToWidgets } from "../../shared/models/widget"; + +export class WidgetComponent { + + constructor(protected scalarApi: ScalarService, protected roomId: string) { + } + + protected getWidgetsOfType(type: string, altType: string = null): Promise { + return this.scalarApi.getWidgets(this.roomId) + .then(resp => ScalarToWidgets(resp)) + .then(widgets => { + let filtered: Widget[] = []; + + for (let widget of widgets) { + if (widget.type === type || (altType && widget.type === altType)) + filtered.push(widget); + } + + return filtered; + }); + } +} diff --git a/web/app/integration/integration.component.html b/web/app/integration/integration.component.html index 3f9d756..44ce08d 100644 --- a/web/app/integration/integration.component.html +++ b/web/app/integration/integration.component.html @@ -3,7 +3,7 @@
{{ integration.name }}
-
+
diff --git a/web/app/riot/riot.component.ts b/web/app/riot/riot.component.ts index eca1ca1..e7f6efe 100644 --- a/web/app/riot/riot.component.ts +++ b/web/app/riot/riot.component.ts @@ -54,6 +54,12 @@ export class RiotComponent { private updateIntegrationState(integration: Integration) { integration.hasConfig = IntegrationService.hasConfig(integration); + if (integration.type === "widget") { + integration.isEnabled = true; + integration.isBroken = false; + return Promise.resolve(); + } + if (integration.requirements) { let keys = _.keys(integration.requirements); let promises = []; diff --git a/web/app/shared/integration.service.ts b/web/app/shared/integration.service.ts index 1a8d462..2705602 100644 --- a/web/app/shared/integration.service.ts +++ b/web/app/shared/integration.service.ts @@ -4,6 +4,7 @@ import { RssConfigComponent } from "../configs/rss/rss-config.component"; import { ContainerContent } from "ngx-modialog"; import { IrcConfigComponent } from "../configs/irc/irc-config.component"; import { TravisCiConfigComponent } from "../configs/travisci/travisci-config.component"; +import { CustomWidgetConfigComponent } from "../configs/widget/custom_widget/custom_widget-config.component"; @Injectable() export class IntegrationService { @@ -16,7 +17,10 @@ export class IntegrationService { }, "bridge": { "irc": true, - } + }, + "widget": { + "customwidget": true + }, }; private static components = { @@ -26,7 +30,10 @@ export class IntegrationService { }, "bridge": { "irc": IrcConfigComponent, - } + }, + "widget": { + "customwidget": CustomWidgetConfigComponent, + }, }; static isSupported(integration: Integration): boolean { diff --git a/web/app/shared/models/scalar_responses.ts b/web/app/shared/models/scalar_responses.ts index 4b3e4be..cc6024a 100644 --- a/web/app/shared/models/scalar_responses.ts +++ b/web/app/shared/models/scalar_responses.ts @@ -30,4 +30,19 @@ export interface JoinRuleStateResponse extends ScalarRoomResponse { response: { join_rule: string; }; +} + +export interface WidgetsResponse extends ScalarRoomResponse { + response: { + type: "im.vector.modular.widgets"; + state_key: string; + sender: string; + room_id: string; + content: { + type: string; + url: string; + name?: string; + data?: any; + } + }[]; } \ No newline at end of file diff --git a/web/app/shared/models/widget.ts b/web/app/shared/models/widget.ts new file mode 100644 index 0000000..9446ef0 --- /dev/null +++ b/web/app/shared/models/widget.ts @@ -0,0 +1,42 @@ +import { WidgetsResponse } from "./scalar_responses"; + +// Scalar's widget types (known) +export const WIDGET_SCALAR_CUSTOM = "customwidget"; +export const WIDGET_SCALAR_ETHERPAD = "etherpad"; +export const WIDGET_SCALAR_GOOGLEDOCS = "googledocs"; +export const WIDGET_SCALAR_JITSI = "jitsi"; +export const WIDGET_SCALAR_YOUTUBE = "youtube"; +export const WIDGET_SCALAR_GRAFANA = "grafana"; + +// Dimension has its own set of types to ensure that we don't conflict with Scalar +export const WIDGET_DIM_CUSTOM = "dimension-customwidget"; + +export interface Widget { + id: string; + type: string; + url: string; + name?: string; + data?: any; + ownerId?: string; + + // used only in ui + newName?: string; + newUrl?: string; +} + +export function ScalarToWidgets(scalarResponse: WidgetsResponse): Widget[] { + let widgets = []; + + for (let event of scalarResponse.response) { + widgets.push({ + id: event.state_key, + type: event.content.type, + url: event.content.url, + name: event.content.name, + data: event.content.data, + ownerId: event.sender, + }); + } + + return widgets; +} diff --git a/web/app/shared/scalar.service.ts b/web/app/shared/scalar.service.ts index 7d57ba1..6fc2f71 100644 --- a/web/app/shared/scalar.service.ts +++ b/web/app/shared/scalar.service.ts @@ -1,6 +1,12 @@ import { Injectable } from "@angular/core"; import * as randomString from "random-string"; -import { MembershipStateResponse, ScalarSuccessResponse, JoinRuleStateResponse } from "./models/scalar_responses"; +import { + MembershipStateResponse, + ScalarSuccessResponse, + JoinRuleStateResponse, + WidgetsResponse +} from "./models/scalar_responses"; +import { Widget } from "./models/widget"; @Injectable() export class ScalarService { @@ -36,6 +42,32 @@ export class ScalarService { }); } + public getWidgets(roomId: string): Promise { + return this.callAction("get_widgets", { + room_id: roomId + }); + } + + public setWidget(roomId: string, widget: Widget): Promise { + return this.callAction("set_widget", { + room_id: roomId, + widget_id: widget.id, + type: widget.type, + url: widget.url, + name: widget.name, + data: widget.data + }); + } + + public deleteWidget(roomId: string, widget: Widget): Promise { + return this.callAction("set_widget", { + room_id: roomId, + widget_id: widget.id, + type: widget.type, // required for some reason + url: "" + }); + } + public close(): void { this.callAction("close_scalar", {}); } diff --git a/web/public/img/avatars/customwidget.png b/web/public/img/avatars/customwidget.png new file mode 100644 index 0000000000000000000000000000000000000000..8ec4211eb253d35367ea3759f2a5c1a3cfc5460f GIT binary patch literal 28561 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 literal 0 HcmV?d00001