From fce6f2174a5cb47038659c5e3182583b1e1eebe4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sat, 23 Dec 2017 13:16:05 -0700 Subject: [PATCH] Add etherpad widget configuration --- web/app/app.module.ts | 4 + web/app/app.routing.ts | 6 + .../etherpad/etherpad.widget.component.html | 18 +++ .../etherpad/etherpad.widget.component.scss | 0 .../etherpad/etherpad.widget.component.ts | 34 +++++ web/app/configs/widget/widget.component.ts | 129 ++++++++++++++---- web/app/shared/services/name.service.ts | 18 +++ 7 files changed, 183 insertions(+), 26 deletions(-) create mode 100644 web/app/configs/widget/etherpad/etherpad.widget.component.html create mode 100644 web/app/configs/widget/etherpad/etherpad.widget.component.scss create mode 100644 web/app/configs/widget/etherpad/etherpad.widget.component.ts create mode 100644 web/app/shared/services/name.service.ts diff --git a/web/app/app.module.ts b/web/app/app.module.ts index 94e1b46..413525d 100644 --- a/web/app/app.module.ts +++ b/web/app/app.module.ts @@ -33,6 +33,8 @@ import { ServiceLocator } from "./shared/services/locator.service"; import { IboxComponent } from "./elements/ibox/ibox.component"; import { CustomWidgetConfigComponent } from "./configs/widget/custom/custom.widget.component"; 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"; @NgModule({ imports: [ @@ -66,6 +68,7 @@ import { ConfigScreenWidgetComponent } from "./configs/widget/config_screen/conf IboxComponent, ConfigScreenWidgetComponent, CustomWidgetConfigComponent, + EtherpadWidgetConfigComponent, // Vendor ], @@ -74,6 +77,7 @@ import { ConfigScreenWidgetComponent } from "./configs/widget/config_screen/conf ScalarServerApiService, DimensionApiService, AdminApiService, + NameService, {provide: Window, useValue: window}, // Vendor diff --git a/web/app/app.routing.ts b/web/app/app.routing.ts index df889b8..34c2e03 100644 --- a/web/app/app.routing.ts +++ b/web/app/app.routing.ts @@ -7,6 +7,7 @@ import { JitsiWidgetWrapperComponent } from "./widget_wrappers/jitsi/jitsi.compo import { GCalWidgetWrapperComponent } from "./widget_wrappers/gcal/gcal.component"; 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"; const routes: Routes = [ {path: "", component: HomeComponent}, @@ -28,6 +29,11 @@ const routes: Routes = [ component: CustomWidgetConfigComponent, data: {breadcrumb: "Custom Widgets", name: "Custom Widgets"} }, + { + path: "etherpad", + component: EtherpadWidgetConfigComponent, + data: {breadcrumb: "Etherpad Widgets", name: "Etherpad Widgets"} + }, ], }, ], diff --git a/web/app/configs/widget/etherpad/etherpad.widget.component.html b/web/app/configs/widget/etherpad/etherpad.widget.component.html new file mode 100644 index 0000000..abc5fa3 --- /dev/null +++ b/web/app/configs/widget/etherpad/etherpad.widget.component.html @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/web/app/configs/widget/etherpad/etherpad.widget.component.scss b/web/app/configs/widget/etherpad/etherpad.widget.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/app/configs/widget/etherpad/etherpad.widget.component.ts b/web/app/configs/widget/etherpad/etherpad.widget.component.ts new file mode 100644 index 0000000..5e795b6 --- /dev/null +++ b/web/app/configs/widget/etherpad/etherpad.widget.component.ts @@ -0,0 +1,34 @@ +import { NewWidgetComponent } from "../widget.component"; +import { EditableWidget, WIDGET_ETHERPAD } from "../../../shared/models/widget"; +import { Component } from "@angular/core"; +import { EtherpadWidget } from "../../../shared/models/integration"; +import { SessionStorage } from "../../../shared/SessionStorage"; +import { NameService } from "../../../shared/services/name.service"; + +@Component({ + templateUrl: "etherpad.widget.component.html", + styleUrls: ["etherpad.widget.component.scss"], +}) +export class EtherpadWidgetConfigComponent extends NewWidgetComponent { + + private etherpadWidget: EtherpadWidget = SessionStorage.editIntegration; + + constructor(private nameService: NameService) { + super(WIDGET_ETHERPAD, "Etherpad Widget", "generic", "etherpad"); + } + + protected OnNewWidgetPrepared(widget: EditableWidget): void { + const name = this.nameService.getHumanReadableName(); + + let template = "https://demo.riot.im/etherpad/p/$roomId_$padName"; + if (this.etherpadWidget.options && this.etherpadWidget.options.defaultUrl) { + template = this.etherpadWidget.options.defaultUrl; + } + + template = template.replace("$roomId", encodeURIComponent(SessionStorage.roomId)); + template = template.replace("$padName", encodeURIComponent(name)); + + widget.dimension.newUrl = template; + widget.dimension.newName = name; + } +} \ 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 3e2254d..518bd32 100644 --- a/web/app/configs/widget/widget.component.ts +++ b/web/app/configs/widget/widget.component.ts @@ -1,9 +1,9 @@ -import { EventEmitter } from "@angular/core"; import { convertScalarWidgetsToDtos, EditableWidget } from "../../shared/models/widget"; import { ToasterService } from "angular2-toaster"; import { ScalarClientApiService } from "../../shared/services/scalar-client-api.service"; import { ServiceLocator } from "../../shared/services/locator.service"; import { SessionStorage } from "../../shared/SessionStorage"; +import { OnInit } from "@angular/core"; const SCALAR_WIDGET_LINKS = [ "https://scalar-staging.riot.im/scalar/api/widgets/__TYPE__.html?url=", @@ -12,7 +12,7 @@ const SCALAR_WIDGET_LINKS = [ "https://demo.riot.im/scalar/api/widgets/__TYPE__.html?url=", ]; -export class NewWidgetComponent { +export class NewWidgetComponent implements OnInit { public isLoading = true; public isUpdating = false; @@ -27,40 +27,32 @@ export class NewWidgetComponent { private window = ServiceLocator.injector.get(Window); private scalarApi = ServiceLocator.injector.get(ScalarClientApiService); - protected OnNewWidgetPrepared = new EventEmitter(); - protected OnWidgetsDiscovered = new EventEmitter(); - protected OnWidgetBeforeAdd = new EventEmitter(); - protected OnWidgetAfterAdd = new EventEmitter(); - protected OnWidgetPreparedForEdit = new EventEmitter(); - protected OnWidgetBeforeEdit = new EventEmitter(); - protected OnWidgetAfterEdit = new EventEmitter(); - protected OnWidgetBeforeDelete = new EventEmitter(); - protected OnWidgetAfterDelete = new EventEmitter(); - constructor(private widgetTypes: string[], public defaultName: string, private wrapperId = "generic", private scalarWrapperId = null) { this.isLoading = true; this.isUpdating = false; + } - if (wrapperId) { - this.wrapperUrl = this.window.location.origin + "/widgets/" + wrapperId + "?url="; + public ngOnInit(): void { + if (this.wrapperId) { + this.wrapperUrl = this.window.location.origin + "/widgets/" + this.wrapperId + "?url="; - if (!scalarWrapperId) scalarWrapperId = wrapperId; + if (!this.scalarWrapperId) this.scalarWrapperId = this.wrapperId; for (let widgetLink of SCALAR_WIDGET_LINKS) { - this.scalarWrapperUrls.push(widgetLink.replace("__TYPE__", scalarWrapperId)); + this.scalarWrapperUrls.push(widgetLink.replace("__TYPE__", this.scalarWrapperId)); } } this.prepareNewWidget(); - this.getWidgetsOfType(widgetTypes).then(widgets => { + this.getWidgetsOfType(this.widgetTypes).then(widgets => { this.widgets = widgets; for (let widget of this.widgets) { this.unpackWidget(widget); } - this.OnWidgetsDiscovered.emit(this.widgets); + this.OnWidgetsDiscovered(this.widgets); this.isLoading = false; this.isUpdating = false; @@ -155,7 +147,8 @@ export class NewWidgetComponent { newData: {}, }, }; - this.OnNewWidgetPrepared.emit(this.newWidget); + console.log("Emitting new widget prepared event"); + this.OnNewWidgetPrepared(this.newWidget); } /** @@ -238,12 +231,12 @@ export class NewWidgetComponent { this.packWidget(this.newWidget); this.isUpdating = true; - this.OnWidgetBeforeAdd.emit(this.newWidget); + this.OnWidgetBeforeAdd(this.newWidget); return this.scalarApi.setWidget(SessionStorage.roomId, this.newWidget) .then(() => this.widgets.push(this.newWidget)) .then(() => { this.isUpdating = false; - this.OnWidgetAfterAdd.emit(this.newWidget); + this.OnWidgetAfterAdd(this.newWidget); this.prepareNewWidget(); this.toaster.pop("success", "Widget added!"); }) @@ -268,11 +261,11 @@ export class NewWidgetComponent { this.packWidget(widget); this.isUpdating = true; - this.OnWidgetBeforeEdit.emit(widget); + this.OnWidgetBeforeEdit(widget); return this.scalarApi.setWidget(SessionStorage.roomId, widget) .then(() => { this.isUpdating = false; - this.OnWidgetAfterEdit.emit(widget); + this.OnWidgetAfterEdit(widget); this.toaster.pop("success", "Widget updated!"); }) .catch(err => { @@ -289,12 +282,12 @@ export class NewWidgetComponent { */ public removeWidget(widget: EditableWidget): Promise { this.isUpdating = true; - this.OnWidgetBeforeDelete.emit(widget); + this.OnWidgetBeforeDelete(widget); return this.scalarApi.deleteWidget(SessionStorage.roomId, widget) .then(() => this.widgets.splice(this.widgets.indexOf(widget), 1)) .then(() => { this.isUpdating = false; - this.OnWidgetAfterDelete.emit(widget); + this.OnWidgetAfterDelete(widget); this.toaster.pop("success", "Widget deleted!"); }) .catch(err => { @@ -310,6 +303,90 @@ export class NewWidgetComponent { */ public resetWidget(widget: EditableWidget) { this.unpackWidget(widget); - this.OnWidgetPreparedForEdit.emit(widget); + this.OnWidgetPreparedForEdit(widget); + } + + // Component hooks below here + // ------------------------------------------------------------------ + + /** + * Called when a new widget has been created in the newWidget field + * @param {EditableWidget} _widget The widget that has been prepared + */ + protected OnNewWidgetPrepared(_widget: EditableWidget): void { + // Component hook + } + + /** + * Called after all the widgets have been discovered and unpacked for the room. + * @param {EditableWidget[]} _widgets The widgets that were discovered + */ + protected OnWidgetsDiscovered(_widgets: EditableWidget[]): void { + // Component hook + } + + /** + * Called before the widget is added to the room, but after the Dimension-specific + * settings have been copied over to the primary fields. + * @param {EditableWidget} _widget The widget that is about to be added + */ + protected OnWidgetBeforeAdd(_widget: EditableWidget): void { + // Component hook + } + + /** + * Called after the widget has been added to the room, but before the newWidget field + * has been set to a new widget. + * @param {EditableWidget} _widget The widget that has been added. + */ + protected OnWidgetAfterAdd(_widget: EditableWidget): void { + // Component hook + } + + /** + * Called when the given widget has been asked to be prepared for editing. At this point + * the widget is not being persisted to the room, it is just updating the EditingWidget's + * properties for the user's ability to edit it. + * @param {EditableWidget} _widget The widget that has been prepared for editing + */ + protected OnWidgetPreparedForEdit(_widget: EditableWidget): void { + // Component hook + } + + /** + * 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. + * This is not called for widgets being deleted. + * @param {EditableWidget} _widget The widget about to be edited + */ + protected OnWidgetBeforeEdit(_widget: EditableWidget): void { + // Component hook + } + + /** + * Called after a given widget has been updated in the room. This is not called for + * widgets being deleted. + * @param {EditableWidget} _widget The widget that has been updated. + */ + protected OnWidgetAfterEdit(_widget: EditableWidget): void { + // Component hook + } + + /** + * Called before the given widget has been removed from the room. No changes to the + * widget have been made at this point. + * @param {EditableWidget} _widget The widget about to be deleted. + */ + protected OnWidgetBeforeDelete(_widget: EditableWidget): void { + // Component hook + } + + /** + * Called after a given widget has been deleted from the room. The widget will be in + * the deleted state and will no longer be tracked anywhere on the component. + * @param {EditableWidget} _widget The widget that has been deleted. + */ + protected OnWidgetAfterDelete(_widget: EditableWidget): void { + // Component hook } } \ No newline at end of file diff --git a/web/app/shared/services/name.service.ts b/web/app/shared/services/name.service.ts new file mode 100644 index 0000000..8abc0df --- /dev/null +++ b/web/app/shared/services/name.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from "@angular/core"; +import * as gobyInit from "goby"; + +const goby = gobyInit.init({ + // Converts words to a url-safe name + // Ie: "hello world how-are you" becomes "HelloWorldHowAreYou" + decorator: parts => parts.map(p => p ? p.split('-').map(p2 => p2 ? p2[0].toUpperCase() + p2.substring(1).toLowerCase() : '').join('') : '').join(''), +}); + +@Injectable() +export class NameService { + constructor() { + } + + public getHumanReadableName(): string { + return goby.generate(["adj", "pre", "suf"]); + } +}