diff --git a/src/db/migrations/20171218203245-AddWidgets.ts b/src/db/migrations/20171218203245-AddWidgets.ts index 5cbddbb..9d60881 100644 --- a/src/db/migrations/20171218203245-AddWidgets.ts +++ b/src/db/migrations/20171218203245-AddWidgets.ts @@ -78,4 +78,4 @@ export default { down: (queryInterface: QueryInterface) => { return queryInterface.dropTable("dimension_widgets"); } -} \ No newline at end of file +} diff --git a/src/db/migrations/20201204142645-AddWhiteboardWidget.ts b/src/db/migrations/20201204142645-AddWhiteboardWidget.ts new file mode 100644 index 0000000..68192c4 --- /dev/null +++ b/src/db/migrations/20201204142645-AddWhiteboardWidget.ts @@ -0,0 +1,24 @@ +import { QueryInterface } from "sequelize"; + +export default { + up: (queryInterface: QueryInterface) => { + return Promise.resolve() + .then(() => queryInterface.bulkInsert("dimension_widgets", [ + { + type: "whiteboard", + name: "Whiteboard", + avatarUrl: "/img/avatars/whiteboard.png", + isEnabled: true, + isPublic: true, + description: "A whiteboard app embedded in the room.", + optionsJson: '{"defaultUrl":"https://cloud13.de/testwhiteboard/?whiteboardid=$roomId_$boardName"}', + } + ])); + }, + down: (queryInterface: QueryInterface) => { + return Promise.resolve() + .then(() => queryInterface.bulkDelete("dimension_widgets", { + type: "whiteboard", + })); + } +} diff --git a/src/integrations/Widget.ts b/src/integrations/Widget.ts index e29da8c..c348834 100644 --- a/src/integrations/Widget.ts +++ b/src/integrations/Widget.ts @@ -5,6 +5,10 @@ export interface EtherpadWidgetOptions { defaultUrl: string; } +export interface WhiteboardWidgetOptions { + defaultUrl: string; +} + export interface JitsiWidgetOptions { jitsiDomain: string; scriptUrl: string; diff --git a/web/app/admin/widgets/whiteboard/whiteboard.component.html b/web/app/admin/widgets/whiteboard/whiteboard.component.html new file mode 100644 index 0000000..68fc80c --- /dev/null +++ b/web/app/admin/widgets/whiteboard/whiteboard.component.html @@ -0,0 +1,22 @@ +
+
+

Whiteboard Widget Configuration

+
+
+ +
+ +
diff --git a/web/app/admin/widgets/whiteboard/whiteboard.component.scss b/web/app/admin/widgets/whiteboard/whiteboard.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/app/admin/widgets/whiteboard/whiteboard.component.ts b/web/app/admin/widgets/whiteboard/whiteboard.component.ts new file mode 100644 index 0000000..6b9c01b --- /dev/null +++ b/web/app/admin/widgets/whiteboard/whiteboard.component.ts @@ -0,0 +1,35 @@ +import { Component } from "@angular/core"; +import { FE_WhiteBoardWidget } from "../../../shared/models/integration"; +import { ToasterService } from "angular2-toaster"; +import { DialogRef, ModalComponent } from "ngx-modialog"; +import { WidgetConfigDialogContext } from "../widgets.component"; +import { AdminIntegrationsApiService } from "../../../shared/services/admin/admin-integrations-api.service"; + +@Component({ + templateUrl: "./whiteboard.component.html", + styleUrls: ["./whiteboard.component.scss", "../config-dialog.scss"], +}) +export class AdminWidgetWhiteboardConfigComponent implements ModalComponent { + + public isUpdating = false; + public widget: FE_WhiteBoardWidget; + private originalWidget: FE_WhiteBoardWidget; + + constructor(public dialog: DialogRef, private adminIntegrationsApi: AdminIntegrationsApiService, private toaster: ToasterService) { + this.originalWidget = dialog.context.widget; + this.widget = JSON.parse(JSON.stringify(this.originalWidget)); + } + + public save() { + this.isUpdating = true; + this.adminIntegrationsApi.setIntegrationOptions(this.widget.category, this.widget.type, this.widget.options).then(() => { + this.originalWidget.options = this.widget.options; + this.toaster.pop("success", "Widget updated"); + this.dialog.close(); + }).catch(err => { + this.isUpdating = false; + console.error(err); + this.toaster.pop("error", "Error updating widget"); + }); + } +} diff --git a/web/app/admin/widgets/widgets.component.ts b/web/app/admin/widgets/widgets.component.ts index 5debd8e..6aab37c 100644 --- a/web/app/admin/widgets/widgets.component.ts +++ b/web/app/admin/widgets/widgets.component.ts @@ -6,6 +6,7 @@ import { Modal, overlayConfigFactory } from "ngx-modialog"; import { BSModalContext } from "ngx-modialog/plugins/bootstrap"; import { AdminWidgetJitsiConfigComponent } from "./jitsi/jitsi.component"; import { AdminIntegrationsApiService } from "../../shared/services/admin/admin-integrations-api.service"; +import { AdminWidgetWhiteboardConfigComponent } from "./whiteboard/whiteboard.component"; export class WidgetConfigDialogContext extends BSModalContext { public widget: FE_Widget; @@ -50,6 +51,7 @@ export class AdminWidgetsComponent { if (widget.type === "etherpad") component = AdminWidgetEtherpadConfigComponent; if (widget.type === "jitsi") component = AdminWidgetJitsiConfigComponent; + if (widget.type === "whiteboard") component = AdminWidgetWhiteboardConfigComponent; if (!component) { console.error("No known dialog component for " + widget.type); @@ -67,6 +69,6 @@ export class AdminWidgetsComponent { public hasConfiguration(widget: FE_Widget) { // Currently only Jitsi and Etherpad have additional configuration - return widget.type === "jitsi" || widget.type === "etherpad"; + return widget.type === "jitsi" || widget.type === "etherpad" || widget.type === "whiteboard"; } } diff --git a/web/app/app.module.ts b/web/app/app.module.ts index 9d9dbf7..c1889ae 100644 --- a/web/app/app.module.ts +++ b/web/app/app.module.ts @@ -121,6 +121,8 @@ import { TermsWidgetWrapperComponent } from "./widget-wrappers/terms/terms.compo import { BigBlueButtonConfigComponent } from "./configs/widget/bigbluebutton/bigbluebutton.widget.component"; import { BigBlueButtonWidgetWrapperComponent } from "./widget-wrappers/bigbluebutton/bigbluebutton.component"; import { BigBlueButtonApiService } from "./shared/services/integrations/bigbluebutton-api.service"; +import { WhiteboardWidgetComponent } from "./configs/widget/whiteboard/whiteboard.widget.component"; +import { AdminWidgetWhiteboardConfigComponent } from "./admin/widgets/whiteboard/whiteboard.component"; @NgModule({ imports: [ @@ -220,6 +222,8 @@ import { BigBlueButtonApiService } from "./shared/services/integrations/bigblueb AdminNewEditTermsComponent, AdminTermsNewEditPublishDialogComponent, TermsWidgetWrapperComponent, + WhiteboardWidgetComponent, + AdminWidgetWhiteboardConfigComponent // Vendor ], @@ -277,6 +281,7 @@ import { BigBlueButtonApiService } from "./shared/services/integrations/bigblueb AdminSlackBridgeManageSelfhostedComponent, AdminLogoutConfirmationDialogComponent, AdminTermsNewEditPublishDialogComponent, + AdminWidgetWhiteboardConfigComponent ] }) export class AppModule { diff --git a/web/app/app.routing.ts b/web/app/app.routing.ts index 8bbd11c..eb778e0 100644 --- a/web/app/app.routing.ts +++ b/web/app/app.routing.ts @@ -49,6 +49,7 @@ import { ManagerTestWidgetWrapperComponent } from "./widget-wrappers/manager-tes import { AdminTermsComponent } from "./admin/terms/terms.component"; import { AdminNewEditTermsComponent } from "./admin/terms/new-edit/new-edit.component"; import { TermsWidgetWrapperComponent } from "./widget-wrappers/terms/terms.component"; +import { WhiteboardWidgetComponent } from "./configs/widget/whiteboard/whiteboard.widget.component"; const routes: Routes = [ {path: "", component: HomeComponent}, @@ -232,6 +233,11 @@ const routes: Routes = [ component: SpotifyWidgetConfigComponent, data: {breadcrumb: "Spotify Widgets", name: "Spotify Widgets"}, }, + { + path: "whiteboard", + component: WhiteboardWidgetComponent, + data: {breadcrumb: "Whiteboard Widgets", name: "Whiteboard Widgets"}, + }, ], }, { diff --git a/web/app/configs/widget/whiteboard/whiteboard.widget.component.html b/web/app/configs/widget/whiteboard/whiteboard.widget.component.html new file mode 100644 index 0000000..49e80b1 --- /dev/null +++ b/web/app/configs/widget/whiteboard/whiteboard.widget.component.html @@ -0,0 +1,18 @@ + + + + + + diff --git a/web/app/configs/widget/whiteboard/whiteboard.widget.component.scss b/web/app/configs/widget/whiteboard/whiteboard.widget.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/app/configs/widget/whiteboard/whiteboard.widget.component.ts b/web/app/configs/widget/whiteboard/whiteboard.widget.component.ts new file mode 100644 index 0000000..e10cd98 --- /dev/null +++ b/web/app/configs/widget/whiteboard/whiteboard.widget.component.ts @@ -0,0 +1,46 @@ +import { WidgetComponent } from "../widget.component"; +import { Component } from "@angular/core"; +import { EditableWidget, WIDGET_WHITEBOARD } from "../../../shared/models/widget"; +import * as url from "url"; +import { SessionStorage } from "../../../shared/SessionStorage"; +import { NameService } from "../../../shared/services/name.service"; +import { FE_WhiteBoardWidget } from "../../../shared/models/integration"; + +@Component({ + templateUrl: "whiteboard.widget.component.html", + styleUrls: ["whiteboard.widget.component.scss"], +}) +export class WhiteboardWidgetComponent extends WidgetComponent { + private whiteBoardWidget: FE_WhiteBoardWidget = SessionStorage.editIntegration; + + constructor(private nameService: NameService) { + super(WIDGET_WHITEBOARD, "Whiteboard", "generic", "whiteboard", "boardName"); + } + protected OnWidgetsDiscovered(widgets: EditableWidget[]): void { + console.log(widgets); + for (const widget of widgets) { + if (!widget.dimension.newUrl.startsWith("http://") && !widget.dimension.newUrl.startsWith("https://")) { + const parsedUrl = url.parse(widget.url, true); + const boardName = parsedUrl.query["boardName"]; + + // Set the new URL so that it unpacks correctly + widget.url = `https://dev-whiteboard.nordeck.net/?whiteboardid=${boardName}`; + } + } + } + + protected OnNewWidgetPrepared(widget: EditableWidget): void { + const name = this.nameService.getHumanReadableName(); + + let template = "https://dev-whiteboard.nordeck.net/?whiteboardid=$roomId_$boardName"; + if (this.whiteBoardWidget.options && this.whiteBoardWidget.options.defaultUrl) { + template = this.whiteBoardWidget.options.defaultUrl; + } + + template = template.replace("$roomId", encodeURIComponent(SessionStorage.roomId)); + template = template.replace("$boardName", encodeURIComponent(name)); + + widget.dimension.newUrl = template; + widget.dimension.newName = name; + } +} diff --git a/web/app/home/home.component.html b/web/app/home/home.component.html index 72a58f1..da34646 100644 --- a/web/app/home/home.component.html +++ b/web/app/home/home.component.html @@ -77,6 +77,10 @@ Custom Widget +
+ + Whiteboard +
diff --git a/web/app/shared/models/integration.ts b/web/app/shared/models/integration.ts index 6d8a444..519cb32 100644 --- a/web/app/shared/models/integration.ts +++ b/web/app/shared/models/integration.ts @@ -99,6 +99,12 @@ export interface FE_BigBlueButtonWidget extends FE_Widget { }; } +export interface FE_WhiteBoardWidget extends FE_Widget { + options: { + defaultUrl: string; + }; +} + export interface FE_IntegrationRequirement { condition: "publicRoom" | "canSendEventTypes" | "userInRoom"; argument: any; diff --git a/web/app/shared/models/widget.ts b/web/app/shared/models/widget.ts index 795189b..4026d4a 100644 --- a/web/app/shared/models/widget.ts +++ b/web/app/shared/models/widget.ts @@ -12,6 +12,7 @@ export const WIDGET_TWITCH = ["twitch", "dimension-twitch"]; // TODO: m.* namesp export const WIDGET_STICKER_PICKER = ["m.stickerpicker"]; export const WIDGET_TRADINGVIEW = ["tradingview", "dimension-tradingview"]; // TODO: Use m.tradingview (https://github.com/turt2live/matrix-dimension/issues/261) export const WIDGET_SPOTIFY = ["m.spotify", "spotify", "dimension-spotify"]; +export const WIDGET_WHITEBOARD = ["whiteboard", "phoenix-whiteboard"]; export interface EditableWidget { /** diff --git a/web/app/shared/registry/integrations.registry.ts b/web/app/shared/registry/integrations.registry.ts index 673836b..4c834bf 100644 --- a/web/app/shared/registry/integrations.registry.ts +++ b/web/app/shared/registry/integrations.registry.ts @@ -11,7 +11,8 @@ import { WIDGET_STICKER_PICKER, WIDGET_TRADINGVIEW, WIDGET_TWITCH, - WIDGET_YOUTUBE + WIDGET_YOUTUBE, + WIDGET_WHITEBOARD } from "../models/widget"; import { FE_Integration } from "../models/integration"; @@ -69,6 +70,9 @@ export class IntegrationsRegistry { "stickerpicker": { types: WIDGET_STICKER_PICKER, }, + "whiteboard": { + type: WIDGET_WHITEBOARD, + } }, }; diff --git a/web/public/img/avatars/whiteboard.png b/web/public/img/avatars/whiteboard.png new file mode 100644 index 0000000..526d155 Binary files /dev/null and b/web/public/img/avatars/whiteboard.png differ