mirror of
https://github.com/turt2live/matrix-dimension.git
synced 2024-10-01 01:05:53 -04:00
Support vector's RSS bot. Adds #13
This has a side effect of adding #23 as well. A more performant caching method is probably needed (as this doesn't cache at all).
This commit is contained in:
parent
3aa60b66a6
commit
58feb07119
@ -6,4 +6,3 @@ about: "Tracks any Atom/RSS feed and sends new items into this room"
|
||||
avatar: "/img/avatars/rssbot.png"
|
||||
upstream:
|
||||
type: "vector"
|
||||
id: "rssbot"
|
@ -44,6 +44,7 @@
|
||||
"@angularclass/hmr-loader": "^3.0.2",
|
||||
"@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.22",
|
||||
"@types/node": "^7.0.18",
|
||||
"angular2-modal": "^2.0.3",
|
||||
"angular2-template-loader": "^0.6.2",
|
||||
"angular2-toaster": "^4.0.0",
|
||||
"angular2-ui-switch": "^1.2.0",
|
||||
|
@ -24,6 +24,7 @@ class DimensionApi {
|
||||
|
||||
app.get("/api/v1/dimension/integrations/:roomId", this._getIntegrations.bind(this));
|
||||
app.delete("/api/v1/dimension/integrations/:roomId/:type/:integrationType", this._removeIntegration.bind(this));
|
||||
app.put("/api/v1/dimension/integrations/:roomId/:type/:integrationType/state", this._updateIntegrationState.bind(this));
|
||||
}
|
||||
|
||||
_getIntegration(integrationConfig, roomId, scalarToken) {
|
||||
@ -103,6 +104,38 @@ class DimensionApi {
|
||||
res.status(500).send({error: err.message});
|
||||
});
|
||||
}
|
||||
|
||||
_updateIntegrationState(req, res) {
|
||||
var roomId = req.params.roomId;
|
||||
var scalarToken = req.body.scalar_token;
|
||||
var type = req.params.type;
|
||||
var integrationType = req.params.integrationType;
|
||||
|
||||
if (!roomId || !scalarToken || !type || !integrationType) {
|
||||
res.status(400).send({error: "Missing room, integration type, type, or token"});
|
||||
return;
|
||||
}
|
||||
|
||||
var integrationConfig = Integrations.byType[type][integrationType];
|
||||
if (!integrationConfig) {
|
||||
res.status(400).send({error: "Unknown integration"});
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("DimensionApi", "Update state requested for " + type + " (" + integrationType + ") in room " + roomId);
|
||||
|
||||
this._db.checkToken(scalarToken).then(() => {
|
||||
return this._getIntegration(integrationConfig, roomId, scalarToken);
|
||||
}).then(integration => {
|
||||
return integration.updateState(req.body.state);
|
||||
}).then(newState => {
|
||||
res.status(200).send(newState);
|
||||
}).catch(err => {
|
||||
log.error("DimensionApi", err);
|
||||
console.error(err);
|
||||
res.status(500).send({error: err.message});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new DimensionApi();
|
||||
|
@ -30,6 +30,15 @@ class IntegrationStub {
|
||||
removeFromRoom(roomId) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the state information for this integration. The data passed is an implementation detail.
|
||||
* @param {*} newState the new state
|
||||
* @returns {Promise<*>} resolves when completed, with the new state of the integration
|
||||
*/
|
||||
updateState(newState) {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IntegrationStub;
|
||||
|
@ -22,8 +22,16 @@ class RSSBot extends ComplexBot {
|
||||
|
||||
/*override*/
|
||||
getState() {
|
||||
var response = {
|
||||
feeds: [],
|
||||
immutableFeeds: []
|
||||
};
|
||||
return this._backbone.getFeeds().then(feeds => {
|
||||
return {feeds: feeds};
|
||||
response.feeds = feeds;
|
||||
return this._backbone.getImmutableFeeds();
|
||||
}).then(feeds => {
|
||||
response.immutableFeeds = feeds;
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
@ -31,6 +39,11 @@ class RSSBot extends ComplexBot {
|
||||
removeFromRoom(roomId) {
|
||||
return this._backbone.removeFromRoom(roomId);
|
||||
}
|
||||
|
||||
/*override*/
|
||||
updateState(newState) {
|
||||
return this._backbone.setFeeds(newState.feeds).then(() => this.getState());
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RSSBot;
|
@ -25,6 +25,23 @@ class StubbedRssBackbone {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new feeds for this backbone
|
||||
* @param {string[]} newFeeds the new feed URLs
|
||||
* @returns {Promise<>} resolves when complete
|
||||
*/
|
||||
setFeeds(newFeeds) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the immutable feeds for this backbone
|
||||
* @returns {Promise<{url:string,ownerId:string}>} resolves to the collection of immutable feeds
|
||||
*/
|
||||
getImmutableFeeds() {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the bot from the given room
|
||||
* @param {string} roomId the room ID to remove the bot from
|
||||
|
@ -18,6 +18,7 @@ class VectorRssBackbone extends StubbedRssBackbone {
|
||||
this._roomId = roomId;
|
||||
this._scalarToken = upstreamScalarToken;
|
||||
this._info = null;
|
||||
this._otherFeeds = [];
|
||||
}
|
||||
|
||||
/*override*/
|
||||
@ -35,15 +36,46 @@ class VectorRssBackbone extends StubbedRssBackbone {
|
||||
});
|
||||
}
|
||||
|
||||
/*override*/
|
||||
setFeeds(newFeeds) {
|
||||
var feedConfig = {};
|
||||
for (var feed of newFeeds) feedConfig[feed] = {};
|
||||
|
||||
return VectorScalarClient.configureIntegration("rssbot", this._scalarToken, {
|
||||
feeds: feedConfig,
|
||||
room_id: this._roomId
|
||||
});
|
||||
}
|
||||
|
||||
/*override*/
|
||||
getImmutableFeeds() {
|
||||
return (this._info ? Promise.resolve() : this._getInfo()).then(() => {
|
||||
return this._otherFeeds;
|
||||
});
|
||||
}
|
||||
|
||||
_getInfo() {
|
||||
return VectorScalarClient.getIntegration("rssbot", this._roomId, this._scalarToken).then(info => {
|
||||
return VectorScalarClient.getIntegrationsForRoom(this._roomId, this._scalarToken).then(integrations => {
|
||||
this._otherFeeds = [];
|
||||
for (var integration of integrations) {
|
||||
if (integration.self) continue; // skip - we're not looking for ones we know about
|
||||
if (integration.type == "rssbot") {
|
||||
var urls = _.keys(integration.config.feeds);
|
||||
for (var url of urls) {
|
||||
this._otherFeeds.push({url: url, ownerId: integration.user_id});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return VectorScalarClient.getIntegration("rssbot", this._roomId, this._scalarToken);
|
||||
}).then(info => {
|
||||
this._info = info;
|
||||
});
|
||||
}
|
||||
|
||||
/*override*/
|
||||
removeFromRoom(roomId) {
|
||||
return VectorScalarClient.removeIntegration(this._config.upstream.id, roomId, this._upstreamToken);
|
||||
return VectorScalarClient.removeIntegration("rssbot", roomId, this._scalarToken);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ module.exports = (db, integrationConfig, roomId, scalarToken) => {
|
||||
if (integrationConfig.upstream) {
|
||||
if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream");
|
||||
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
|
||||
var backbone = new VectorSimpleBackbone(roomId, upstreamToken);
|
||||
var backbone = new VectorSimpleBackbone(integrationConfig, upstreamToken);
|
||||
return new SimpleBot(integrationConfig, backbone);
|
||||
});
|
||||
} else if (integrationConfig.hosted) {
|
||||
|
@ -64,6 +64,23 @@ class VectorScalarClient {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the integrations currently in a room
|
||||
* @param {string} roomId the room ID
|
||||
* @param {string} scalarToken the scalar token to use
|
||||
* @returns {Promise<*[]>} resolves a collection of integrations
|
||||
*/
|
||||
getIntegrationsForRoom(roomId, scalarToken) {
|
||||
return this._do("POST", "/integrations", {scalar_token: scalarToken}, {RoomId: roomId}).then((response, body) => {
|
||||
if (response.statusCode !== 200) {
|
||||
log.error("VectorScalarClient", response.body);
|
||||
return Promise.reject(response.body);
|
||||
}
|
||||
|
||||
return response.body.integrations;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information on
|
||||
* @param {string} type the type to lookup
|
||||
|
@ -15,6 +15,10 @@ import { ToasterModule } from "angular2-toaster";
|
||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||
import { IntegrationComponent } from "./integration/integration.component";
|
||||
import { ScalarCloseComponent } from "./riot/scalar-close/scalar-close.component";
|
||||
import { IntegrationService } from "./shared/integration.service";
|
||||
import { BootstrapModalModule } from "angular2-modal/plugins/bootstrap";
|
||||
import { ModalModule } from "angular2-modal";
|
||||
import { RssConfigComponent } from "./configs/rss/rss-config.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -26,6 +30,8 @@ import { ScalarCloseComponent } from "./riot/scalar-close/scalar-close.component
|
||||
UiSwitchModule,
|
||||
ToasterModule,
|
||||
BrowserAnimationsModule,
|
||||
ModalModule.forRoot(),
|
||||
BootstrapModalModule,
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
@ -33,17 +39,21 @@ import { ScalarCloseComponent } from "./riot/scalar-close/scalar-close.component
|
||||
RiotComponent,
|
||||
IntegrationComponent,
|
||||
ScalarCloseComponent,
|
||||
RssConfigComponent,
|
||||
|
||||
// Vendor
|
||||
],
|
||||
providers: [
|
||||
ApiService,
|
||||
ScalarService,
|
||||
IntegrationService,
|
||||
|
||||
// Vendor
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
entryComponents: []
|
||||
entryComponents: [
|
||||
RssConfigComponent,
|
||||
]
|
||||
})
|
||||
export class AppModule {
|
||||
constructor(public appRef: ApplicationRef) {
|
||||
|
32
web/app/configs/config.component.scss
Normal file
32
web/app/configs/config.component.scss
Normal file
@ -0,0 +1,32 @@
|
||||
// shared styling for all config screens
|
||||
.config-wrapper {
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.config-header {
|
||||
padding-bottom: 8px;
|
||||
margin-bottom: 14px;
|
||||
border-bottom: 1px solid #dadada;
|
||||
}
|
||||
|
||||
.config-header h4 {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.config-header img {
|
||||
margin-right: 7px;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
border-radius: 35px;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
float: right;
|
||||
margin: -17px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.config-content {
|
||||
display: block;
|
||||
}
|
38
web/app/configs/rss/rss-config.component.html
Normal file
38
web/app/configs/rss/rss-config.component.html
Normal file
@ -0,0 +1,38 @@
|
||||
<div class="config-wrapper">
|
||||
<img src="/img/close.svg" (click)="dialog.close()" class="close-icon">
|
||||
<div class="config-header">
|
||||
<img src="/img/avatars/rssbot.png">
|
||||
<h4>Configure RSS/Atom Feeds</h4>
|
||||
</div>
|
||||
<div class="config-content">
|
||||
<form (submit)="addFeed()" novalidate name="addFeedForm">
|
||||
<div class="row">
|
||||
<div class="col-md-8" style="margin-bottom: 12px;">
|
||||
<div class="input-group input-group-sm">
|
||||
<input type="text" class="form-control"
|
||||
[(ngModel)]="feedUrl" name="feedUrl"
|
||||
[disabled]="isUpdating">
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn-success" [disabled]="isUpdating">
|
||||
<i class="fa fa-plus-circle"></i> Add Feed
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12" *ngFor="let feed of integration.feeds trackById">
|
||||
<a [href]="feed" rel="noopener" target="_blank">{{ feed }}</a>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" (click)="removeFeed(feed)"
|
||||
style="margin-top: -5px;" [disabled]="isUpdating">
|
||||
<i class="fa fa-times"></i> Remove Feed
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-12" *ngIf="integration.immutableFeeds.length > 0">
|
||||
<h6 class="other-feeds-title">Feeds from other users in the room</h6>
|
||||
</div>
|
||||
<div class="col-md-12 feed-list" *ngFor="let feed of integration.immutableFeeds trackById">
|
||||
<a [href]="feed.url" rel="noopener" target="_blank">{{ feed.url }}</a> <span class="text-muted">(added by {{ feed.ownerId }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
9
web/app/configs/rss/rss-config.component.scss
Normal file
9
web/app/configs/rss/rss-config.component.scss
Normal file
@ -0,0 +1,9 @@
|
||||
// component styles are encapsulated and only applied to their components
|
||||
.feed-list {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.other-feeds-title {
|
||||
margin-top: 25px;
|
||||
margin-bottom: 0;
|
||||
}
|
67
web/app/configs/rss/rss-config.component.ts
Normal file
67
web/app/configs/rss/rss-config.component.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { RSSIntegration } from "../../shared/models/integration";
|
||||
import { ModalComponent, DialogRef } from "angular2-modal";
|
||||
import { ConfigModalContext } from "../../integration/integration.component";
|
||||
import { ToasterService } from "angular2-toaster";
|
||||
import { ApiService } from "../../shared/api.service";
|
||||
|
||||
@Component({
|
||||
selector: 'my-rss-config',
|
||||
templateUrl: './rss-config.component.html',
|
||||
styleUrls: ['./rss-config.component.scss', './../config.component.scss'],
|
||||
})
|
||||
export class RssConfigComponent implements ModalComponent<ConfigModalContext> {
|
||||
|
||||
public integration: RSSIntegration;
|
||||
|
||||
public isUpdating = false;
|
||||
public feedUrl = "";
|
||||
|
||||
private roomId: string;
|
||||
private scalarToken: string;
|
||||
|
||||
constructor(public dialog: DialogRef<ConfigModalContext>,
|
||||
private toaster: ToasterService,
|
||||
private api: ApiService) {
|
||||
this.integration = <RSSIntegration>dialog.context.integration;
|
||||
this.roomId = dialog.context.roomId;
|
||||
this.scalarToken = dialog.context.scalarToken;
|
||||
}
|
||||
|
||||
public addFeed() {
|
||||
if (!this.feedUrl || this.feedUrl.trim().length === 0) {
|
||||
this.toaster.pop("warning", "Please enter a feed URL");
|
||||
return;
|
||||
}
|
||||
if (this.integration.feeds.indexOf(this.feedUrl) !== -1) {
|
||||
this.toaster.pop("error", "This feed has already been added");
|
||||
}
|
||||
|
||||
let feedCopy = JSON.parse(JSON.stringify(this.integration.feeds));
|
||||
feedCopy.push(this.feedUrl);
|
||||
this.updateFeeds(feedCopy);
|
||||
}
|
||||
|
||||
public removeFeed(feedUrl) {
|
||||
let feedCopy = JSON.parse(JSON.stringify(this.integration.feeds));
|
||||
const idx = feedCopy.indexOf(feedUrl);
|
||||
feedCopy.splice(idx, 1);
|
||||
this.updateFeeds(feedCopy);
|
||||
}
|
||||
|
||||
private updateFeeds(newFeeds) {
|
||||
this.isUpdating = true;
|
||||
this.api.updateIntegrationState(this.roomId, this.integration.type, this.integration.integrationType, this.scalarToken, {
|
||||
feeds: newFeeds
|
||||
}).then(response => {
|
||||
this.integration.feeds = response.feeds;
|
||||
this.integration.immutableFeeds = response.immutableFeeds;
|
||||
this.isUpdating = false;
|
||||
this.toaster.pop("success", "Feeds updated");
|
||||
}).catch(err => {
|
||||
this.toaster.pop("error", err.json().error);
|
||||
console.error(err);
|
||||
this.isUpdating = false;
|
||||
});
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
</div>
|
||||
<div class="toolbar">
|
||||
<i class="fa fa-question-circle text-info" ngbTooltip="{{integration.about}}" *ngIf="integration.about"></i>
|
||||
<i class="fa fa-cog text-info config-icon" (click)="configureIntegration()" *ngIf="integration.isEnabled && integration.hasConfig"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -32,3 +32,7 @@
|
||||
vertical-align: top;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.config-icon {
|
||||
cursor: pointer;
|
||||
}
|
@ -1,5 +1,14 @@
|
||||
import { Component, Input, Output, EventEmitter } from "@angular/core";
|
||||
import { Component, Input, Output, EventEmitter, ViewContainerRef } from "@angular/core";
|
||||
import { Integration } from "../shared/models/integration";
|
||||
import { Overlay, overlayConfigFactory } from "angular2-modal";
|
||||
import { Modal, BSModalContext } from "angular2-modal/plugins/bootstrap";
|
||||
import { IntegrationService } from "../shared/integration.service";
|
||||
|
||||
export class ConfigModalContext extends BSModalContext {
|
||||
public integration: Integration;
|
||||
public roomId: string;
|
||||
public scalarToken: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-integration',
|
||||
@ -9,13 +18,26 @@ import { Integration } from "../shared/models/integration";
|
||||
export class IntegrationComponent {
|
||||
|
||||
@Input() integration: Integration;
|
||||
@Input() roomId: string;
|
||||
@Input() scalarToken: string;
|
||||
@Output() updated: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
constructor() {
|
||||
constructor(overlay: Overlay, vcRef: ViewContainerRef, public modal: Modal) {
|
||||
overlay.defaultViewContainer = vcRef;
|
||||
}
|
||||
|
||||
public update(): void {
|
||||
this.integration.isEnabled = !this.integration.isEnabled;
|
||||
this.updated.emit();
|
||||
}
|
||||
|
||||
public configureIntegration(): void {
|
||||
this.modal.open(IntegrationService.getConfigComponent(this.integration), overlayConfigFactory({
|
||||
integration: this.integration,
|
||||
roomId: this.roomId,
|
||||
scalarToken: this.scalarToken,
|
||||
isBlocking: false,
|
||||
size: 'lg'
|
||||
}, BSModalContext));
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,14 @@
|
||||
</div>
|
||||
<div *ngIf="!error && !loading">
|
||||
<h3>Manage Integrations</h3>
|
||||
<p>Turn on anything you like. If an integration has some special configuration, it will have a configuration icon next to it.</p>
|
||||
<p>Turn on anything you like. If an integration has some special configuration, it will have a configuration
|
||||
icon next to it.</p>
|
||||
<div class="integration-container">
|
||||
<my-integration *ngFor="let integration of integrations" [integration]="integration" (updated)="updateIntegration(integration)"></my-integration>
|
||||
<my-integration *ngFor="let integration of integrations"
|
||||
[integration]="integration"
|
||||
[roomId]="roomId"
|
||||
[scalarToken]="scalarToken"
|
||||
(updated)="updateIntegration(integration)"></my-integration>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -4,6 +4,8 @@ import { ApiService } from "../shared/api.service";
|
||||
import { ScalarService } from "../shared/scalar.service";
|
||||
import { ToasterService } from "angular2-toaster";
|
||||
import { Integration } from "../shared/models/integration";
|
||||
import { IntegrationService } from "../shared/integration.service";
|
||||
import * as _ from "lodash";
|
||||
|
||||
@Component({
|
||||
selector: 'my-riot',
|
||||
@ -16,8 +18,7 @@ export class RiotComponent {
|
||||
public integrations: Integration[] = [];
|
||||
public loading = true;
|
||||
public roomId: string;
|
||||
|
||||
private scalarToken: string;
|
||||
public scalarToken: string;
|
||||
|
||||
constructor(private activatedRoute: ActivatedRoute,
|
||||
private api: ApiService,
|
||||
@ -41,7 +42,7 @@ export class RiotComponent {
|
||||
|
||||
private init() {
|
||||
this.api.getIntegrations(this.roomId, this.scalarToken).then(integrations => {
|
||||
this.integrations = integrations;
|
||||
this.integrations = _.filter(integrations, i => IntegrationService.isSupported(i));
|
||||
let promises = integrations.map(b => this.updateIntegrationState(b));
|
||||
return Promise.all(promises);
|
||||
}).then(() => this.loading = false).catch(err => {
|
||||
@ -51,6 +52,8 @@ export class RiotComponent {
|
||||
}
|
||||
|
||||
private updateIntegrationState(integration: Integration) {
|
||||
integration.hasConfig = IntegrationService.hasConfig(integration);
|
||||
|
||||
return this.scalar.getMembershipState(this.roomId, integration.userId).then(payload => {
|
||||
integration.isBroken = false;
|
||||
|
||||
@ -74,13 +77,11 @@ export class RiotComponent {
|
||||
promise = this.api.removeIntegration(this.roomId, integration.type, integration.integrationType, this.scalarToken);
|
||||
} else promise = this.scalar.inviteUser(this.roomId, integration.userId);
|
||||
|
||||
promise
|
||||
.then(() => {
|
||||
promise.then(() => {
|
||||
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");
|
||||
})
|
||||
.catch(err => {
|
||||
}).catch(err => {
|
||||
let errorMessage = "Could not update integration status";
|
||||
|
||||
if (err.json) {
|
||||
|
@ -18,7 +18,14 @@ export class ApiService {
|
||||
}
|
||||
|
||||
removeIntegration(roomId: string, type: string, integrationType: string, scalarToken: string): Promise<any> {
|
||||
return this.http.delete("/api/v1/dimension/integrations/" + roomId + "/" + type + "/" + integrationType, {params: {scalar_token: scalarToken}})
|
||||
const url = "/api/v1/dimension/integrations/" + roomId + "/" + type + "/" + integrationType;
|
||||
return this.http.delete(url, {params: {scalar_token: scalarToken}})
|
||||
.map(res => res.json()).toPromise();
|
||||
}
|
||||
|
||||
updateIntegrationState(roomId: string, type: string, integrationType: string, scalarToken: string, newState: any): Promise<any> {
|
||||
const url = "/api/v1/dimension/integrations/" + roomId + "/" + type + "/" + integrationType + "/state";
|
||||
return this.http.put(url, {scalar_token: scalarToken, state: newState})
|
||||
.map(res => res.json()).toPromise();
|
||||
}
|
||||
}
|
||||
|
38
web/app/shared/integration.service.ts
Normal file
38
web/app/shared/integration.service.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Integration } from "./models/integration";
|
||||
import { RssConfigComponent } from "../configs/rss/rss-config.component";
|
||||
import { ContainerContent } from "angular2-modal";
|
||||
|
||||
@Injectable()
|
||||
export class IntegrationService {
|
||||
|
||||
private static supportedTypeMap = {
|
||||
"bot": true,
|
||||
"complex-bot": {
|
||||
"rss": true
|
||||
}
|
||||
};
|
||||
|
||||
private static components = {
|
||||
"complex-bot": {
|
||||
"rss": RssConfigComponent
|
||||
}
|
||||
};
|
||||
|
||||
static isSupported(integration: Integration): boolean {
|
||||
if (IntegrationService.supportedTypeMap[integration.type] === true) return true;
|
||||
if (!IntegrationService.supportedTypeMap[integration.type]) return false;
|
||||
return IntegrationService.supportedTypeMap[integration.type][integration.integrationType] === true;
|
||||
}
|
||||
|
||||
static hasConfig(integration: Integration): boolean {
|
||||
return integration.type !== "bot";
|
||||
}
|
||||
|
||||
static getConfigComponent(integration: Integration): ContainerContent {
|
||||
return IntegrationService.components[integration.type][integration.integrationType];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
}
|
||||
}
|
@ -7,4 +7,10 @@ export interface Integration {
|
||||
about: string; // nullable
|
||||
isEnabled: boolean;
|
||||
isBroken: boolean;
|
||||
hasConfig: boolean;
|
||||
}
|
||||
|
||||
export interface RSSIntegration extends Integration {
|
||||
feeds: string[];
|
||||
immutableFeeds: {url: string, ownerId: string}[];
|
||||
}
|
@ -7,3 +7,23 @@ body {
|
||||
padding: 0;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
// HACK: Work around dialog not showing up
|
||||
// ref: https://github.com/shlomiassaf/angular2-modal/issues/280
|
||||
.fade.in {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.modal.in .modal-dialog {
|
||||
-webkit-transform: translate(0, 0);
|
||||
-o-transform: translate(0, 0);
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
|
||||
.modal-backdrop.in {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
Loading…
Reference in New Issue
Block a user