From 4965b61f2de369d1ed8e08c86c2b1232119759b2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 20 Dec 2017 21:28:43 -0700 Subject: [PATCH] Re-wire the UI to support the new backend This still doesn't allow editing, but it supports showing the widgets at least. --- .../dimension/DimensionIntegrationsService.ts | 9 +- .../migrations/20171218203245-AddWidgets.ts | 10 + src-ts/integrations/Integration.ts | 5 + src-ts/integrations/Widget.ts | 10 +- src-ts/matrix/MatrixOpenIdClient.ts | 2 + web/app/app.module.ts | 19 +- .../circleci/circleci-config.component.ts | 4 +- web/app/configs/irc/irc-config.component.ts | 6 +- web/app/configs/rss/rss-config.component.ts | 4 +- .../travisci/travisci-config.component.ts | 4 +- .../custom_widget-config.component.ts | 4 +- .../etherpad/etherpad-config.component.ts | 6 +- .../googlecalendar-config.component.ts | 4 +- .../googledocs/googledocs-config.component.ts | 4 +- .../widget/jitsi/jitsi-config.component.ts | 6 +- .../widget/twitch/twitch-config.component.ts | 4 +- web/app/configs/widget/widget.component.ts | 8 +- .../youtube/youtube-config.component.ts | 4 +- .../integration-bag.component.html | 8 +- .../integration-bag.component.ts | 5 +- web/app/integration/integration.component.ts | 6 +- web/app/riot/riot-home/home.component.ts | 200 ++++++++---------- .../scalar-close/scalar-close.component.ts | 4 +- web/app/shared/models/dimension_responses.ts | 5 + web/app/shared/models/integration.ts | 65 +++--- web/app/shared/models/legacyintegration.ts | 49 +++++ ...esponses.ts => scalar_client_responses.ts} | 0 .../shared/models/scalar_server_responses.ts | 3 + web/app/shared/models/widget.ts | 2 +- web/app/shared/services/AuthedApi.ts | 15 ++ web/app/shared/services/admin-api.service.ts | 10 + .../shared/services/dimension-api.service.ts | 15 ++ .../shared/services/integration.service.ts | 58 ++--- .../services/{ => legacy}/api.service.ts | 6 +- .../legacy}/integration.service.ts | 50 ++--- .../services/{ => legacy}/irc-api.service.ts | 0 ...ervice.ts => scalar-client-api.service.ts} | 12 +- .../services/scalar-server-api.service.ts | 15 ++ .../generic/generic.component.ts | 2 +- .../widget_wrappers/jitsi/jitsi.component.ts | 4 +- 40 files changed, 373 insertions(+), 274 deletions(-) create mode 100644 web/app/shared/models/dimension_responses.ts create mode 100644 web/app/shared/models/legacyintegration.ts rename web/app/shared/models/{scalar_responses.ts => scalar_client_responses.ts} (100%) create mode 100644 web/app/shared/models/scalar_server_responses.ts create mode 100644 web/app/shared/services/AuthedApi.ts create mode 100644 web/app/shared/services/admin-api.service.ts create mode 100644 web/app/shared/services/dimension-api.service.ts rename web/app/shared/services/{ => legacy}/api.service.ts (95%) rename web/app/shared/{ => services/legacy}/integration.service.ts (52%) rename web/app/shared/services/{ => legacy}/irc-api.service.ts (100%) rename web/app/shared/services/{scalar.service.ts => scalar-client-api.service.ts} (91%) create mode 100644 web/app/shared/services/scalar-server-api.service.ts diff --git a/src-ts/api/dimension/DimensionIntegrationsService.ts b/src-ts/api/dimension/DimensionIntegrationsService.ts index 250fe5d..eaf9f38 100644 --- a/src-ts/api/dimension/DimensionIntegrationsService.ts +++ b/src-ts/api/dimension/DimensionIntegrationsService.ts @@ -1,4 +1,4 @@ -import { GET, Path, QueryParam } from "typescript-rest"; +import { GET, Path, PathParam, QueryParam } from "typescript-rest"; import * as Promise from "bluebird"; import { ScalarService } from "../scalar/ScalarService"; import { DimensionStore } from "../../db/DimensionStore"; @@ -35,6 +35,13 @@ export class DimensionIntegrationsService { }); } + @GET + @Path("room/:roomId") + public getIntegrationsInRoom(@QueryParam("scalar_token") scalarToken: string, @PathParam("roomId") roomId: string) :Promise{ + console.log(roomId); + return this.getEnabledIntegrations(scalarToken); + } + private getIntegrations(isEnabledCheck?: boolean): Promise { const cachedResponse = DimensionIntegrationsService.integrationCache.get("integrations_" + isEnabledCheck); if (cachedResponse) { diff --git a/src-ts/db/migrations/20171218203245-AddWidgets.ts b/src-ts/db/migrations/20171218203245-AddWidgets.ts index 1218af7..7ad07fa 100644 --- a/src-ts/db/migrations/20171218203245-AddWidgets.ts +++ b/src-ts/db/migrations/20171218203245-AddWidgets.ts @@ -12,6 +12,7 @@ export default { "avatarUrl": {type: DataType.STRING, allowNull: false}, "description": {type: DataType.STRING, allowNull: false}, "isEnabled": {type: DataType.BOOLEAN, allowNull: false}, + "isPublic": {type: DataType.BOOLEAN, allowNull: false}, "optionsJson": {type: DataType.STRING, allowNull: true}, })) .then(() => queryInterface.bulkInsert("dimension_widgets", [ @@ -64,6 +65,15 @@ export default { avatarUrl: "/img/avatars/twitch.png", description: "Embed a Twitch livestream into your room.", }, + { + type: "jitsi", + name: "Jitsi Conference", + isEnabled: true, + isPublic: true, + avatarUrl: "/img/avatars/jitsi.png", + description: "Hold a video conference with Jitsi Meet", + optionsJson: '{"jitsiDomain":"jitsi.riot.im", "scriptUrl":"https://jitsi.riot.im/libs/external_api.min.js"}', + }, ])); }, down: (queryInterface: QueryInterface) => { diff --git a/src-ts/integrations/Integration.ts b/src-ts/integrations/Integration.ts index 808e896..38e74ec 100644 --- a/src-ts/integrations/Integration.ts +++ b/src-ts/integrations/Integration.ts @@ -5,6 +5,7 @@ export class Integration { public category: "bot" | "complex-bot" | "bridge" | "widget"; public type: string; public requirements: IntegrationRequirement[]; + public isEncryptionSupported = false; // These are meant to be set by us public displayName: string; @@ -26,5 +27,9 @@ export class Integration { export interface IntegrationRequirement { condition: "publicRoom" | "canSendEventTypes"; argument: any; + + // For publicRoom this is true or false (boolean) + // For canSendEventTypes this is an array of {isState: boolean, type: string} + // For userInRoom this is the user ID expectedValue: any; } \ No newline at end of file diff --git a/src-ts/integrations/Widget.ts b/src-ts/integrations/Widget.ts index d546b75..536d3b8 100644 --- a/src-ts/integrations/Widget.ts +++ b/src-ts/integrations/Widget.ts @@ -5,6 +5,11 @@ export interface EtherpadWidgetOptions { defaultUrl: string; } +export interface JitsiWidgetOptions { + jitsiDomain: string; + scriptUrl: string; +} + export class Widget extends Integration { public options: any; @@ -14,8 +19,11 @@ export class Widget extends Integration { this.options = widgetRecord.optionsJson ? JSON.parse(widgetRecord.optionsJson) : {}; this.requirements = [{ condition: "canSendEventTypes", - argument: ["im.vector.widget"], + argument: [{isState: true, type: "im.vector.widget"}], expectedValue: true, }]; + + // Technically widgets are supported in encrypted rooms, although at risk. + this.isEncryptionSupported = true; } } \ No newline at end of file diff --git a/src-ts/matrix/MatrixOpenIdClient.ts b/src-ts/matrix/MatrixOpenIdClient.ts index beec738..fe516f5 100644 --- a/src-ts/matrix/MatrixOpenIdClient.ts +++ b/src-ts/matrix/MatrixOpenIdClient.ts @@ -14,6 +14,8 @@ export class MatrixOpenIdClient { "/_matrix/federation/v1/openid/userinfo", {access_token: this.openId.access_token} ).then(response => { + // Annoyingly, the response isn't JSON for this + response = JSON.parse(response); return response['sub']; }); } diff --git a/web/app/app.module.ts b/web/app/app.module.ts index 440c96c..d3474fe 100644 --- a/web/app/app.module.ts +++ b/web/app/app.module.ts @@ -8,16 +8,14 @@ import { routing } from "./app.routing"; import { createNewHosts, removeNgStyles } from "@angularclass/hmr"; import { NgbModule } from "@ng-bootstrap/ng-bootstrap"; import { RiotComponent } from "./riot/riot.component"; -import { ApiService } from "./shared/services/api.service"; import { UiSwitchModule } from "angular2-ui-switch"; -import { ScalarService } from "./shared/services/scalar.service"; +import { ScalarClientApiService } from "./shared/services/scalar-client-api.service"; import { ToasterModule } from "angular2-toaster"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { ScalarCloseComponent } from "./riot/scalar-close/scalar-close.component"; -import { IntegrationService } from "./shared/services/integration.service"; +import { LegacyIntegrationService } from "./shared/services/legacy/integration.service"; import { BootstrapModalModule } from "ngx-modialog/plugins/bootstrap"; import { ModalModule } from "ngx-modialog"; -import { IrcApiService } from "./shared/services/irc-api.service"; import { GenericWidgetWrapperComponent } from "./widget_wrappers/generic/generic.component"; import { ToggleFullscreenDirective } from "./shared/directives/toggle-fullscreen.directive"; import { FullscreenButtonComponent } from "./fullscreen-button/fullscreen-button.component"; @@ -30,8 +28,11 @@ import { BreadcrumbsModule } from "ng2-breadcrumbs"; import { RiotHomeComponent } from "./riot/riot-home/home.component"; import { IntegrationBagComponent } from "./integration-bag/integration-bag.component"; import { IntegrationComponent } from "./integration/integration.component"; +import { ScalarServerApiService } from "./shared/services/scalar-server-api.service"; +import { DimensionApiService } from "./shared/services/dimension-api.service"; +import { AdminApiService } from "./shared/services/admin-api.service"; -const WIDGET_CONFIGURATION_COMPONENTS: any[] = IntegrationService.getAllConfigComponents(); +const WIDGET_CONFIGURATION_COMPONENTS: any[] = LegacyIntegrationService.getAllConfigComponents(); @NgModule({ imports: [ @@ -68,10 +69,10 @@ const WIDGET_CONFIGURATION_COMPONENTS: any[] = IntegrationService.getAllConfigCo // Vendor ], providers: [ - ApiService, - ScalarService, - IntegrationService, - IrcApiService, + ScalarClientApiService, + ScalarServerApiService, + DimensionApiService, + AdminApiService, {provide: Window, useValue: window}, // Vendor diff --git a/web/app/configs/circleci/circleci-config.component.ts b/web/app/configs/circleci/circleci-config.component.ts index 384065e..ee0f9bf 100644 --- a/web/app/configs/circleci/circleci-config.component.ts +++ b/web/app/configs/circleci/circleci-config.component.ts @@ -1,9 +1,9 @@ import { Component } from "@angular/core"; -import { CircleCiIntegration } from "../../shared/models/integration"; +import { CircleCiIntegration } from "../../shared/models/legacyintegration"; import { ModalComponent, DialogRef } from "ngx-modialog"; import { ConfigModalContext } from "../../integration/integration.component"; import { ToasterService } from "angular2-toaster"; -import { ApiService } from "../../shared/services/api.service"; +import { ApiService } from "../../shared/services/legacy/api.service"; @Component({ selector: "my-circleci-config", diff --git a/web/app/configs/irc/irc-config.component.ts b/web/app/configs/irc/irc-config.component.ts index 4bf0d67..822c431 100644 --- a/web/app/configs/irc/irc-config.component.ts +++ b/web/app/configs/irc/irc-config.component.ts @@ -1,11 +1,11 @@ import { Component, OnDestroy } from "@angular/core"; -import { IRCIntegration } from "../../shared/models/integration"; +import { IRCIntegration } from "../../shared/models/legacyintegration"; import { ModalComponent, DialogRef } from "ngx-modialog"; import { ConfigModalContext } from "../../integration/integration.component"; -import { IrcApiService } from "../../shared/services/irc-api.service"; +import { IrcApiService } from "../../shared/services/legacy/irc-api.service"; import { ToasterService } from "angular2-toaster"; import { IntervalObservable } from "rxjs/observable/IntervalObservable"; -import { ApiService } from "../../shared/services/api.service"; +import { ApiService } from "../../shared/services/legacy/api.service"; import { Subscription } from "rxjs"; @Component({ diff --git a/web/app/configs/rss/rss-config.component.ts b/web/app/configs/rss/rss-config.component.ts index fdc342b..c906714 100644 --- a/web/app/configs/rss/rss-config.component.ts +++ b/web/app/configs/rss/rss-config.component.ts @@ -1,9 +1,9 @@ import { Component } from "@angular/core"; -import { RSSIntegration } from "../../shared/models/integration"; +import { RSSIntegration } from "../../shared/models/legacyintegration"; import { ModalComponent, DialogRef } from "ngx-modialog"; import { ConfigModalContext } from "../../integration/integration.component"; import { ToasterService } from "angular2-toaster"; -import { ApiService } from "../../shared/services/api.service"; +import { ApiService } from "../../shared/services/legacy/api.service"; @Component({ selector: "my-rss-config", diff --git a/web/app/configs/travisci/travisci-config.component.ts b/web/app/configs/travisci/travisci-config.component.ts index f755946..58cce40 100644 --- a/web/app/configs/travisci/travisci-config.component.ts +++ b/web/app/configs/travisci/travisci-config.component.ts @@ -1,9 +1,9 @@ import { Component } from "@angular/core"; -import { TravisCiIntegration } from "../../shared/models/integration"; +import { TravisCiIntegration } from "../../shared/models/legacyintegration"; import { ModalComponent, DialogRef } from "ngx-modialog"; import { ConfigModalContext } from "../../integration/integration.component"; import { ToasterService } from "angular2-toaster"; -import { ApiService } from "../../shared/services/api.service"; +import { ApiService } from "../../shared/services/legacy/api.service"; @Component({ selector: "my-travisci-config", 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 index 27c15e6..9cf559a 100644 --- a/web/app/configs/widget/custom_widget/custom_widget-config.component.ts +++ b/web/app/configs/widget/custom_widget/custom_widget-config.component.ts @@ -1,7 +1,7 @@ import { Component } from "@angular/core"; import { DialogRef, ModalComponent } from "ngx-modialog"; import { WidgetComponent } from "../widget.component"; -import { ScalarService } from "../../../shared/services/scalar.service"; +import { ScalarClientApiService } from "../../../shared/services/scalar-client-api.service"; import { ConfigModalContext } from "../../../integration/integration.component"; import { ToasterService } from "angular2-toaster"; import { WIDGET_CUSTOM } from "../../../shared/models/widget"; @@ -15,7 +15,7 @@ export class CustomWidgetConfigComponent extends WidgetComponent implements Moda constructor(public dialog: DialogRef, toaster: ToasterService, - scalarService: ScalarService, + scalarService: ScalarClientApiService, window: Window) { super( window, diff --git a/web/app/configs/widget/etherpad/etherpad-config.component.ts b/web/app/configs/widget/etherpad/etherpad-config.component.ts index 1afc303..ff84f58 100644 --- a/web/app/configs/widget/etherpad/etherpad-config.component.ts +++ b/web/app/configs/widget/etherpad/etherpad-config.component.ts @@ -1,11 +1,11 @@ import { Component } from "@angular/core"; import { DialogRef, ModalComponent } from "ngx-modialog"; import { WidgetComponent } from "../widget.component"; -import { ScalarService } from "../../../shared/services/scalar.service"; +import { ScalarClientApiService } from "../../../shared/services/scalar-client-api.service"; import { ConfigModalContext } from "../../../integration/integration.component"; import { ToasterService } from "angular2-toaster"; import { WIDGET_ETHERPAD } from "../../../shared/models/widget"; -import { EtherpadWidgetIntegration } from "../../../shared/models/integration"; +import { EtherpadWidgetIntegration } from "../../../shared/models/legacyintegration"; @Component({ selector: "my-etherpadwidget-config", @@ -18,7 +18,7 @@ export class EtherpadWidgetConfigComponent extends WidgetComponent implements Mo constructor(public dialog: DialogRef, toaster: ToasterService, - scalarService: ScalarService, + scalarService: ScalarClientApiService, window: Window) { super( window, diff --git a/web/app/configs/widget/googlecalendar/googlecalendar-config.component.ts b/web/app/configs/widget/googlecalendar/googlecalendar-config.component.ts index 6fefa91..1710075 100644 --- a/web/app/configs/widget/googlecalendar/googlecalendar-config.component.ts +++ b/web/app/configs/widget/googlecalendar/googlecalendar-config.component.ts @@ -1,7 +1,7 @@ import { Component } from "@angular/core"; import { DialogRef, ModalComponent } from "ngx-modialog"; import { WidgetComponent } from "../widget.component"; -import { ScalarService } from "../../../shared/services/scalar.service"; +import { ScalarClientApiService } from "../../../shared/services/scalar-client-api.service"; import { ConfigModalContext } from "../../../integration/integration.component"; import { ToasterService } from "angular2-toaster"; import { EditableWidget, WIDGET_GOOGLE_CALENDAR } from "../../../shared/models/widget"; @@ -15,7 +15,7 @@ export class GoogleCalendarWidgetConfigComponent extends WidgetComponent impleme constructor(public dialog: DialogRef, toaster: ToasterService, - scalarService: ScalarService, + scalarService: ScalarClientApiService, window: Window) { super( window, diff --git a/web/app/configs/widget/googledocs/googledocs-config.component.ts b/web/app/configs/widget/googledocs/googledocs-config.component.ts index 74f25e4..e9fe721 100644 --- a/web/app/configs/widget/googledocs/googledocs-config.component.ts +++ b/web/app/configs/widget/googledocs/googledocs-config.component.ts @@ -1,7 +1,7 @@ import { Component } from "@angular/core"; import { DialogRef, ModalComponent } from "ngx-modialog"; import { WidgetComponent } from "../widget.component"; -import { ScalarService } from "../../../shared/services/scalar.service"; +import { ScalarClientApiService } from "../../../shared/services/scalar-client-api.service"; import { ConfigModalContext } from "../../../integration/integration.component"; import { ToasterService } from "angular2-toaster"; import { WIDGET_GOOGLE_DOCS } from "../../../shared/models/widget"; @@ -15,7 +15,7 @@ export class GoogleDocsWidgetConfigComponent extends WidgetComponent implements constructor(public dialog: DialogRef, toaster: ToasterService, - scalarService: ScalarService, + scalarService: ScalarClientApiService, window: Window) { super( window, diff --git a/web/app/configs/widget/jitsi/jitsi-config.component.ts b/web/app/configs/widget/jitsi/jitsi-config.component.ts index fc5a3f1..dae5091 100644 --- a/web/app/configs/widget/jitsi/jitsi-config.component.ts +++ b/web/app/configs/widget/jitsi/jitsi-config.component.ts @@ -1,11 +1,11 @@ import { Component } from "@angular/core"; import { DialogRef, ModalComponent } from "ngx-modialog"; import { WidgetComponent } from "../widget.component"; -import { ScalarService } from "../../../shared/services/scalar.service"; +import { ScalarClientApiService } from "../../../shared/services/scalar-client-api.service"; import { ConfigModalContext } from "../../../integration/integration.component"; import { ToasterService } from "angular2-toaster"; import { EditableWidget, WIDGET_JITSI } from "../../../shared/models/widget"; -import { JitsiWidgetIntegration } from "../../../shared/models/integration"; +import { JitsiWidgetIntegration } from "../../../shared/models/legacyintegration"; import * as gobyInit from "goby"; import * as url from "url"; @@ -26,7 +26,7 @@ export class JitsiWidgetConfigComponent extends WidgetComponent implements Modal constructor(public dialog: DialogRef, toaster: ToasterService, - scalarService: ScalarService, + scalarService: ScalarClientApiService, window: Window) { super( window, diff --git a/web/app/configs/widget/twitch/twitch-config.component.ts b/web/app/configs/widget/twitch/twitch-config.component.ts index e15e2b5..2e8e334 100644 --- a/web/app/configs/widget/twitch/twitch-config.component.ts +++ b/web/app/configs/widget/twitch/twitch-config.component.ts @@ -1,7 +1,7 @@ import { Component } from "@angular/core"; import { DialogRef, ModalComponent } from "ngx-modialog"; import { WidgetComponent } from "../widget.component"; -import { ScalarService } from "../../../shared/services/scalar.service"; +import { ScalarClientApiService } from "../../../shared/services/scalar-client-api.service"; import { ConfigModalContext } from "../../../integration/integration.component"; import { ToasterService } from "angular2-toaster"; import { EditableWidget, WIDGET_TWITCH } from "../../../shared/models/widget"; @@ -15,7 +15,7 @@ export class TwitchWidgetConfigComponent extends WidgetComponent implements Moda constructor(public dialog: DialogRef, toaster: ToasterService, - scalarService: ScalarService, + scalarService: ScalarClientApiService, window: Window) { super( window, diff --git a/web/app/configs/widget/widget.component.ts b/web/app/configs/widget/widget.component.ts index 1821178..6816fda 100644 --- a/web/app/configs/widget/widget.component.ts +++ b/web/app/configs/widget/widget.component.ts @@ -1,7 +1,7 @@ -import { ScalarService } from "../../shared/services/scalar.service"; +import { ScalarClientApiService } from "../../shared/services/scalar-client-api.service"; import { convertScalarWidgetsToDtos, EditableWidget } from "../../shared/models/widget"; import { ToasterService } from "angular2-toaster"; -import { Integration } from "../../shared/models/integration"; +import { LegacyIntegration } from "../../shared/models/legacyintegration"; const SCALAR_WIDGET_LINKS = [ "https://scalar-staging.riot.im/scalar/api/widgets/__TYPE__.html?url=", @@ -23,9 +23,9 @@ export class WidgetComponent { constructor(window: Window, protected toaster: ToasterService, - protected scalarApi: ScalarService, + protected scalarApi: ScalarClientApiService, public roomId: string, - public integration: Integration, + public integration: LegacyIntegration, editWidgetId: string, private widgetIds: string[], private defaultName: string, diff --git a/web/app/configs/widget/youtube/youtube-config.component.ts b/web/app/configs/widget/youtube/youtube-config.component.ts index 938f667..0962728 100644 --- a/web/app/configs/widget/youtube/youtube-config.component.ts +++ b/web/app/configs/widget/youtube/youtube-config.component.ts @@ -1,7 +1,7 @@ import { Component } from "@angular/core"; import { DialogRef, ModalComponent } from "ngx-modialog"; import { WidgetComponent } from "../widget.component"; -import { ScalarService } from "../../../shared/services/scalar.service"; +import { ScalarClientApiService } from "../../../shared/services/scalar-client-api.service"; import { ConfigModalContext } from "../../../integration/integration.component"; import { ToasterService } from "angular2-toaster"; import { EditableWidget, WIDGET_YOUTUBE } from "../../../shared/models/widget"; @@ -17,7 +17,7 @@ export class YoutubeWidgetConfigComponent extends WidgetComponent implements Mod constructor(public dialog: DialogRef, toaster: ToasterService, - scalarService: ScalarService, + scalarService: ScalarClientApiService, window: Window) { super( window, diff --git a/web/app/integration-bag/integration-bag.component.html b/web/app/integration-bag/integration-bag.component.html index 29e2b3c..a74afe6 100644 --- a/web/app/integration-bag/integration-bag.component.html +++ b/web/app/integration-bag/integration-bag.component.html @@ -1,10 +1,8 @@
- -
- -
{{ integration.name }}
-
{{ integration.about }}
+ +
{{ integration.displayName }}
+
{{ integration.description }}
\ No newline at end of file diff --git a/web/app/integration-bag/integration-bag.component.ts b/web/app/integration-bag/integration-bag.component.ts index dfd1874..7e25b29 100644 --- a/web/app/integration-bag/integration-bag.component.ts +++ b/web/app/integration-bag/integration-bag.component.ts @@ -1,6 +1,7 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { Integration } from "../shared/models/integration"; +import { LegacyIntegration } from "../shared/models/legacyintegration"; import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser"; +import { Integration } from "../shared/models/integration"; @Component({ selector: "my-integration-bag", @@ -9,7 +10,7 @@ import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser"; }) export class IntegrationBagComponent { - @Input() integrations: Integration[]; + @Input() integrations: LegacyIntegration[]; @Output() integrationClicked: EventEmitter = new EventEmitter(); constructor(private sanitizer: DomSanitizer) { diff --git a/web/app/integration/integration.component.ts b/web/app/integration/integration.component.ts index 5416c8b..82fa88c 100644 --- a/web/app/integration/integration.component.ts +++ b/web/app/integration/integration.component.ts @@ -1,10 +1,10 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { Integration } from "../shared/models/integration"; +import { LegacyIntegration } from "../shared/models/legacyintegration"; import { BSModalContext } from "ngx-modialog/plugins/bootstrap"; import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser"; export class ConfigModalContext extends BSModalContext { - public integration: Integration; + public integration: LegacyIntegration; public roomId: string; public userId: string; public scalarToken: string; @@ -18,7 +18,7 @@ export class ConfigModalContext extends BSModalContext { }) export class IntegrationComponent { - @Input() integration: Integration; + @Input() integration: LegacyIntegration; @Output() selected: EventEmitter = new EventEmitter(); constructor(private sanitizer: DomSanitizer) { diff --git a/web/app/riot/riot-home/home.component.ts b/web/app/riot/riot-home/home.component.ts index fcb10f0..9b94f9c 100644 --- a/web/app/riot/riot-home/home.component.ts +++ b/web/app/riot/riot-home/home.component.ts @@ -1,12 +1,13 @@ -import { Component, ViewChildren } from "@angular/core"; -import { IntegrationService } from "../../shared/services/integration.service"; -import { IntegrationComponent } from "../../integration/integration.component"; +import { Component } from "@angular/core"; import { ToasterService } from "angular2-toaster"; -import { Integration } from "../../shared/models/integration"; import { ActivatedRoute } from "@angular/router"; -import { ApiService } from "../../shared/services/api.service"; -import { ScalarService } from "../../shared/services/scalar.service"; +import { ScalarClientApiService } from "../../shared/services/scalar-client-api.service"; import * as _ from "lodash"; +import { ScalarServerApiService } from "../../shared/services/scalar-server-api.service"; +import { AuthedApi } from "../../shared/services/AuthedApi"; +import { DimensionApiService } from "../../shared/services/dimension-api.service"; +import { Integration, IntegrationRequirement } from "../../shared/models/integration"; +import { IntegrationService } from "../../shared/services/integration.service"; const CATEGORY_MAP = { "Widgets": ["widget"], @@ -20,14 +21,11 @@ const CATEGORY_MAP = { styleUrls: ["./home.component.scss"], }) export class RiotHomeComponent { - @ViewChildren(IntegrationComponent) integrationComponents: Array; - public isLoading = true; public isError = false; public errorMessage: string; public isRoomEncrypted: boolean; - private scalarToken: string; private roomId: string; private userId: string; private requestedScreen: string = null; @@ -36,8 +34,9 @@ export class RiotHomeComponent { private categoryMap: { [categoryName: string]: string[] } = CATEGORY_MAP; constructor(private activatedRoute: ActivatedRoute, - private api: ApiService, - private scalar: ScalarService, + private scalarApi: ScalarServerApiService, + private scalar: ScalarClientApiService, + private dimensionApi: DimensionApiService, private toaster: ToasterService) { let params: any = this.activatedRoute.snapshot.queryParams; @@ -51,9 +50,10 @@ export class RiotHomeComponent { this.errorMessage = "Unable to load Dimension - missing room ID or token."; } else { this.roomId = params.room_id; - this.scalarToken = params.scalar_token; + AuthedApi.SCALAR_TOKEN = params.scalar_token; - this.api.getTokenOwner(params.scalar_token).then(userId => { + this.scalarApi.getAccount().then(response => { + const userId = response.user_id; if (!userId) { console.error("No user returned for token. Is the token registered in Dimension?"); this.isError = true; @@ -89,31 +89,42 @@ export class RiotHomeComponent { return this.integrationsForCategory[category]; } - public modifyIntegration(integration: Integration) { - console.log(this.userId + " is trying to modify " + integration.name); + private getIntegrations(): Integration[] { + const result: Integration[] = []; - if (integration.hasAdditionalConfig) { - // TODO: Navigate to edit screen - console.log("EDIT SCREEN FOR " + integration.name); - } else { - // It's a flip-a-bit (simple bot) + for (const category of this.getCategories()) { + for (const integration of this.getIntegrationsIn(category)) { + result.push(integration); + } + } + + return result; + } + + + public modifyIntegration(integration: Integration) { + console.log(this.userId + " is trying to modify " + integration.displayName); + + if (integration.category === "bot") { + // It's a bot // TODO: "Are you sure?" dialog - let promise = null; - if (!integration.isEnabled) { - promise = this.scalar.inviteUser(this.roomId, integration.userId); - } else promise = this.api.removeIntegration(this.roomId, integration.type, integration.integrationType, this.scalarToken); - + // let promise = null; + const promise = Promise.resolve(); + // if (!integration._inRoom) { + // promise = this.scalar.inviteUser(this.roomId, integration.userId); + // } else promise = this.api.removeIntegration(this.roomId, integration.type, integration.integrationType, this.scalarToken); // We set this ahead of the promise for debouncing - integration.isEnabled = !integration.isEnabled; - integration.isUpdating = true; + + integration._inRoom = !integration._inRoom; + integration._isUpdating = true; promise.then(() => { - integration.isUpdating = false; - if (integration.isEnabled) this.toaster.pop("success", integration.name + " was invited to the room"); - else this.toaster.pop("success", integration.name + " was removed from the room"); + integration._isUpdating = false; + if (integration._inRoom) this.toaster.pop("success", integration.displayName + " was invited to the room"); + else this.toaster.pop("success", integration.displayName + " was removed from the room"); }).catch(err => { - integration.isEnabled = !integration.isEnabled; // revert the status change - integration.isUpdating = false; + integration._inRoom = !integration._inRoom; // revert the status change + integration._isUpdating = false; console.error(err); let errorMessage = null; @@ -122,31 +133,27 @@ export class RiotHomeComponent { if (!errorMessage) errorMessage = "Could not update integration status"; this.toaster.pop("error", errorMessage); - }) + }); + } else { + // TODO: Navigate to edit screen + console.log("EDIT SCREEN FOR " + integration.displayName); } } private prepareIntegrations() { this.scalar.isRoomEncrypted(this.roomId).then(payload => { this.isRoomEncrypted = payload.response; - return this.api.getIntegrations(this.roomId, this.scalarToken); - }).then(integrations => { + return this.dimensionApi.getIntegrations(this.roomId); + }).then(response => { + const integrations: Integration[] = _.flatten(Object.keys(response).map(k => response[k])); const supportedIntegrations: Integration[] = _.filter(integrations, i => IntegrationService.isSupported(i)); - for (const integration of supportedIntegrations) { - // Widgets technically support encrypted rooms, so unless they explicitly declare that - // they don't, we'll assume they do. A warning about adding widgets in encrypted rooms - // is displayed to users elsewhere. - if (integration.type === "widget" && integration.supportsEncryptedRooms !== false) - integration.supportsEncryptedRooms = true; - } - // Flag integrations that aren't supported in encrypted rooms if (this.isRoomEncrypted) { for (const integration of supportedIntegrations) { - if (!integration.supportsEncryptedRooms) { - integration.isSupported = false; - integration.notSupportedReason = "This integration is not supported in encrypted rooms"; + if (!integration.isEncryptionSupported) { + integration._isSupported = false; + integration._notSupportedReason = "This integration is not supported in encrypted rooms"; } } } @@ -154,7 +161,7 @@ export class RiotHomeComponent { // Set up the categories for (const category of Object.keys(this.categoryMap)) { const supportedTypes = this.categoryMap[category]; - this.integrationsForCategory[category] = _.filter(supportedIntegrations, i => supportedTypes.indexOf(i.type) !== -1); + this.integrationsForCategory[category] = _.filter(supportedIntegrations, i => supportedTypes.indexOf(i.category) !== -1); } let promises = supportedIntegrations.map(i => this.updateIntegrationState(i)); @@ -173,92 +180,57 @@ export class RiotHomeComponent { } private tryOpenConfigScreen() { + let category = null; let type = null; - let integrationType = null; if (!this.requestedScreen) return; const targetIntegration = IntegrationService.getIntegrationForScreen(this.requestedScreen); if (targetIntegration) { + category = targetIntegration.category; type = targetIntegration.type; - integrationType = targetIntegration.integrationType; } else { console.log("Unknown screen requested: " + this.requestedScreen); } - let opened = false; - this.integrationComponents.forEach(component => { - if (opened) return; - if (component.integration.type !== type || component.integration.integrationType !== integrationType) return; - console.log("Configuring integration " + this.requestedIntegration + " type=" + type + " integrationType=" + integrationType); - //component.configureIntegration(this.requestedIntegration); - // TODO: Support editing integrations - opened = true; - }); - if (!opened) { - console.log("Failed to find integration component for type=" + type + " integrationType=" + integrationType); + for (const integration of this.getIntegrations()) { + if (integration.category === category && integration.type === type) { + console.log("Configuring integration " + this.requestedIntegration + " category=" + category + " type=" + type); + // TODO: Actually edit integration + return; + } } + + console.log("Failed to find integration component for category=" + category + " type=" + type); } private updateIntegrationState(integration: Integration) { - integration.hasAdditionalConfig = IntegrationService.hasConfig(integration); + if (!integration.requirements) return; - if (integration.type === "widget") { - if (!integration.requirements) integration.requirements = {}; - integration.requirements["canSetWidget"] = true; - } + let promises = integration.requirements.map(r => this.checkRequirement(r)); - // If the integration has requirements, then we'll check those instead of anything else - if (integration.requirements) { - let keys = _.keys(integration.requirements); - let promises = []; - - for (let key of keys) { - let requirement = this.checkRequirement(integration, key); - promises.push(requirement); - } - - return Promise.all(promises).then(() => { - integration.isSupported = true; - integration.notSupportedReason = null; - }, error => { - console.error(error); - integration.isSupported = false; - integration.notSupportedReason = error; - }); - } - - // The integration doesn't have requirements, so we'll just make sure the bot user can be retrieved. - return this.scalar.getMembershipState(this.roomId, integration.userId).then(payload => { - if (payload.response) { - integration.isSupported = true; - integration.notSupportedReason = null; - integration.isEnabled = (payload.response.membership === "join" || payload.response.membership === "invite"); - } else { - console.error("No response received to membership query of " + integration.userId); - integration.isSupported = false; - integration.notSupportedReason = "Unable to query membership state for this bot"; - } - }, (error) => { + return Promise.all(promises).then(() => { + integration._isSupported = true; + integration._notSupportedReason = null; + }, error => { console.error(error); - integration.isSupported = false; - integration.notSupportedReason = "Unable to query membership state for this bot"; + integration._isSupported = false; + integration._notSupportedReason = error; }); } - private checkRequirement(integration: Integration, key: string) { - let requirement = integration.requirements[key]; - - switch (key) { - case "joinRule": + private checkRequirement(requirement: IntegrationRequirement) { + switch (requirement.condition) { + case "publicRoom": return this.scalar.getJoinRule(this.roomId).then(payload => { if (!payload.response) { return Promise.reject("Could not communicate with Riot"); } - return payload.response.join_rule === requirement - ? Promise.resolve() - : Promise.reject("The room must be " + requirement + " to use this integration."); + const isPublic = payload.response.join_rule === "public"; + if (isPublic !== requirement.expectedValue) { + return Promise.reject("The room must be " + (isPublic ? "non-public" : "public") + " to use this integration"); + } else return Promise.resolve(); }); - case "canSetWidget": + case "canSendEventTypes": const processPayload = payload => { const response = payload.response; if (response === true) return Promise.resolve(); @@ -266,9 +238,17 @@ export class RiotHomeComponent { return Promise.reject("You cannot modify widgets in this room"); return Promise.reject("Error communicating with Riot"); }; - return this.scalar.canSendEvent(this.roomId, "im.vector.modular.widgets", true).then(processPayload).catch(processPayload); + + let promiseChain = Promise.resolve(); + requirement.argument.forEach(e => promiseChain = promiseChain.then(() => this.scalar.canSendEvent(this.roomId, e.type, e.isState).then(processPayload).catch(processPayload))); + return promiseChain.then(() => { + if (!requirement.expectedValue) return Promise.reject("Expected to not be able to send specific event types"); + }).catch(err => { + console.error(err); + if (requirement.expectedValue) return Promise.reject("Expected to be able to send specific event types"); + }); default: - return Promise.reject("Requirement '" + key + "' not found"); + return Promise.reject("Requirement '" + requirement.condition + "' not found"); } } } diff --git a/web/app/riot/scalar-close/scalar-close.component.ts b/web/app/riot/scalar-close/scalar-close.component.ts index 2c073bc..9d1e85f 100644 --- a/web/app/riot/scalar-close/scalar-close.component.ts +++ b/web/app/riot/scalar-close/scalar-close.component.ts @@ -1,5 +1,5 @@ import { Component } from "@angular/core"; -import { ScalarService } from "../../shared/services/scalar.service"; +import { ScalarClientApiService } from "../../shared/services/scalar-client-api.service"; @Component({ selector: "my-scalar-close", @@ -8,7 +8,7 @@ import { ScalarService } from "../../shared/services/scalar.service"; }) export class ScalarCloseComponent { - constructor(private scalar: ScalarService) { + constructor(private scalar: ScalarClientApiService) { } public closeScalar() { diff --git a/web/app/shared/models/dimension_responses.ts b/web/app/shared/models/dimension_responses.ts new file mode 100644 index 0000000..4da941f --- /dev/null +++ b/web/app/shared/models/dimension_responses.ts @@ -0,0 +1,5 @@ +import { Widget } from "./integration"; + +export interface DimensionIntegrationsResponse { + widgets: Widget[]; +} \ No newline at end of file diff --git a/web/app/shared/models/integration.ts b/web/app/shared/models/integration.ts index 61418b4..949bce4 100644 --- a/web/app/shared/models/integration.ts +++ b/web/app/shared/models/integration.ts @@ -1,49 +1,40 @@ export interface Integration { - // These are from the server + category: "bot" | "complex-bot" | "bridge" | "widget"; type: string; - integrationType: string; - userId: string; - name: string; - avatar: string; - about: string; // nullable - supportsEncryptedRooms: boolean; - requirements: any; // nullable + requirements: IntegrationRequirement[]; + isEncryptionSupported: boolean; + displayName: string; + avatarUrl: string; + description: string; + isEnabled: boolean; + isPublic: boolean; - // These are set in the UI - isSupported: boolean; - notSupportedReason: string; - hasAdditionalConfig: boolean; - isEnabled: boolean; // for the flip-a-bit integrations - isUpdating: boolean; + // Used by us + _inRoom: boolean; + _isUpdating: boolean; + _isSupported: boolean; + _notSupportedReason: string; } -export interface RSSIntegration extends Integration { - feeds: string[]; - immutableFeeds: { url: string, ownerId: string }[]; +export interface Widget extends Integration { + options: any; } -export interface TravisCiIntegration extends Integration { - repoTemplates: { repoKey: string, template: string, newTemplate: string }[]; // newTemplate is local - immutableRepoTemplates: { repoKey: string, template: string, ownerId: string }[]; - webhookUrl: string; // immutable +export interface EtherpadWidget extends Widget { + options: { + defaultUrl: string; + }; } -export interface CircleCiIntegration extends Integration { - repoTemplates: { repoKey: string, template: string, newTemplate: string }[]; // newTemplate is local - immutableRepoTemplates: { repoKey: string, template: string, ownerId: string }[]; - webhookUrl: string; // immutable +export interface JitsiWidget extends Widget { + options: { + jitsiDomain: string; + scriptUrl: string; + }; } -export interface IRCIntegration extends Integration { - availableNetworks: { name: string, id: string }[]; - channels: { [networkId: string]: string[] }; -} - -export interface EtherpadWidgetIntegration extends Integration { - defaultUrl: string; -} - -export interface JitsiWidgetIntegration extends Integration { - jitsiDomain: string; - scriptUrl: string +export interface IntegrationRequirement { + condition: "publicRoom" | "canSendEventTypes"; + argument: any; + expectedValue: any; } \ No newline at end of file diff --git a/web/app/shared/models/legacyintegration.ts b/web/app/shared/models/legacyintegration.ts new file mode 100644 index 0000000..e8cce38 --- /dev/null +++ b/web/app/shared/models/legacyintegration.ts @@ -0,0 +1,49 @@ +export interface LegacyIntegration { + // These are from the server + type: string; + integrationType: string; + userId: string; + name: string; + avatar: string; + about: string; // nullable + supportsEncryptedRooms: boolean; + requirements: any; // nullable + + // These are set in the UI + isSupported: boolean; + notSupportedReason: string; + hasAdditionalConfig: boolean; + isEnabled: boolean; // for the flip-a-bit integrations + isUpdating: boolean; +} + +export interface RSSIntegration extends LegacyIntegration { + feeds: string[]; + immutableFeeds: { url: string, ownerId: string }[]; +} + +export interface TravisCiIntegration extends LegacyIntegration { + repoTemplates: { repoKey: string, template: string, newTemplate: string }[]; // newTemplate is local + immutableRepoTemplates: { repoKey: string, template: string, ownerId: string }[]; + webhookUrl: string; // immutable +} + +export interface CircleCiIntegration extends LegacyIntegration { + repoTemplates: { repoKey: string, template: string, newTemplate: string }[]; // newTemplate is local + immutableRepoTemplates: { repoKey: string, template: string, ownerId: string }[]; + webhookUrl: string; // immutable +} + +export interface IRCIntegration extends LegacyIntegration { + availableNetworks: { name: string, id: string }[]; + channels: { [networkId: string]: string[] }; +} + +export interface EtherpadWidgetIntegration extends LegacyIntegration { + defaultUrl: string; +} + +export interface JitsiWidgetIntegration extends LegacyIntegration { + jitsiDomain: string; + scriptUrl: string +} \ No newline at end of file diff --git a/web/app/shared/models/scalar_responses.ts b/web/app/shared/models/scalar_client_responses.ts similarity index 100% rename from web/app/shared/models/scalar_responses.ts rename to web/app/shared/models/scalar_client_responses.ts diff --git a/web/app/shared/models/scalar_server_responses.ts b/web/app/shared/models/scalar_server_responses.ts new file mode 100644 index 0000000..9d29191 --- /dev/null +++ b/web/app/shared/models/scalar_server_responses.ts @@ -0,0 +1,3 @@ +export interface ScalarAccountResponse { + user_id: string; +} \ No newline at end of file diff --git a/web/app/shared/models/widget.ts b/web/app/shared/models/widget.ts index e851f7a..fb707a3 100644 --- a/web/app/shared/models/widget.ts +++ b/web/app/shared/models/widget.ts @@ -1,4 +1,4 @@ -import { WidgetsResponse } from "./scalar_responses"; +import { WidgetsResponse } from "./scalar_client_responses"; export const WIDGET_CUSTOM = ["customwidget", "dimension-customwidget"]; export const WIDGET_ETHERPAD = ["etherpad", "dimension-etherpad"]; diff --git a/web/app/shared/services/AuthedApi.ts b/web/app/shared/services/AuthedApi.ts new file mode 100644 index 0000000..062e852 --- /dev/null +++ b/web/app/shared/services/AuthedApi.ts @@ -0,0 +1,15 @@ +import { Http, Response } from "@angular/http"; +import { Observable } from "rxjs/Observable"; + +export class AuthedApi { + public static SCALAR_TOKEN: string = null; + + constructor(protected http: Http) { + } + + protected authedGet(url: string, qs?: any): Observable { + if (!qs) qs = {}; + qs["scalar_token"] = AuthedApi.SCALAR_TOKEN; + return this.http.get(url, {params: qs}); + } +} diff --git a/web/app/shared/services/admin-api.service.ts b/web/app/shared/services/admin-api.service.ts new file mode 100644 index 0000000..7bda7cd --- /dev/null +++ b/web/app/shared/services/admin-api.service.ts @@ -0,0 +1,10 @@ +import { Injectable } from "@angular/core"; +import { Http } from "@angular/http"; +import { AuthedApi } from "./AuthedApi"; + +@Injectable() +export class AdminApiService extends AuthedApi { + constructor(http: Http) { + super(http); + } +} diff --git a/web/app/shared/services/dimension-api.service.ts b/web/app/shared/services/dimension-api.service.ts new file mode 100644 index 0000000..ffa7738 --- /dev/null +++ b/web/app/shared/services/dimension-api.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from "@angular/core"; +import { Http } from "@angular/http"; +import { AuthedApi } from "./AuthedApi"; +import { DimensionIntegrationsResponse } from "../models/dimension_responses"; + +@Injectable() +export class DimensionApiService extends AuthedApi { + constructor(http: Http) { + super(http); + } + + public getIntegrations(roomId: string): Promise { + return this.authedGet("/api/v1/dimension/integrations/room/" + roomId).map(r => r.json()).toPromise(); + } +} diff --git a/web/app/shared/services/integration.service.ts b/web/app/shared/services/integration.service.ts index 841e7fe..29d4be0 100644 --- a/web/app/shared/services/integration.service.ts +++ b/web/app/shared/services/integration.service.ts @@ -1,21 +1,9 @@ -import { Injectable } from "@angular/core"; -import { Integration } from "../models/integration"; -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"; -import { YoutubeWidgetConfigComponent } from "../../configs/widget/youtube/youtube-config.component"; -import { TwitchWidgetConfigComponent } from "../../configs/widget/twitch/twitch-config.component"; -import { EtherpadWidgetConfigComponent } from "../../configs/widget/etherpad/etherpad-config.component"; -import { JitsiWidgetConfigComponent } from "../../configs/widget/jitsi/jitsi-config.component"; +import { Component, Injectable } from "@angular/core"; import { WIDGET_CUSTOM, WIDGET_ETHERPAD, WIDGET_GOOGLE_CALENDAR, WIDGET_GOOGLE_DOCS, WIDGET_JITSI, WIDGET_TWITCH, WIDGET_YOUTUBE } from "../models/widget"; -import { GoogleDocsWidgetConfigComponent } from "../../configs/widget/googledocs/googledocs-config.component"; -import { GoogleCalendarWidgetConfigComponent } from "../../configs/widget/googlecalendar/googlecalendar-config.component"; -import { CircleCiConfigComponent } from "../../configs/circleci/circleci-config.component"; +import { Integration } from "../models/integration"; @Injectable() export class IntegrationService { @@ -24,53 +12,53 @@ export class IntegrationService { "bot": {}, // empty == supported "complex-bot": { "rss": { - component: RssConfigComponent, + //component: RssConfigComponent, }, "travisci": { - component: TravisCiConfigComponent, + //component: TravisCiConfigComponent, }, "circleci": { - component: CircleCiConfigComponent, + //component: CircleCiConfigComponent, }, }, "bridge": { "irc": { - component: IrcConfigComponent, + //component: IrcConfigComponent, }, }, "widget": { "customwidget": { - component: CustomWidgetConfigComponent, + //component: CustomWidgetConfigComponent, types: WIDGET_CUSTOM, }, "youtube": { - component: YoutubeWidgetConfigComponent, + //component: YoutubeWidgetConfigComponent, types: WIDGET_YOUTUBE }, "etherpad": { - component: EtherpadWidgetConfigComponent, + //component: EtherpadWidgetConfigComponent, types: WIDGET_ETHERPAD, }, "twitch": { - component: TwitchWidgetConfigComponent, + //component: TwitchWidgetConfigComponent, types: WIDGET_TWITCH, }, "jitsi": { - component: JitsiWidgetConfigComponent, + //component: JitsiWidgetConfigComponent, types: WIDGET_JITSI, }, "googledocs": { - component: GoogleDocsWidgetConfigComponent, + //component: GoogleDocsWidgetConfigComponent, types: WIDGET_GOOGLE_DOCS, }, "googlecalendar": { - component: GoogleCalendarWidgetConfigComponent, + //component: GoogleCalendarWidgetConfigComponent, types: WIDGET_GOOGLE_CALENDAR, }, }, }; - static getAllConfigComponents(): ContainerContent[] { + static getAllConfigComponents(): Component[] { const components = []; for (const iType of Object.keys(IntegrationService.supportedIntegrationsMap)) { @@ -84,34 +72,30 @@ export class IntegrationService { } static isSupported(integration: Integration): boolean { - const forType = IntegrationService.supportedIntegrationsMap[integration.type]; + const forType = IntegrationService.supportedIntegrationsMap[integration.category]; if (!forType) return false; if (Object.keys(forType).length === 0) return true; - return forType[integration.integrationType]; // has sub type + return forType[integration.type]; // has sub type } - static hasConfig(integration: Integration): boolean { - return integration.type !== "bot"; + static getConfigComponent(integration: Integration): Component { + return IntegrationService.supportedIntegrationsMap[integration.category][integration.type].component; } - static getConfigComponent(integration: Integration): ContainerContent { - return IntegrationService.supportedIntegrationsMap[integration.type][integration.integrationType].component; - } - - static getIntegrationForScreen(screen: string): { type: string, integrationType: string } { + static getIntegrationForScreen(screen: string): { category: string, type: string } { for (const iType of Object.keys(IntegrationService.supportedIntegrationsMap)) { for (const iiType of Object.keys(IntegrationService.supportedIntegrationsMap[iType])) { const integrationTypes = IntegrationService.supportedIntegrationsMap[iType][iiType].types; const integrationScreens = integrationTypes.map(t => "type_" + t); - if (integrationScreens.includes(screen)) return {type: iType, integrationType: iiType}; + if (integrationScreens.includes(screen)) return {category: iType, type: iiType}; } } return null; } - constructor() { + private constructor() { } } diff --git a/web/app/shared/services/api.service.ts b/web/app/shared/services/legacy/api.service.ts similarity index 95% rename from web/app/shared/services/api.service.ts rename to web/app/shared/services/legacy/api.service.ts index b5d3b16..1eb5185 100644 --- a/web/app/shared/services/api.service.ts +++ b/web/app/shared/services/legacy/api.service.ts @@ -1,6 +1,6 @@ import { Injectable } from "@angular/core"; import { Http } from "@angular/http"; -import { Integration } from "../models/integration"; +import { LegacyIntegration } from "../../models/legacyintegration"; @Injectable() export class ApiService { @@ -17,7 +17,7 @@ export class ApiService { .map(res => res.status === 200 ? res.json()["userId"] : null).toPromise(); } - getIntegrations(roomId: string, scalarToken: string): Promise { + getIntegrations(roomId: string, scalarToken: string): Promise { return this.http.get("/api/v1/dimension/integrations/" + roomId, {params: {scalar_token: scalarToken}}) .map(res => res.json()).toPromise(); } @@ -46,7 +46,7 @@ export class ApiService { .map(res => res.json()).toPromise(); } - getIntegration(type: string, integrationType: string): Promise { + getIntegration(type: string, integrationType: string): Promise { const url = "/api/v1/dimension/integration/" + type + "/" + integrationType; return this.http.get(url).map(res => res.json()).toPromise(); } diff --git a/web/app/shared/integration.service.ts b/web/app/shared/services/legacy/integration.service.ts similarity index 52% rename from web/app/shared/integration.service.ts rename to web/app/shared/services/legacy/integration.service.ts index 67c5e5e..2db28cc 100644 --- a/web/app/shared/integration.service.ts +++ b/web/app/shared/services/legacy/integration.service.ts @@ -1,24 +1,24 @@ import { Injectable } from "@angular/core"; -import { Integration } from "./models/integration"; -import { RssConfigComponent } from "../configs/rss/rss-config.component"; +import { LegacyIntegration } from "../../models/legacyintegration"; +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 { CircleCiConfigComponent } from "../configs/circleci/circleci-config.component"; -import { CustomWidgetConfigComponent } from "../configs/widget/custom_widget/custom_widget-config.component"; -import { YoutubeWidgetConfigComponent } from "../configs/widget/youtube/youtube-config.component"; -import { TwitchWidgetConfigComponent } from "../configs/widget/twitch/twitch-config.component"; -import { EtherpadWidgetConfigComponent } from "../configs/widget/etherpad/etherpad-config.component"; -import { JitsiWidgetConfigComponent } from "../configs/widget/jitsi/jitsi-config.component"; +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"; +import { YoutubeWidgetConfigComponent } from "../../../configs/widget/youtube/youtube-config.component"; +import { TwitchWidgetConfigComponent } from "../../../configs/widget/twitch/twitch-config.component"; +import { EtherpadWidgetConfigComponent } from "../../../configs/widget/etherpad/etherpad-config.component"; +import { JitsiWidgetConfigComponent } from "../../../configs/widget/jitsi/jitsi-config.component"; import { WIDGET_CUSTOM, WIDGET_ETHERPAD, WIDGET_GOOGLE_CALENDAR, WIDGET_GOOGLE_DOCS, WIDGET_JITSI, WIDGET_TWITCH, WIDGET_YOUTUBE -} from "./models/widget"; -import { GoogleDocsWidgetConfigComponent } from "../configs/widget/googledocs/googledocs-config.component"; -import { GoogleCalendarWidgetConfigComponent } from "../configs/widget/googlecalendar/googlecalendar-config.component"; +} from "../../models/widget"; +import { GoogleDocsWidgetConfigComponent } from "../../../configs/widget/googledocs/googledocs-config.component"; +import { GoogleCalendarWidgetConfigComponent } from "../../../configs/widget/googlecalendar/googlecalendar-config.component"; +import { CircleCiConfigComponent } from "../../../configs/circleci/circleci-config.component"; @Injectable() -export class IntegrationService { +export class LegacyIntegrationService { private static supportedIntegrationsMap = { "bot": {}, // empty == supported @@ -73,9 +73,9 @@ export class IntegrationService { static getAllConfigComponents(): ContainerContent[] { const components = []; - for (const iType of Object.keys(IntegrationService.supportedIntegrationsMap)) { - for (const iiType of Object.keys(IntegrationService.supportedIntegrationsMap[iType])) { - const component = IntegrationService.supportedIntegrationsMap[iType][iiType].component; + for (const iType of Object.keys(LegacyIntegrationService.supportedIntegrationsMap)) { + for (const iiType of Object.keys(LegacyIntegrationService.supportedIntegrationsMap[iType])) { + const component = LegacyIntegrationService.supportedIntegrationsMap[iType][iiType].component; if (component) components.push(component); } } @@ -83,8 +83,8 @@ export class IntegrationService { return components; } - static isSupported(integration: Integration): boolean { - const forType = IntegrationService.supportedIntegrationsMap[integration.type]; + static isSupported(integration: LegacyIntegration): boolean { + const forType = LegacyIntegrationService.supportedIntegrationsMap[integration.type]; if (!forType) return false; if (Object.keys(forType).length === 0) return true; @@ -92,18 +92,18 @@ export class IntegrationService { return forType[integration.integrationType]; // has sub type } - static hasConfig(integration: Integration): boolean { + static hasConfig(integration: LegacyIntegration): boolean { return integration.type !== "bot"; } - static getConfigComponent(integration: Integration): ContainerContent { - return IntegrationService.supportedIntegrationsMap[integration.type][integration.integrationType].component; + static getConfigComponent(integration: LegacyIntegration): ContainerContent { + return LegacyIntegrationService.supportedIntegrationsMap[integration.type][integration.integrationType].component; } static getIntegrationForScreen(screen: string): { type: string, integrationType: string } { - for (const iType of Object.keys(IntegrationService.supportedIntegrationsMap)) { - for (const iiType of Object.keys(IntegrationService.supportedIntegrationsMap[iType])) { - const integrationTypes = IntegrationService.supportedIntegrationsMap[iType][iiType].types; + for (const iType of Object.keys(LegacyIntegrationService.supportedIntegrationsMap)) { + for (const iiType of Object.keys(LegacyIntegrationService.supportedIntegrationsMap[iType])) { + const integrationTypes = LegacyIntegrationService.supportedIntegrationsMap[iType][iiType].types; const integrationScreens = integrationTypes.map(t => "type_" + t); if (integrationScreens.includes(screen)) return {type: iType, integrationType: iiType}; } diff --git a/web/app/shared/services/irc-api.service.ts b/web/app/shared/services/legacy/irc-api.service.ts similarity index 100% rename from web/app/shared/services/irc-api.service.ts rename to web/app/shared/services/legacy/irc-api.service.ts diff --git a/web/app/shared/services/scalar.service.ts b/web/app/shared/services/scalar-client-api.service.ts similarity index 91% rename from web/app/shared/services/scalar.service.ts rename to web/app/shared/services/scalar-client-api.service.ts index 0a86582..b117d52 100644 --- a/web/app/shared/services/scalar.service.ts +++ b/web/app/shared/services/scalar-client-api.service.ts @@ -6,17 +6,17 @@ import { MembershipStateResponse, RoomEncryptionStatusResponse, ScalarSuccessResponse, WidgetsResponse -} from "../models/scalar_responses"; +} from "../models/scalar_client_responses"; import { EditableWidget } from "../models/widget"; @Injectable() -export class ScalarService { +export class ScalarClientApiService { private static actionMap: { [key: string]: { resolve: (obj: any) => void, reject: (obj: any) => void } } = {}; public static getAndRemoveActionHandler(requestKey: string): { resolve: (obj: any) => void, reject: (obj: any) => void } { - let handler = ScalarService.actionMap[requestKey]; - ScalarService.actionMap[requestKey] = null; + let handler = ScalarClientApiService.actionMap[requestKey]; + ScalarClientApiService.actionMap[requestKey] = null; return handler; } @@ -96,7 +96,7 @@ export class ScalarService { return; } - ScalarService.actionMap[requestKey] = { + ScalarClientApiService.actionMap[requestKey] = { resolve: resolve, reject: reject }; @@ -117,7 +117,7 @@ window.addEventListener("message", event => { let requestKey = event.data["request_id"]; if (!requestKey) return; - let action = ScalarService.getAndRemoveActionHandler(requestKey); + let action = ScalarClientApiService.getAndRemoveActionHandler(requestKey); if (!action) return; if (event.data.response && event.data.response.error) action.reject(event.data); diff --git a/web/app/shared/services/scalar-server-api.service.ts b/web/app/shared/services/scalar-server-api.service.ts new file mode 100644 index 0000000..e0703a8 --- /dev/null +++ b/web/app/shared/services/scalar-server-api.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from "@angular/core"; +import { Http } from "@angular/http"; +import { ScalarAccountResponse } from "../models/scalar_server_responses"; +import { AuthedApi } from "./AuthedApi"; + +@Injectable() +export class ScalarServerApiService extends AuthedApi { + constructor(http: Http) { + super(http) + } + + public getAccount(): Promise { + return this.authedGet("/api/v1/scalar/account").map(res => res.json()).toPromise(); + } +} diff --git a/web/app/widget_wrappers/generic/generic.component.ts b/web/app/widget_wrappers/generic/generic.component.ts index 3957941..1fda647 100644 --- a/web/app/widget_wrappers/generic/generic.component.ts +++ b/web/app/widget_wrappers/generic/generic.component.ts @@ -1,5 +1,5 @@ import { Component } from "@angular/core"; -import { ApiService } from "../../shared/services/api.service"; +import { ApiService } from "../../shared/services/legacy/api.service"; import { ActivatedRoute } from "@angular/router"; import { DomSanitizer, SafeUrl } from "@angular/platform-browser"; diff --git a/web/app/widget_wrappers/jitsi/jitsi.component.ts b/web/app/widget_wrappers/jitsi/jitsi.component.ts index b1500e2..c5e81f6 100644 --- a/web/app/widget_wrappers/jitsi/jitsi.component.ts +++ b/web/app/widget_wrappers/jitsi/jitsi.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; import * as $ from "jquery"; -import { ApiService } from "../../shared/services/api.service"; -import { JitsiWidgetIntegration } from "../../shared/models/integration"; +import { ApiService } from "../../shared/services/legacy/api.service"; +import { JitsiWidgetIntegration } from "../../shared/models/legacyintegration"; declare var JitsiMeetExternalAPI: any;