Replace ngx-modialog with ng-bootstrap modals

This commit is contained in:
Tony Stipanic 2021-08-17 00:00:59 +02:00
parent 4954de2a96
commit e77d712afd
No known key found for this signature in database
GPG Key ID: 3026BCCB6C9CC6BD
63 changed files with 1300 additions and 1151 deletions

2
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,2 @@
{
}

6
config/bot.json Normal file
View File

@ -0,0 +1,6 @@
{
"syncToken": "s22_2648_0_1_29_1_25_19_1",
"filter": null,
"appserviceUsers": {},
"appserviceTransactions": {}
}

6
config/bot.json.bak Normal file
View File

@ -0,0 +1,6 @@
{
"syncToken": "s3510138_39116385_25484_5602626_218935_269_119102_8310593_32",
"filter": null,
"appserviceUsers": {},
"appserviceTransactions": {}
}

BIN
config/dimension.db.bak Normal file

Binary file not shown.

9
package-lock.json generated
View File

@ -11648,15 +11648,6 @@
"resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
"integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==" "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="
}, },
"ngx-modialog": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ngx-modialog/-/ngx-modialog-5.0.1.tgz",
"integrity": "sha512-m6o7V01j97C/6GGQjNfxF96k6713G11Fs/S6RF7NbxRV3EUZsG0MqkDn2Q1fR0UEoaNqyKMDVDTya7N3hAnfPA==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"ngx-ui-switch": { "ngx-ui-switch": {
"version": "12.0.1", "version": "12.0.1",
"resolved": "https://registry.npmjs.org/ngx-ui-switch/-/ngx-ui-switch-12.0.1.tgz", "resolved": "https://registry.npmjs.org/ngx-ui-switch/-/ngx-ui-switch-12.0.1.tgz",

View File

@ -117,7 +117,6 @@
"jquery": "^3.6.0", "jquery": "^3.6.0",
"json-loader": "^0.5.7", "json-loader": "^0.5.7",
"mini-css-extract-plugin": "^2.1.0", "mini-css-extract-plugin": "^2.1.0",
"ngx-modialog": "^5.0.1",
"postcss-cssnext": "^3.1.0", "postcss-cssnext": "^3.1.0",
"postcss-import": "^14.0.2", "postcss-import": "^14.0.2",
"postcss-loader": "^6.1.1", "postcss-loader": "^6.1.1",

View File

@ -1,24 +1,23 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'Add a new self-hosted IRC Bridge' | translate}}</h4>
<h4>{{'Add a new self-hosted IRC Bridge' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
<p>{{'Self-hosted IRC bridges must have' | translate}}<code>{{'provisioning' | translate}}</code> {{'enabled in the configuration.' | translate}}</p> <p>{{'Self-hosted IRC bridges must have' | translate}}<code>{{'provisioning' | translate}}</code> {{'enabled in the configuration.' | translate}}</p>
<label class="label-block"> <label class="label-block">
{{'Provisioning URL' | translate}} {{'Provisioning URL' | translate}}
<span class="text-muted ">{{'The provisioning URL for the bridge. This is usually the same as the URL given in the registration. This API is not authenticated and should be treated with caution.' | translate}}</span> <span class="text-muted ">{{'The provisioning URL for the bridge. This is usually the same as the URL given in the registration. This API is not authenticated and should be treated with caution.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="http://localhost:9999" placeholder="http://localhost:9999"
[(ngModel)]="provisionUrl" [disabled]="isSaving"/> [(ngModel)]="provisionUrl" [disabled]="isSaving"/>
</label> </label>
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="add()" title="close" class="btn btn-primary btn-sm"> <button type="button" (click)="add()" title="close" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}} <i class="far fa-save"></i> {{'Save' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,23 +1,24 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { AdminIrcApiService } from "../../../../shared/services/admin/admin-irc-api.service"; import { AdminIrcApiService } from "../../../../shared/services/admin/admin-irc-api.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
export class AddSelfhostedIrcBridgeDialogContext extends BSModalContext { export interface AddSelfhostedIrcBridgeDialogContextt {
isSaving: boolean;
provisionUrl: string;
} }
@Component({ @Component({
templateUrl: "./add-selfhosted.component.html", templateUrl: "./add-selfhosted.component.html",
styleUrls: ["./add-selfhosted.component.scss"], styleUrls: ["./add-selfhosted.component.scss"],
}) })
export class AdminIrcBridgeAddSelfhostedComponent implements ModalComponent<AddSelfhostedIrcBridgeDialogContext> { export class AdminIrcBridgeAddSelfhostedComponent {
public isSaving = false; public isSaving = false;
public provisionUrl: string; public provisionUrl: string;
constructor(public dialog: DialogRef<AddSelfhostedIrcBridgeDialogContext>, constructor(public modal: NgbActiveModal,
private ircApi: AdminIrcApiService, private ircApi: AdminIrcApiService,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
@ -28,7 +29,7 @@ export class AdminIrcBridgeAddSelfhostedComponent implements ModalComponent<AddS
this.isSaving = true; this.isSaving = true;
this.ircApi.newSelfhosted(this.provisionUrl).then(() => { this.ircApi.newSelfhosted(this.provisionUrl).then(() => {
this.translate.get('IRC Bridge added').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('IRC Bridge added').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
console.error(err); console.error(err);
this.isSaving = false; this.isSaving = false;

View File

@ -4,13 +4,12 @@ import { AdminIrcApiService } from "../../../shared/services/admin/admin-irc-api
import { FE_Upstream } from "../../../shared/models/admin-responses"; import { FE_Upstream } from "../../../shared/models/admin-responses";
import { AdminUpstreamApiService } from "../../../shared/services/admin/admin-upstream-api.service"; import { AdminUpstreamApiService } from "../../../shared/services/admin/admin-upstream-api.service";
import { FE_IrcBridge } from "../../../shared/models/irc"; import { FE_IrcBridge } from "../../../shared/models/irc";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { AdminIrcBridgeNetworksComponent, IrcNetworksDialogContext } from "./networks/networks.component"; import { AdminIrcBridgeNetworksComponent, IrcNetworksDialogContext } from "./networks/networks.component";
import { import {
AddSelfhostedIrcBridgeDialogContext,
AdminIrcBridgeAddSelfhostedComponent AdminIrcBridgeAddSelfhostedComponent
} from "./add-selfhosted/add-selfhosted.component"; } from "./add-selfhosted/add-selfhosted.component";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({ @Component({
templateUrl: "./irc.component.html", templateUrl: "./irc.component.html",
@ -28,7 +27,7 @@ export class AdminIrcBridgeComponent implements OnInit {
constructor(private upstreamApi: AdminUpstreamApiService, constructor(private upstreamApi: AdminUpstreamApiService,
private ircApi: AdminIrcApiService, private ircApi: AdminIrcApiService,
private toaster: ToasterService, private toaster: ToasterService,
private modal: Modal, private modal: NgbModal,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
} }
@ -94,23 +93,34 @@ export class AdminIrcBridgeComponent implements OnInit {
} }
public addSelfHostedBridge() { public addSelfHostedBridge() {
this.modal.open(AdminIrcBridgeAddSelfhostedComponent, overlayConfigFactory({ const selfhostedRef = this.modal.open(AdminIrcBridgeAddSelfhostedComponent, {
isBlocking: true, backdrop: 'static',
size: 'lg', size: 'lg',
}, AddSelfhostedIrcBridgeDialogContext)).result.then(() => { });
this.reload().catch(err => { selfhostedRef.result.then(() => {
try {
this.reload()
} catch (err) {
console.error(err); console.error(err);
this.translate.get('Failed to get an update IRC bridge list').subscribe((res: string) => {this.toaster.pop("error", res); }); this.translate.get('Failed to get an update IRC bridge list').subscribe((res: string) => {this.toaster.pop("error", res); });
}); }
}); })
} }
public editNetworks(bridge: FE_IrcBridge) { public editNetworks(bridge: FE_IrcBridge) {
this.modal.open(AdminIrcBridgeNetworksComponent, overlayConfigFactory({ const selfhostedRef = this.modal.open(AdminIrcBridgeNetworksComponent, {
bridge: bridge, backdrop: 'static',
isBlocking: true,
size: 'lg', size: 'lg',
}, IrcNetworksDialogContext)); });
selfhostedRef.result.then(() => {
try {
this.reload()
} catch (err) {
console.error(err);
this.translate.get('Failed to get an update IRC bridge list').subscribe((res: string) => {this.toaster.pop("error", res); });
}
})
const selfhostedInstance = selfhostedRef.componentInstance as IrcNetworksDialogContext;
selfhostedInstance.bridge = bridge;
} }
} }

View File

@ -1,29 +1,28 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{ bridge.upstreamId ? "matrix.org's" : "Self-hosted" }} IRC Bridge Networks</h4>
<h4>{{ bridge.upstreamId ? "matrix.org's" : "Self-hosted" }} IRC Bridge Networks</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
<table class="table table-striped table-condensed table-bordered"> <table class="table table-striped table-condensed table-bordered">
<thead> <thead>
<tr> <tr>
<th>{{'Network' | translate}}</th> <th>{{'Network' | translate}}</th>
<th>{{'Enabled' | translate}}</th> <th>{{'Enabled' | translate}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let network of networks trackById"> <tr *ngFor="let network of networks trackById">
<td>{{ network.name }}</td> <td>{{ network.name }}</td>
<td> <td>
<ui-switch [checked]="network.isEnabled" size="small" [disabled]="isUpdating" <ui-switch [checked]="network.isEnabled" size="small" [disabled]="isUpdating"
(change)="toggleNetwork(network)"></ui-switch> (change)="toggleNetwork(network)"></ui-switch>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="dialog.close()" title="close" class="btn btn-primary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-outline-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}} <i class="far fa-times-circle"></i> {{'Close' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,13 +1,12 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { FE_IrcBridge } from "../../../../shared/models/irc"; import { FE_IrcBridge } from "../../../../shared/models/irc";
import { AdminIrcApiService } from "../../../../shared/services/admin/admin-irc-api.service"; import { AdminIrcApiService } from "../../../../shared/services/admin/admin-irc-api.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
export class IrcNetworksDialogContext extends BSModalContext { export interface IrcNetworksDialogContext {
public bridge: FE_IrcBridge; bridge: FE_IrcBridge;
} }
interface LocalNetwork { interface LocalNetwork {
@ -22,19 +21,17 @@ interface LocalNetwork {
templateUrl: "./networks.component.html", templateUrl: "./networks.component.html",
styleUrls: ["./networks.component.scss"], styleUrls: ["./networks.component.scss"],
}) })
export class AdminIrcBridgeNetworksComponent implements ModalComponent<IrcNetworksDialogContext> { export class AdminIrcBridgeNetworksComponent {
public isUpdating = false; public isUpdating = false;
public bridge: FE_IrcBridge; public bridge: FE_IrcBridge;
public networks: LocalNetwork[]; public networks: LocalNetwork[];
constructor(public dialog: DialogRef<IrcNetworksDialogContext>, constructor(public modal: NgbActiveModal,
private ircApi: AdminIrcApiService, private ircApi: AdminIrcApiService,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.bridge = dialog.context.bridge;
const networkIds = Object.keys(this.bridge.availableNetworks); const networkIds = Object.keys(this.bridge.availableNetworks);
this.networks = networkIds.map(i => { this.networks = networkIds.map(i => {
return { return {

View File

@ -1,26 +1,25 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'self-hosted Slack bridge' | translate}} ({{ isAdding ? "Add a new" : "Edit" }})</h4>
<h4> {{'self-hosted Slack bridge' | translate}} ({{ isAdding ? "Add a new" : "Edit" }})</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
<p> <p>
{{'Self-hosted Slack bridges already have provisioning enabled. Be careful not to expose the API to the public internet.' | translate}} {{'Self-hosted Slack bridges already have provisioning enabled. Be careful not to expose the API to the public internet.' | translate}}
</p> </p>
<label class="label-block"> <label class="label-block">
{{'Provisioning URL' | translate}} {{'Provisioning URL' | translate}}
<span class="text-muted ">{{'The provisioning URL for the bridge. This is usually the same as the URL your homeserver uses to communicate with the bridge.' | translate}}</span> <span class="text-muted ">{{'The provisioning URL for the bridge. This is usually the same as the URL your homeserver uses to communicate with the bridge.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="http://localhost:9000" placeholder="http://localhost:9000"
[(ngModel)]="provisionUrl" [disabled]="isSaving"/> [(ngModel)]="provisionUrl" [disabled]="isSaving"/>
</label> </label>
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="add()" title="close" class="btn btn-primary btn-sm"> <button type="button" (click)="add()" title="close" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}} <i class="far fa-save"></i> {{'Save' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,34 +1,31 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { AdminSlackApiService } from "../../../../shared/services/admin/admin-slack-api.service"; import { AdminSlackApiService } from "../../../../shared/services/admin/admin-slack-api.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
export class ManageSelfhostedSlackBridgeDialogContext extends BSModalContext { export interface ManageSelfhostedSlackBridgeDialogContext {
public provisionUrl: string; provisionUrl: string;
public bridgeId: number; bridgeId: number;
isAdding: boolean;
} }
@Component({ @Component({
templateUrl: "./manage-selfhosted.component.html", templateUrl: "./manage-selfhosted.component.html",
styleUrls: ["./manage-selfhosted.component.scss"], styleUrls: ["./manage-selfhosted.component.scss"],
}) })
export class AdminSlackBridgeManageSelfhostedComponent implements ModalComponent<ManageSelfhostedSlackBridgeDialogContext> { export class AdminSlackBridgeManageSelfhostedComponent {
public isSaving = false; public isSaving = false;
public provisionUrl: string; public provisionUrl: string;
public bridgeId: number; public bridgeId: number;
public isAdding = false; public isAdding = true;
constructor(public dialog: DialogRef<ManageSelfhostedSlackBridgeDialogContext>, constructor(public modal: NgbActiveModal,
private slackApi: AdminSlackApiService, private slackApi: AdminSlackApiService,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.provisionUrl = dialog.context.provisionUrl;
this.bridgeId = dialog.context.bridgeId;
this.isAdding = !this.bridgeId;
} }
public add() { public add() {
@ -36,7 +33,7 @@ export class AdminSlackBridgeManageSelfhostedComponent implements ModalComponent
if (this.isAdding) { if (this.isAdding) {
this.slackApi.newSelfhosted(this.provisionUrl).then(() => { this.slackApi.newSelfhosted(this.provisionUrl).then(() => {
this.translate.get('Slack bridge added').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Slack bridge added').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
console.error(err); console.error(err);
this.isSaving = false; this.isSaving = false;
@ -45,7 +42,7 @@ export class AdminSlackBridgeManageSelfhostedComponent implements ModalComponent
} else { } else {
this.slackApi.updateSelfhosted(this.bridgeId, this.provisionUrl).then(() => { this.slackApi.updateSelfhosted(this.bridgeId, this.provisionUrl).then(() => {
this.translate.get('Slack bridge updated').subscribe((res: string) => this.toaster.pop("success", res)); this.translate.get('Slack bridge updated').subscribe((res: string) => this.toaster.pop("success", res));
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
console.error(err); console.error(err);
this.isSaving = false; this.isSaving = false;

View File

@ -1,6 +1,5 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { FE_Upstream } from "../../../shared/models/admin-responses"; import { FE_Upstream } from "../../../shared/models/admin-responses";
import { AdminUpstreamApiService } from "../../../shared/services/admin/admin-upstream-api.service"; import { AdminUpstreamApiService } from "../../../shared/services/admin/admin-upstream-api.service";
import { import {
@ -10,6 +9,7 @@ import {
import { FE_SlackBridge } from "../../../shared/models/slack"; import { FE_SlackBridge } from "../../../shared/models/slack";
import { AdminSlackApiService } from "../../../shared/services/admin/admin-slack-api.service"; import { AdminSlackApiService } from "../../../shared/services/admin/admin-slack-api.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({ @Component({
templateUrl: "./slack.component.html", templateUrl: "./slack.component.html",
@ -26,7 +26,7 @@ export class AdminSlackBridgeComponent implements OnInit {
constructor(private slackApi: AdminSlackApiService, constructor(private slackApi: AdminSlackApiService,
private upstreamApi: AdminUpstreamApiService, private upstreamApi: AdminUpstreamApiService,
private toaster: ToasterService, private toaster: ToasterService,
private modal: Modal, private modal: NgbModal,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
} }
@ -75,31 +75,38 @@ export class AdminSlackBridgeComponent implements OnInit {
} }
public addSelfHostedBridge() { public addSelfHostedBridge() {
this.modal.open(AdminSlackBridgeManageSelfhostedComponent, overlayConfigFactory({ const selfhostedRef = this.modal.open(AdminSlackBridgeManageSelfhostedComponent, {
isBlocking: true, backdrop: 'static',
size: 'lg', size: 'lg',
});
provisionUrl: '', selfhostedRef.result.then(() => {
}, ManageSelfhostedSlackBridgeDialogContext)).result.then(() => { try {
this.reload().catch(err => { this.reload()
} catch (err) {
console.error(err); console.error(err);
this.translate.get('Failed to get an update Slack bridge list').subscribe((res: string) => {this.toaster.pop("error", res); }); this.translate.get('Failed to get an update Slack bridge list').subscribe((res: string) => {this.toaster.pop("error", res); });
}); }
}); });
const selfhostedInstance = selfhostedRef.componentInstance as ManageSelfhostedSlackBridgeDialogContext;
selfhostedInstance.provisionUrl = '';
} }
public editBridge(bridge: FE_SlackBridge) { public editBridge(bridge: FE_SlackBridge) {
this.modal.open(AdminSlackBridgeManageSelfhostedComponent, overlayConfigFactory({ const selfhostedRef = this.modal.open(AdminSlackBridgeManageSelfhostedComponent, {
isBlocking: true, backdrop: 'static',
size: 'lg', size: 'lg',
});
provisionUrl: bridge.provisionUrl, selfhostedRef.result.then(() => {
bridgeId: bridge.id, try {
}, ManageSelfhostedSlackBridgeDialogContext)).result.then(() => { this.reload()
this.reload().catch(err => { } catch (err) {
console.error(err); console.error(err);
this.translate.get('Failed to get an update Slack bridge list').subscribe((res: string) => {this.toaster.pop("error", res); }); this.translate.get('Failed to get an update Slack bridge list').subscribe((res: string) => {this.toaster.pop("error", res); });
}); }
}); });
const selfhostedInstance = selfhostedRef.componentInstance as ManageSelfhostedSlackBridgeDialogContext;
selfhostedInstance.provisionUrl = '';
selfhostedInstance.bridgeId = bridge.id;
selfhostedInstance.isAdding = !bridge.id;
} }
} }

View File

@ -1,46 +1,45 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'self-hosted Telegram bridge' | translate}} ({{ isAdding ? "Add a new" : "Edit" }})</h4>
<h4> {{'self-hosted Telegram bridge' | translate}} ({{ isAdding ? "Add a new" : "Edit" }})</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
<p>{{'Self-hosted Telegram bridges must have' | translate}} <code>{{'provisioning' | translate}}</code> {{'enabled in the configuration.' | translate}}</p> <p>{{'Self-hosted Telegram bridges must have' | translate}} <code>{{'provisioning' | translate}}</code> {{'enabled in the configuration.' | translate}}</p>
<label class="label-block"> <label class="label-block">
{{'Provisioning URL' | translate}} {{'Provisioning URL' | translate}}
<span class="text-muted ">{{'The provisioning URL for the bridge. This is the public address for the bridge followed by the provisioning prefix given in the configuration.' | translate}}</span> <span class="text-muted ">{{'The provisioning URL for the bridge. This is the public address for the bridge followed by the provisioning prefix given in the configuration.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="http://localhost:9999/_matrix/provision/v1" placeholder="http://localhost:9999/_matrix/provision/v1"
[(ngModel)]="provisionUrl" [disabled]="isSaving"/> [(ngModel)]="provisionUrl" [disabled]="isSaving"/>
</label> </label>
<label class="label-block"> <label class="label-block">
{{'Shared Secret' | translate}} {{'Shared Secret' | translate}}
<span class="text-muted ">{{'The shared secret defined in the configuration for provisioning.' | translate}}</span> <span class="text-muted ">{{'The shared secret defined in the configuration for provisioning.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="some_secret_value" placeholder="some_secret_value"
[(ngModel)]="sharedSecret" [disabled]="isSaving"/> [(ngModel)]="sharedSecret" [disabled]="isSaving"/>
</label> </label>
<label class="label-block"> <label class="label-block">
{{'Promote Telegram Puppeting' | translate}} {{'Promote Telegram Puppeting' | translate}}
<span class="text-muted ">{{'If enabled, Dimension will recommend that users log in to their Telegram accounts.' | translate}}</span> <span class="text-muted ">{{'If enabled, Dimension will recommend that users log in to their Telegram accounts.' | translate}}</span>
<ui-switch [checked]="allowTgPuppets" size="small" [disabled]="isSaving" <ui-switch [checked]="allowTgPuppets" size="small" [disabled]="isSaving"
(change)="allowTgPuppets = !allowTgPuppets"></ui-switch> (change)="allowTgPuppets = !allowTgPuppets"></ui-switch>
</label> </label>
<label class="label-block"> <label class="label-block">
{{'Promote Matrix Puppeting' | translate}} {{'Promote Matrix Puppeting' | translate}}
<span class="text-muted ">{{'If enabled, Dimension will recommend that users log in to their Matrix accounts.' | translate}}</span> <span class="text-muted ">{{'If enabled, Dimension will recommend that users log in to their Matrix accounts.' | translate}}</span>
<ui-switch [checked]="allowMxPuppets" size="small" [disabled]="isSaving" <ui-switch [checked]="allowMxPuppets" size="small" [disabled]="isSaving"
(change)="allowMxPuppets = !allowMxPuppets"></ui-switch> (change)="allowMxPuppets = !allowMxPuppets"></ui-switch>
</label> </label>
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="add()" title="close" class="btn btn-primary btn-sm"> <button type="button" (click)="add()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}} <i class="far fa-save"></i> {{'Save' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,43 +1,37 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { AdminTelegramApiService } from "../../../../shared/services/admin/admin-telegram-api.service"; import { AdminTelegramApiService } from "../../../../shared/services/admin/admin-telegram-api.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
export class ManageSelfhostedTelegramBridgeDialogContext extends BSModalContext { export interface ManageSelfhostedTelegramBridgeDialogContext {
public provisionUrl: string; provisionUrl: string;
public sharedSecret: string; sharedSecret: string;
public allowTgPuppets = false; allowTgPuppets: boolean;
public allowMxPuppets = false; allowMxPuppets: boolean;
public bridgeId: number; bridgeId: number;
isAdding: boolean;
} }
@Component({ @Component({
templateUrl: "./manage-selfhosted.component.html", templateUrl: "./manage-selfhosted.component.html",
styleUrls: ["./manage-selfhosted.component.scss"], styleUrls: ["./manage-selfhosted.component.scss"],
}) })
export class AdminTelegramBridgeManageSelfhostedComponent implements ModalComponent<ManageSelfhostedTelegramBridgeDialogContext> { export class AdminTelegramBridgeManageSelfhostedComponent {
public isSaving = false; isSaving = false;
public provisionUrl: string; provisionUrl: string;
public sharedSecret: string; sharedSecret: string;
public allowTgPuppets = false; allowTgPuppets = false;
public allowMxPuppets = false; allowMxPuppets = false;
public bridgeId: number; bridgeId: number;
public isAdding = false; isAdding = true;
constructor(public dialog: DialogRef<ManageSelfhostedTelegramBridgeDialogContext>, constructor(public modal: NgbActiveModal,
private telegramApi: AdminTelegramApiService, private telegramApi: AdminTelegramApiService,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.provisionUrl = dialog.context.provisionUrl;
this.sharedSecret = dialog.context.sharedSecret;
this.allowTgPuppets = dialog.context.allowTgPuppets;
this.allowMxPuppets = dialog.context.allowMxPuppets;
this.bridgeId = dialog.context.bridgeId;
this.isAdding = !this.bridgeId;
} }
public add() { public add() {
@ -49,7 +43,7 @@ export class AdminTelegramBridgeManageSelfhostedComponent implements ModalCompon
if (this.isAdding) { if (this.isAdding) {
this.telegramApi.newSelfhosted(this.provisionUrl, this.sharedSecret, options).then(() => { this.telegramApi.newSelfhosted(this.provisionUrl, this.sharedSecret, options).then(() => {
this.translate.get('Telegram bridge added').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Telegram bridge added').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
console.error(err); console.error(err);
this.isSaving = false; this.isSaving = false;
@ -58,7 +52,7 @@ export class AdminTelegramBridgeManageSelfhostedComponent implements ModalCompon
} else { } else {
this.telegramApi.updateSelfhosted(this.bridgeId, this.provisionUrl, this.sharedSecret, options).then(() => { this.telegramApi.updateSelfhosted(this.bridgeId, this.provisionUrl, this.sharedSecret, options).then(() => {
this.translate.get('Telegram bridge updated').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Telegram bridge updated').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
console.error(err); console.error(err);
this.isSaving = false; this.isSaving = false;

View File

@ -1,6 +1,5 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { import {
AdminTelegramBridgeManageSelfhostedComponent, AdminTelegramBridgeManageSelfhostedComponent,
ManageSelfhostedTelegramBridgeDialogContext ManageSelfhostedTelegramBridgeDialogContext
@ -8,6 +7,7 @@ import {
import { FE_TelegramBridge } from "../../../shared/models/telegram"; import { FE_TelegramBridge } from "../../../shared/models/telegram";
import { AdminTelegramApiService } from "../../../shared/services/admin/admin-telegram-api.service"; import { AdminTelegramApiService } from "../../../shared/services/admin/admin-telegram-api.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({ @Component({
templateUrl: "./telegram.component.html", templateUrl: "./telegram.component.html",
@ -21,7 +21,7 @@ export class AdminTelegramBridgeComponent implements OnInit {
constructor(private telegramApi: AdminTelegramApiService, constructor(private telegramApi: AdminTelegramApiService,
private toaster: ToasterService, private toaster: ToasterService,
private modal: Modal, private modal: NgbModal,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
} }
@ -40,19 +40,23 @@ export class AdminTelegramBridgeComponent implements OnInit {
} }
public addSelfHostedBridge() { public addSelfHostedBridge() {
this.modal.open(AdminTelegramBridgeManageSelfhostedComponent, overlayConfigFactory({ const selfhostedRef = this.modal.open(AdminTelegramBridgeManageSelfhostedComponent, {
isBlocking: true, backdrop: 'static',
size: 'lg', size: 'lg',
});
provisionUrl: '', selfhostedRef.result.then(() => {
sharedSecret: '', try {
allowPuppets: false, this.reload()
}, ManageSelfhostedTelegramBridgeDialogContext)).result.then(() => { } catch (err) {
this.reload().catch(err => {
console.error(err); console.error(err);
this.translate.get('Failed to get an update Telegram bridge list').subscribe((res: string) => {this.toaster.pop("error", res); }); this.translate.get('Failed to get an update Telegram bridge list').subscribe((res: string) => {this.toaster.pop("error", res); });
}); }
}); })
const selfhostedInstance = selfhostedRef.componentInstance as ManageSelfhostedTelegramBridgeDialogContext;
selfhostedInstance.provisionUrl = '';
selfhostedInstance.sharedSecret = '';
selfhostedInstance.allowMxPuppets = false;
selfhostedInstance.allowTgPuppets = false;
} }
public getEnabledFeaturesString(bridge: FE_TelegramBridge): string { public getEnabledFeaturesString(bridge: FE_TelegramBridge): string {
@ -63,20 +67,24 @@ export class AdminTelegramBridgeComponent implements OnInit {
} }
public editBridge(bridge: FE_TelegramBridge) { public editBridge(bridge: FE_TelegramBridge) {
this.modal.open(AdminTelegramBridgeManageSelfhostedComponent, overlayConfigFactory({ const selfhostedRef = this.modal.open(AdminTelegramBridgeManageSelfhostedComponent, {
isBlocking: true, backdrop: 'static',
size: 'lg', size: 'lg',
});
provisionUrl: bridge.provisionUrl, selfhostedRef.result.then(() => {
sharedSecret: bridge.sharedSecret, try {
allowTgPuppets: bridge.options ? bridge.options.allowTgPuppets : false, this.reload()
allowMxPuppets: bridge.options ? bridge.options.allowMxPuppets : false, } catch (err) {
bridgeId: bridge.id,
}, ManageSelfhostedTelegramBridgeDialogContext)).result.then(() => {
this.reload().catch(err => {
console.error(err); console.error(err);
this.translate.get('Failed to get an update Telegram bridge list').subscribe((res: string) => {this.toaster.pop("error", res); }); this.translate.get('Failed to get an update Telegram bridge list').subscribe((res: string) => {this.toaster.pop("error", res); });
}); }
}); })
const selfhostedInstance = selfhostedRef.componentInstance as ManageSelfhostedTelegramBridgeDialogContext;
selfhostedInstance.provisionUrl = bridge.provisionUrl;
selfhostedInstance.sharedSecret = bridge.sharedSecret;
selfhostedInstance.allowMxPuppets = bridge.options?.allowTgPuppets || false;
selfhostedInstance.allowTgPuppets = bridge.options?.allowMxPuppets || false;
selfhostedInstance.bridgeId = bridge.id;
selfhostedInstance.isAdding = !bridge.id;
} }
} }

View File

@ -1,32 +1,31 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'self-hosted webhook bridge' | translate}} ({{ isAdding ? "Add a new" : "Edit" }})</h4>
<h4> {{'self-hosted webhook bridge' | translate}} ({{ isAdding ? "Add a new" : "Edit" }})</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
<p>{{'Self-hosted webhook bridges must have' | translate}} <code>{{'provisioning' | translate}}</code> {{'enabled in the configuration.' | translate}}</p> <p>{{'Self-hosted webhook bridges must have' | translate}} <code>{{'provisioning' | translate}}</code> {{'enabled in the configuration.' | translate}}</p>
<label class="label-block"> <label class="label-block">
{{'Provisioning URL' | translate}} {{'Provisioning URL' | translate}}
<span class="text-muted ">{{'The public URL for the bridge.' | translate}}</span> <span class="text-muted ">{{'The public URL for the bridge.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="https://webhooks.example.org:9000/" placeholder="https://webhooks.example.org:9000/"
[(ngModel)]="provisionUrl" [disabled]="isSaving"/> [(ngModel)]="provisionUrl" [disabled]="isSaving"/>
</label> </label>
<label class="label-block"> <label class="label-block">
{{'Shared Secret' | translate}} {{'Shared Secret' | translate}}
<span class="text-muted ">{{'The provisioning secret defined in the configuration.' | translate}}</span> <span class="text-muted ">{{'The provisioning secret defined in the configuration.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="some_secret_value" placeholder="some_secret_value"
[(ngModel)]="sharedSecret" [disabled]="isSaving"/> [(ngModel)]="sharedSecret" [disabled]="isSaving"/>
</label> </label>
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="add()" title="close" class="btn btn-primary btn-sm"> <button type="button" (click)="add()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}} <i class="far fa-save"></i> {{'Save' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,39 +1,37 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { AdminWebhooksApiService } from "../../../../shared/services/admin/admin-webhooks-api.service"; import { AdminWebhooksApiService } from "../../../../shared/services/admin/admin-webhooks-api.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
export class ManageSelfhostedWebhooksBridgeDialogContext extends BSModalContext { export interface ManageSelfhostedWebhooksBridgeDialogContext {
public provisionUrl: string; provisionUrl: string;
public sharedSecret: string; sharedSecret: string;
public allowTgPuppets = false; allowTgPuppets: boolean;
public allowMxPuppets = false; allowMxPuppets: boolean;
public bridgeId: number; bridgeId: number;
isAdding: boolean;
} }
@Component({ @Component({
templateUrl: "./manage-selfhosted.component.html", templateUrl: "./manage-selfhosted.component.html",
styleUrls: ["./manage-selfhosted.component.scss"], styleUrls: ["./manage-selfhosted.component.scss"],
}) })
export class AdminWebhooksBridgeManageSelfhostedComponent implements ModalComponent<ManageSelfhostedWebhooksBridgeDialogContext> { export class AdminWebhooksBridgeManageSelfhostedComponent {
public isSaving = false; public isSaving = false;
public provisionUrl: string; public provisionUrl: '';
public sharedSecret: string; public sharedSecret: '';
public allowTgPuppets = false;
public allowMxPuppets = false;
public bridgeId: number; public bridgeId: number;
public isAdding = false; public isAdding = true;
constructor(public dialog: DialogRef<ManageSelfhostedWebhooksBridgeDialogContext>, constructor(public modal: NgbActiveModal,
private webhooksApi: AdminWebhooksApiService, private webhooksApi: AdminWebhooksApiService,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.provisionUrl = dialog.context.provisionUrl;
this.sharedSecret = dialog.context.sharedSecret;
this.bridgeId = dialog.context.bridgeId;
this.isAdding = !this.bridgeId;
} }
public add() { public add() {
@ -41,7 +39,7 @@ export class AdminWebhooksBridgeManageSelfhostedComponent implements ModalCompon
if (this.isAdding) { if (this.isAdding) {
this.webhooksApi.newSelfhosted(this.provisionUrl, this.sharedSecret).then(() => { this.webhooksApi.newSelfhosted(this.provisionUrl, this.sharedSecret).then(() => {
this.translate.get('Webhook bridge added').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Webhook bridge added').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
console.error(err); console.error(err);
this.isSaving = false; this.isSaving = false;
@ -50,7 +48,7 @@ export class AdminWebhooksBridgeManageSelfhostedComponent implements ModalCompon
} else { } else {
this.webhooksApi.updateSelfhosted(this.bridgeId, this.provisionUrl, this.sharedSecret).then(() => { this.webhooksApi.updateSelfhosted(this.bridgeId, this.provisionUrl, this.sharedSecret).then(() => {
this.translate.get('Webhook bridge updated').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Webhook bridge updated').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
console.error(err); console.error(err);
this.isSaving = false; this.isSaving = false;

View File

@ -1,6 +1,5 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { import {
AdminWebhooksBridgeManageSelfhostedComponent, AdminWebhooksBridgeManageSelfhostedComponent,
ManageSelfhostedWebhooksBridgeDialogContext ManageSelfhostedWebhooksBridgeDialogContext
@ -8,6 +7,7 @@ import {
import { FE_WebhooksBridge } from "../../../shared/models/webhooks"; import { FE_WebhooksBridge } from "../../../shared/models/webhooks";
import { AdminWebhooksApiService } from "../../../shared/services/admin/admin-webhooks-api.service"; import { AdminWebhooksApiService } from "../../../shared/services/admin/admin-webhooks-api.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({ @Component({
templateUrl: "./webhooks.component.html", templateUrl: "./webhooks.component.html",
@ -21,7 +21,7 @@ export class AdminWebhooksBridgeComponent implements OnInit {
constructor(private webhooksApi: AdminWebhooksApiService, constructor(private webhooksApi: AdminWebhooksApiService,
private toaster: ToasterService, private toaster: ToasterService,
private modal: Modal, private modal: NgbModal,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
} }
@ -40,34 +40,42 @@ export class AdminWebhooksBridgeComponent implements OnInit {
} }
public addSelfHostedBridge() { public addSelfHostedBridge() {
this.modal.open(AdminWebhooksBridgeManageSelfhostedComponent, overlayConfigFactory({ const selfhostedRef = this.modal.open(AdminWebhooksBridgeManageSelfhostedComponent, {
isBlocking: true, backdrop: 'static',
size: 'lg', size: 'lg',
});
provisionUrl: '', selfhostedRef.result.then(() => {
sharedSecret: '', try {
allowPuppets: false, this.reload()
}, ManageSelfhostedWebhooksBridgeDialogContext)).result.then(() => { } catch (err) {
this.reload().catch(err => {
console.error(err); console.error(err);
this.translate.get('Failed to get an update Webhooks bridge list').subscribe((res: string) => {this.toaster.pop("error", res); }); this.translate.get('Failed to get an update Webhooks bridge list').subscribe((res: string) => {this.toaster.pop("error", res); });
}); }
}); });
const selfhostedInstance = selfhostedRef.componentInstance as ManageSelfhostedWebhooksBridgeDialogContext;
selfhostedInstance.provisionUrl = '';
selfhostedInstance.sharedSecret = '';
selfhostedInstance.allowMxPuppets = false;
selfhostedInstance.allowTgPuppets = false;
} }
public editBridge(bridge: FE_WebhooksBridge) { public editBridge(bridge: FE_WebhooksBridge) {
this.modal.open(AdminWebhooksBridgeManageSelfhostedComponent, overlayConfigFactory({ const selfhostedRef = this.modal.open(AdminWebhooksBridgeManageSelfhostedComponent, {
isBlocking: true, backdrop: 'static',
size: 'lg', size: 'lg',
});
provisionUrl: bridge.provisionUrl, selfhostedRef.result.then(() => {
sharedSecret: bridge.sharedSecret, try {
bridgeId: bridge.id, this.reload()
}, ManageSelfhostedWebhooksBridgeDialogContext)).result.then(() => { } catch (err) {
this.reload().catch(err => {
console.error(err); console.error(err);
this.translate.get('Failed to get an update Webhooks bridge list').subscribe((res: string) => {this.toaster.pop("error", res); }); this.translate.get('Failed to get an update Webhooks bridge list').subscribe((res: string) => {this.toaster.pop("error", res); });
}); }
}); });
const selfhostedInstance = selfhostedRef.componentInstance as ManageSelfhostedWebhooksBridgeDialogContext;
selfhostedInstance.provisionUrl = bridge.provisionUrl;
selfhostedInstance.sharedSecret = bridge.sharedSecret
selfhostedInstance.bridgeId = bridge.id;
selfhostedInstance.isAdding = !bridge.id;
} }
} }

View File

@ -1,56 +1,55 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{ isAdding ? "Add a new" : "Edit" }} {{ 'custom bot' | translate}}</h4>
<h4>{{ isAdding ? "Add a new" : "Edit" }} {{ 'custom bot' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
<label class="label-block"> <label class="label-block">
User ID User ID
<span class="text-muted">{{'The user ID that Dimension will invite to rooms.' | translate}}</span> <span class="text-muted">{{'The user ID that Dimension will invite to rooms.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="@yourbot:example.org" placeholder="@yourbot:example.org"
[(ngModel)]="bot.userId" [disabled]="isSaving" (blur)="loadProfile()"/> [(ngModel)]="bot.userId" [disabled]="isSaving" (blur)="loadProfile()"/>
</label> </label>
<label class="label-block"> <label class="label-block">
Description Description
<span class="text-muted ">{{'A few words here will help people understand what the bot does.' | translate}}</span> <span class="text-muted ">{{'A few words here will help people understand what the bot does.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="Does awesome things" placeholder="Does awesome things"
[(ngModel)]="bot.description" [disabled]="isSaving"/> [(ngModel)]="bot.description" [disabled]="isSaving"/>
</label> </label>
<label class="label-block"> <label class="label-block">
{{'Display Name' | translate}} {{'Display Name' | translate}}
<span class="text-muted ">{{'This is the name Dimension will use to tell users which bot this is.' | translate}}</span> <span class="text-muted ">{{'This is the name Dimension will use to tell users which bot this is.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="Cool Bot" placeholder="Cool Bot"
[(ngModel)]="bot.name" [disabled]="isSaving"/> [(ngModel)]="bot.name" [disabled]="isSaving"/>
</label> </label>
<label class="label-block"> <label class="label-block">
Avatar URL Avatar URL
<span class="text-muted ">{{'This can either be an MXC URI or a plain URL.' | translate}}</span> <span class="text-muted ">{{'This can either be an MXC URI or a plain URL.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="mxc://example.org/C00lAvat4r" placeholder="mxc://example.org/C00lAvat4r"
[(ngModel)]="bot.avatarUrl" [disabled]="isSaving"/> [(ngModel)]="bot.avatarUrl" [disabled]="isSaving"/>
</label> </label>
<label class="label-block"> <label class="label-block">
Access Token Access Token
<span class="text-muted ">{{'This is used by Dimension to force the bot to leave the room when the user removes the bot.' | translate}} <span class="text-muted ">{{'This is used by Dimension to force the bot to leave the room when the user removes the bot.' | translate}}
<a href="https://t2bot.io/docs/access_tokens/" target="_blank">{{'Learn more about access tokens.' | translate}}</a>. <a href="https://t2bot.io/docs/access_tokens/" target="_blank">{{'Learn more about access tokens.' | translate}}</a>.
</span> </span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="MDaX..." placeholder="MDaX..."
[(ngModel)]="bot.accessToken" [disabled]="isSaving"/> [(ngModel)]="bot.accessToken" [disabled]="isSaving"/>
</label> </label>
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="add()" title="close" class="btn btn-primary btn-sm"> <button type="button" (click)="add()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}} <i class="far fa-save"></i> {{'Save' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i>{{'Cancel' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,34 +1,32 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { FE_CustomSimpleBot, FE_UserProfile } from "../../../shared/models/admin-responses"; import { FE_CustomSimpleBot, FE_UserProfile } from "../../../shared/models/admin-responses";
import { AdminCustomSimpleBotsApiService } from "../../../shared/services/admin/admin-custom-simple-bots-api.service"; import { AdminCustomSimpleBotsApiService } from "../../../shared/services/admin/admin-custom-simple-bots-api.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
export class AddCustomBotDialogContext extends BSModalContext { export interface AddCustomBotDialogContext {
bot: FE_CustomSimpleBot; bot: FE_CustomSimpleBot;
isAdding: boolean;
} }
@Component({ @Component({
templateUrl: "./add.component.html", templateUrl: "./add.component.html",
styleUrls: ["./add.component.scss"], styleUrls: ["./add.component.scss"],
}) })
export class AdminAddCustomBotComponent implements ModalComponent<AddCustomBotDialogContext> { export class AdminAddCustomBotComponent {
public bot: FE_CustomSimpleBot; public bot = <FE_CustomSimpleBot>{};
public isAdding = false; public isAdding = true;
public isSaving = false; public isSaving = false;
private lastProfile: FE_UserProfile; private lastProfile: FE_UserProfile;
constructor(public dialog: DialogRef<AddCustomBotDialogContext>, constructor(public modal: NgbActiveModal,
private botApi: AdminCustomSimpleBotsApiService, private botApi: AdminCustomSimpleBotsApiService,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.bot = this.dialog.context.bot || <FE_CustomSimpleBot>{};
this.isAdding = !this.dialog.context.bot;
} }
public loadProfile() { public loadProfile() {
@ -89,7 +87,7 @@ export class AdminAddCustomBotComponent implements ModalComponent<AddCustomBotDi
promise.then(() => { promise.then(() => {
this.translate.get('Bot updated').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Bot updated').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(error => { }).catch(error => {
this.isSaving = false; this.isSaving = false;
console.error(error); console.error(error);

View File

@ -2,9 +2,9 @@ import { Component } from "@angular/core";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { FE_CustomSimpleBot } from "../../shared/models/admin-responses"; import { FE_CustomSimpleBot } from "../../shared/models/admin-responses";
import { AdminCustomSimpleBotsApiService } from "../../shared/services/admin/admin-custom-simple-bots-api.service"; import { AdminCustomSimpleBotsApiService } from "../../shared/services/admin/admin-custom-simple-bots-api.service";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { AddCustomBotDialogContext, AdminAddCustomBotComponent } from "./add/add.component"; import { AddCustomBotDialogContext, AdminAddCustomBotComponent } from "./add/add.component";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({ @Component({
templateUrl: "./custom-bots.component.html", templateUrl: "./custom-bots.component.html",
@ -18,7 +18,7 @@ export class AdminCustomBotsComponent {
constructor(private botApi: AdminCustomSimpleBotsApiService, constructor(private botApi: AdminCustomSimpleBotsApiService,
private toaster: ToasterService, private toaster: ToasterService,
private modal: Modal, private modal: NgbModal,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
@ -36,29 +36,36 @@ export class AdminCustomBotsComponent {
} }
public addBot() { public addBot() {
this.modal.open(AdminAddCustomBotComponent, overlayConfigFactory({ const selfhostedRef = this.modal.open(AdminAddCustomBotComponent, {
isBlocking: true, backdrop: 'static',
size: 'lg', size: 'lg',
}, AddCustomBotDialogContext)).result.then(() => { });
this.reload().catch(err => { selfhostedRef.result.then(() => {
try {
this.reload()
} catch (err) {
console.error(err); console.error(err);
this.translate.get('Failed to get an updated bot list').subscribe((res: string) => {this.toaster.pop("error", res); }); this.translate.get('Failed to get an updated bot list').subscribe((res: string) => {this.toaster.pop("error", res); });
}); }
}); });
} }
public editBot(bot: FE_CustomSimpleBot) { public editBot(bot: FE_CustomSimpleBot) {
this.modal.open(AdminAddCustomBotComponent, overlayConfigFactory({ const selfhostedRef = this.modal.open(AdminAddCustomBotComponent, {
isBlocking: true, backdrop: 'static',
size: 'lg', size: 'lg',
});
bot: bot, selfhostedRef.result.then(() => {
}, AddCustomBotDialogContext)).result.then(() => { try {
this.reload().catch(err => { this.reload()
} catch (err) {
console.error(err); console.error(err);
this.translate.get('Failed to get an updated bot list').subscribe((res: string) => {this.toaster.pop("error", res); }); this.translate.get('Failed to get an updated bot list').subscribe((res: string) => {this.toaster.pop("error", res); });
}); }
}); });
const selfhostedInstance = selfhostedRef.componentInstance as AddCustomBotDialogContext;
selfhostedInstance.bot = bot;
selfhostedInstance.isAdding = !bot;
} }
public toggleBot(bot: FE_CustomSimpleBot) { public toggleBot(bot: FE_CustomSimpleBot) {

View File

@ -2,12 +2,11 @@ import { Component } from "@angular/core";
import { AdminApiService } from "../../shared/services/admin/admin-api.service"; import { AdminApiService } from "../../shared/services/admin/admin-api.service";
import { FE_DimensionConfig } from "../../shared/models/admin-responses"; import { FE_DimensionConfig } from "../../shared/models/admin-responses";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { import {
AdminLogoutConfirmationDialogComponent, AdminLogoutConfirmationDialogComponent,
LogoutConfirmationDialogContext
} from "./logout-confirmation/logout-confirmation.component"; } from "./logout-confirmation/logout-confirmation.component";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({ @Component({
templateUrl: "./home.component.html", templateUrl: "./home.component.html",
@ -20,7 +19,7 @@ export class AdminHomeComponent {
constructor(private adminApi: AdminApiService, constructor(private adminApi: AdminApiService,
private toaster: ToasterService, private toaster: ToasterService,
private modal: Modal, private modal: NgbModal,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
adminApi.getConfig().then(config => { adminApi.getConfig().then(config => {
@ -30,16 +29,18 @@ export class AdminHomeComponent {
} }
public logoutAll(): void { public logoutAll(): void {
this.modal.open(AdminLogoutConfirmationDialogComponent, overlayConfigFactory({ const selfhostedRef = this.modal.open(AdminLogoutConfirmationDialogComponent, {
isBlocking: true, backdrop: 'static'
}, LogoutConfirmationDialogContext)).result.then(() => { });
this.adminApi.logoutAll().then(() => { selfhostedRef.result.then(async () => {
try {
await this.adminApi.logoutAll();
this.translate.get('Everyone has been logged out').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Everyone has been logged out').subscribe((res: string) => {this.toaster.pop("success", res); });
this.config.sessionInfo.numTokens = 0; this.config.sessionInfo.numTokens = 0;
}).catch(err => { } catch (err) {
console.error(err); console.error(err);
this.translate.get('Error logging everyone out').subscribe((res: string) => {this.toaster.pop("error", res); }); this.translate.get('Error logging everyone out').subscribe((res: string) => {this.toaster.pop("error", res); });
}); }
}); });
} }
} }

View File

@ -1,18 +1,17 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'Logout confirmation' | translate}}</h4>
<h4>{{'Logout confirmation' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
<p> <p>
{{'Logging everyone out will disable all known login tokens for Dimension and upstream integration managers. Most clients will automatically re-register for a login token behind the scenes, similar to how a login token was first acquired.' | translate}} {{'Logging everyone out will disable all known login tokens for Dimension and upstream integration managers. Most clients will automatically re-register for a login token behind the scenes, similar to how a login token was first acquired.' | translate}}
</p> </p>
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="dialog.dismiss()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.dismiss()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="logout everyone" class="btn btn-danger btn-sm"> <button type="button" (click)="modal.close()" title="logout everyone" class="btn btn-danger btn-sm">
<i class="far fa-times-circle"></i> {{'Logout Everyone' | translate}} <i class="far fa-times-circle"></i> {{'Logout Everyone' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,16 +1,15 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { DialogRef, ModalComponent } from "ngx-modialog"; import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
export class LogoutConfirmationDialogContext extends BSModalContext { export interface LogoutConfirmationDialogContext {
} }
@Component({ @Component({
templateUrl: "./logout-confirmation.component.html", templateUrl: "./logout-confirmation.component.html",
styleUrls: ["./logout-confirmation.component.scss"], styleUrls: ["./logout-confirmation.component.scss"],
}) })
export class AdminLogoutConfirmationDialogComponent implements ModalComponent<LogoutConfirmationDialogContext> { export class AdminLogoutConfirmationDialogComponent {
constructor(public dialog: DialogRef<LogoutConfirmationDialogContext>) { constructor(public modal: NgbActiveModal) {
} }
} }

View File

@ -3,12 +3,12 @@ import { AdminAppserviceApiService } from "../../../shared/services/admin/admin-
import { AdminNebApiService } from "../../../shared/services/admin/admin-neb-api.service"; import { AdminNebApiService } from "../../../shared/services/admin/admin-neb-api.service";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { import {
AdminNebAppserviceConfigComponent, AdminNebAppserviceConfigComponent,
AppserviceConfigDialogContext AppserviceConfigDialogContext
} from "../appservice-config/appservice-config.component"; } from "../appservice-config/appservice-config.component";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({ @Component({
@ -26,9 +26,9 @@ export class AdminAddSelfhostedNebComponent {
private toaster: ToasterService, private toaster: ToasterService,
private router: Router, private router: Router,
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private modal: Modal, private modal: NgbModal,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;{
} }
public save(): void { public save(): void {
@ -37,12 +37,14 @@ export class AdminAddSelfhostedNebComponent {
return this.nebApi.newAppserviceConfiguration(this.adminUrl, appservice); return this.nebApi.newAppserviceConfiguration(this.adminUrl, appservice);
}).then(neb => { }).then(neb => {
this.translate.get('New go-neb created').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('New go-neb created').subscribe((res: string) => {this.toaster.pop("success", res); });
this.modal.open(AdminNebAppserviceConfigComponent, overlayConfigFactory({
neb: neb,
isBlocking: true, const selfhostedRef = this.modal.open(AdminNebAppserviceConfigComponent, {
backdrop: 'static',
size: 'lg', size: 'lg',
}, AppserviceConfigDialogContext)).result.then(() => this.router.navigate(["../.."], {relativeTo: this.activatedRoute})); });
selfhostedRef.result.then(() => this.router.navigate(["../.."], {relativeTo: this.activatedRoute}));
const selfhostedInstance = selfhostedRef.componentInstance as AppserviceConfigDialogContext;
selfhostedInstance.neb = neb;
}).catch(err => { }).catch(err => {
console.error(err); console.error(err);
this.isSaving = false; this.isSaving = false;

View File

@ -1,22 +1,21 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'go-neb appservice configuration' | translate}}</h4>
<h4>{{'go-neb appservice configuration' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content" *ngIf="isLoading"> <div class="modal-body" *ngif="isLoading">
<my-spinner></my-spinner> <my-spinner></my-spinner>
</div> </div>
<div class="dialog-content" *ngIf="!isLoading"> <div class="modal-body" *ngif="!isLoading">
{{'Copy and paste this configuration to' | translate}} <code>appservice-{{appservice.id}}.yaml</code> {{'Copy and paste this configuration to' | translate}} <code>appservice-{{appservice.id}}.yaml</code>
{{'on your homeserver and register it as an application service.' | translate}} {{'on your homeserver and register it as an application service.' | translate}}
<br/> <br/>
<pre>{{appserviceConfig}}</pre> <pre>{{appserviceConfig}}</pre>
</div> </div>
<div class="dialog-footer" *ngIf="!isLoading"> <div class="modal-footer">
<button type="button" (click)="dialog.close()" title="save" class="btn btn-primary btn-sm"> <button type="button" (click)="test()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}} <i class="fa fa-exchange-alt"></i> {{'Test Configuration' | translate}}
</button> </button>
<button type="button" (click)="test()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="fa fa-exchange-alt"></i> {{'Test Configuration' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,32 +1,29 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { FE_Appservice, FE_NebConfiguration } from "../../../shared/models/admin-responses"; import { FE_Appservice, FE_NebConfiguration } from "../../../shared/models/admin-responses";
import { AdminAppserviceApiService } from "../../../shared/services/admin/admin-appservice-api.service"; import { AdminAppserviceApiService } from "../../../shared/services/admin/admin-appservice-api.service";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
export class AppserviceConfigDialogContext extends BSModalContext { export interface AppserviceConfigDialogContext {
public neb: FE_NebConfiguration; neb: FE_NebConfiguration;
} }
@Component({ @Component({
templateUrl: "./appservice-config.component.html", templateUrl: "./appservice-config.component.html",
styleUrls: ["./appservice-config.component.scss"], styleUrls: ["./appservice-config.component.scss"],
}) })
export class AdminNebAppserviceConfigComponent implements ModalComponent<AppserviceConfigDialogContext> { export class AdminNebAppserviceConfigComponent {
public isLoading = true; public isLoading = true;
public neb: FE_NebConfiguration; public neb: FE_NebConfiguration;
public appservice: FE_Appservice; public appservice: FE_Appservice;
constructor(public dialog: DialogRef<AppserviceConfigDialogContext>, constructor(public modal: NgbActiveModal,
private adminAppserviceApi: AdminAppserviceApiService, private adminAppserviceApi: AdminAppserviceApiService,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.neb = dialog.context.neb;
this.adminAppserviceApi.getAppservice(this.neb.appserviceId).then(appservice => { this.adminAppserviceApi.getAppservice(this.neb.appserviceId).then(appservice => {
this.appservice = appservice; this.appservice = appservice;
this.isLoading = false; this.isLoading = false;

View File

@ -1,8 +1,7 @@
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { FE_NebConfiguration } from "../../../shared/models/admin-responses"; import { FE_NebConfiguration } from "../../../shared/models/admin-responses";
import { FE_Integration } from "../../../shared/models/integration"; import { FE_Integration } from "../../../shared/models/integration";
export class NebBotConfigurationDialogContext extends BSModalContext { export interface NebBotConfigurationDialogContext {
public integration: FE_Integration; integration: FE_Integration;
public neb: FE_NebConfiguration; neb: FE_NebConfiguration;
} }

View File

@ -1,33 +1,32 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'Giphy Configuration' | translate}}</h4>
<h4>{{'Giphy Configuration' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content" *ngIf="isLoading"> <div class="modal-body" *ngIf="isLoading">
<my-spinner></my-spinner> <my-spinner></my-spinner>
</div> </div>
<div class="dialog-content" *ngIf="!isLoading"> <div class="modal-body" *ngIf="!isLoading">
<label class="label-block"> <label class="label-block">
{{'Api Key' | translate}} {{'Api Key' | translate}}
<span class="text-muted ">{{'The API key from' | translate}} <a href="https://developers.giphy.com/" target="_blank">developers.giphy.com</a>.</span> <span class="text-muted ">{{'The API key from' | translate}} <a href="https://developers.giphy.com/" target="_blank">developers.giphy.com</a>.</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="your_api_key_here" placeholder="your_api_key_here"
[(ngModel)]="config.api_key" [disabled]="isUpdating"/> [(ngModel)]="config.api_key" [disabled]="isUpdating"/>
</label> </label>
<label class="label-block"> <label class="label-block">
{{'Image Size' | translate}} {{'Image Size' | translate}}
<span class="text-muted ">{{'GIFs can be large, and sometimes it is more desirable to have them downsized.' | translate}}</span> <span class="text-muted ">{{'GIFs can be large, and sometimes it is more desirable to have them downsized.' | translate}}</span>
<label class="checkbox"> <label class="checkbox">
<input type="checkbox" [(ngModel)]="config.use_downsized" [disabled]="isUpdating"/> <input type="checkbox" [(ngModel)]="config.use_downsized" [disabled]="isUpdating"/>
{{'Use downsized images' | translate}} {{'Use downsized images' | translate}}
</label> </label>
</label> </label>
</div> </div>
<div class="dialog-footer" *ngIf="!isLoading"> <div class="modal-footer">
<button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm"> <button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}} <i class="far fa-save"></i> {{'Save' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,11 +1,10 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { NebBotConfigurationDialogContext } from "../config-context";
import { AdminNebApiService } from "../../../../shared/services/admin/admin-neb-api.service"; import { AdminNebApiService } from "../../../../shared/services/admin/admin-neb-api.service";
import { FE_NebConfiguration } from "../../../../shared/models/admin-responses"; import { FE_NebConfiguration } from "../../../../shared/models/admin-responses";
import { FE_Integration } from "../../../../shared/models/integration"; import { FE_Integration } from "../../../../shared/models/integration";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
interface GiphyConfig { interface GiphyConfig {
api_key: string; api_key: string;
@ -16,7 +15,7 @@ interface GiphyConfig {
templateUrl: "./giphy.component.html", templateUrl: "./giphy.component.html",
styleUrls: ["./giphy.component.scss", "../config-dialog.scss"], styleUrls: ["./giphy.component.scss", "../config-dialog.scss"],
}) })
export class AdminNebGiphyConfigComponent implements ModalComponent<NebBotConfigurationDialogContext>, OnInit { export class AdminNebGiphyConfigComponent implements OnInit {
public isLoading = true; public isLoading = true;
public isUpdating = false; public isUpdating = false;
@ -24,13 +23,11 @@ export class AdminNebGiphyConfigComponent implements ModalComponent<NebBotConfig
public integration: FE_Integration; public integration: FE_Integration;
public neb: FE_NebConfiguration; public neb: FE_NebConfiguration;
constructor(public dialog: DialogRef<NebBotConfigurationDialogContext>, constructor(public modal: NgbActiveModal,
private adminNebApi: AdminNebApiService, private adminNebApi: AdminNebApiService,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.neb = dialog.context.neb;
this.integration = dialog.context.integration;
} }
public ngOnInit() { public ngOnInit() {
@ -47,7 +44,7 @@ export class AdminNebGiphyConfigComponent implements ModalComponent<NebBotConfig
this.isUpdating = true; this.isUpdating = true;
this.adminNebApi.setIntegrationConfiguration(this.neb.id, this.integration.type, this.config).then(() => { this.adminNebApi.setIntegrationConfiguration(this.neb.id, this.integration.type, this.config).then(() => {
this.translate.get('Configuration updated').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Configuration updated').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
this.isUpdating = false; this.isUpdating = false;
console.error(err); console.error(err);

View File

@ -1,32 +1,31 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'Google Configuration' | translate}}</h4>
<h4>{{'Google Configuration' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content" *ngIf="isLoading"> <div class="modal-body" *ngIf="isLoading">
<my-spinner></my-spinner> <my-spinner></my-spinner>
</div> </div>
<div class="dialog-content" *ngIf="!isLoading"> <div class="modal-body" *ngIf="!isLoading">
<label class="label-block"> <label class="label-block">
{{'Api Key' | translate}} {{'Api Key' | translate}}
<span class="text-muted ">{{'The API key for your Google Application.' | translate}}</span> <span class="text-muted ">{{'The API key for your Google Application.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="your_api_key_here" placeholder="your_api_key_here"
[(ngModel)]="config.api_key" [disabled]="isUpdating"/> [(ngModel)]="config.api_key" [disabled]="isUpdating"/>
</label> </label>
<label class="label-block"> <label class="label-block">
Search Engine ID {{'Search Engine ID' | translate}}
<span class="text-muted ">{{'The search engine ID' | translate}}</span> <span class="text-muted ">{{'The search engine ID' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="your_cx_id_here" placeholder="your_cx_id_here"
[(ngModel)]="config.cx" [disabled]="isUpdating"/> [(ngModel)]="config.cx" [disabled]="isUpdating"/>
</label> </label>
</div> </div>
<div class="dialog-footer" *ngIf="!isLoading"> <div class="modal-footer">
<button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm"> <button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}} <i class="far fa-save"></i> {{'Save' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,11 +1,10 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { NebBotConfigurationDialogContext } from "../config-context";
import { AdminNebApiService } from "../../../../shared/services/admin/admin-neb-api.service"; import { AdminNebApiService } from "../../../../shared/services/admin/admin-neb-api.service";
import { FE_NebConfiguration } from "../../../../shared/models/admin-responses"; import { FE_NebConfiguration } from "../../../../shared/models/admin-responses";
import { FE_Integration } from "../../../../shared/models/integration"; import { FE_Integration } from "../../../../shared/models/integration";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
interface GoogleConfig { interface GoogleConfig {
api_key: string; api_key: string;
@ -16,7 +15,7 @@ interface GoogleConfig {
templateUrl: "./google.component.html", templateUrl: "./google.component.html",
styleUrls: ["./google.component.scss", "../config-dialog.scss"], styleUrls: ["./google.component.scss", "../config-dialog.scss"],
}) })
export class AdminNebGoogleConfigComponent implements ModalComponent<NebBotConfigurationDialogContext>, OnInit { export class AdminNebGoogleConfigComponent implements OnInit {
public isLoading = true; public isLoading = true;
public isUpdating = false; public isUpdating = false;
@ -24,13 +23,11 @@ export class AdminNebGoogleConfigComponent implements ModalComponent<NebBotConfi
public integration: FE_Integration; public integration: FE_Integration;
public neb: FE_NebConfiguration; public neb: FE_NebConfiguration;
constructor(public dialog: DialogRef<NebBotConfigurationDialogContext>, constructor(public modal: NgbActiveModal,
private adminNebApi: AdminNebApiService, private adminNebApi: AdminNebApiService,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.neb = dialog.context.neb;
this.integration = dialog.context.integration;
} }
public ngOnInit() { public ngOnInit() {
@ -47,7 +44,7 @@ export class AdminNebGoogleConfigComponent implements ModalComponent<NebBotConfi
this.isUpdating = true; this.isUpdating = true;
this.adminNebApi.setIntegrationConfiguration(this.neb.id, this.integration.type, this.config).then(() => { this.adminNebApi.setIntegrationConfiguration(this.neb.id, this.integration.type, this.config).then(() => {
this.translate.get('Configuration updated').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Configuration updated').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
this.isUpdating = false; this.isUpdating = false;
console.error(err); console.error(err);

View File

@ -1,25 +1,24 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'Guggy Configuration' | translate}}</h4>
<h4>{{'Guggy Configuration' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content" *ngIf="isLoading"> <div class="modal-body" *ngIf="isLoading">
<my-spinner></my-spinner> <my-spinner></my-spinner>
</div> </div>
<div class="dialog-content" *ngIf="!isLoading"> <div class="modal-body" *ngIf="!isLoading">
<label class="label-block"> <label class="label-block">
{{'Api Key' | translate}} {{'Api Key' | translate}}
<span class="text-muted ">{{'The API key for' | translate}} <a href="http://docs.guggy.com/" target="_blank">Guggy's API</a>.</span> <span class="text-muted ">{{'The API key for' | translate}} <a href="http://docs.guggy.com/" target="_blank">Guggy's API</a>.</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="your_api_key_here" placeholder="your_api_key_here"
[(ngModel)]="config.api_key" [disabled]="isUpdating"/> [(ngModel)]="config.api_key" [disabled]="isUpdating"/>
</label> </label>
</div> </div>
<div class="dialog-footer" *ngIf="!isLoading"> <div class="modal-footer">
<button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm"> <button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}} <i class="far fa-save"></i> {{'Save' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,11 +1,10 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { NebBotConfigurationDialogContext } from "../config-context";
import { AdminNebApiService } from "../../../../shared/services/admin/admin-neb-api.service"; import { AdminNebApiService } from "../../../../shared/services/admin/admin-neb-api.service";
import { FE_NebConfiguration } from "../../../../shared/models/admin-responses"; import { FE_NebConfiguration } from "../../../../shared/models/admin-responses";
import { FE_Integration } from "../../../../shared/models/integration"; import { FE_Integration } from "../../../../shared/models/integration";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
interface GuggyConfig { interface GuggyConfig {
api_key: string; api_key: string;
@ -15,7 +14,7 @@ interface GuggyConfig {
templateUrl: "./guggy.component.html", templateUrl: "./guggy.component.html",
styleUrls: ["./guggy.component.scss", "../config-dialog.scss"], styleUrls: ["./guggy.component.scss", "../config-dialog.scss"],
}) })
export class AdminNebGuggyConfigComponent implements ModalComponent<NebBotConfigurationDialogContext>, OnInit { export class AdminNebGuggyConfigComponent implements OnInit {
public isLoading = true; public isLoading = true;
public isUpdating = false; public isUpdating = false;
@ -23,13 +22,11 @@ export class AdminNebGuggyConfigComponent implements ModalComponent<NebBotConfig
public integration: FE_Integration; public integration: FE_Integration;
public neb: FE_NebConfiguration; public neb: FE_NebConfiguration;
constructor(public dialog: DialogRef<NebBotConfigurationDialogContext>, constructor(public modal: NgbActiveModal,
private adminNebApi: AdminNebApiService, private adminNebApi: AdminNebApiService,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.neb = dialog.context.neb;
this.integration = dialog.context.integration;
} }
public ngOnInit() { public ngOnInit() {
@ -46,7 +43,7 @@ export class AdminNebGuggyConfigComponent implements ModalComponent<NebBotConfig
this.isUpdating = true; this.isUpdating = true;
this.adminNebApi.setIntegrationConfiguration(this.neb.id, this.integration.type, this.config).then(() => { this.adminNebApi.setIntegrationConfiguration(this.neb.id, this.integration.type, this.config).then(() => {
this.translate.get('Configuration updated').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Configuration updated').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
this.isUpdating = false; this.isUpdating = false;
console.error(err); console.error(err);

View File

@ -1,32 +1,31 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'Imgur Configuration' | translate}}</h4>
<h4>{{'Imgur Configuration' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content" *ngIf="isLoading"> <div class="modal-body" *ngIf="isLoading">
<my-spinner></my-spinner> <my-spinner></my-spinner>
</div> </div>
<div class="dialog-content" *ngIf="!isLoading"> <div class="modal-body" *ngIf="!isLoading">
<label class="label-block"> <label class="label-block">
{{'Client ID' | translate}} {{'Client ID' | translate}}
<span class="text-muted ">{{'The client ID of your' | translate}} <a href="https://apidocs.imgur.com/" target="_blank">{{'Imgur Application' | translate}}</a>.</span> <span class="text-muted ">{{'The client ID of your' | translate}} <a href="https://apidocs.imgur.com/" target="_blank">{{'Imgur Application' | translate}}</a>.</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="your_client_id" placeholder="your_client_id"
[(ngModel)]="config.client_id" [disabled]="isUpdating"/> [(ngModel)]="config.client_id" [disabled]="isUpdating"/>
</label> </label>
<label class="label-block"> <label class="label-block">
{{'Client Secret' | translate}} {{'Client Secret' | translate}}
<span class="text-muted ">{{'The client secret of your' | translate}} <a href="https://apidocs.imgur.com/" target="_blank">{{'Imgur Application' | translate}}</a>.</span> <span class="text-muted ">{{'The client secret of your' | translate}} <a href="https://apidocs.imgur.com/" target="_blank">{{'Imgur Application' | translate}}</a>.</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="your_client_secret" placeholder="your_client_secret"
[(ngModel)]="config.client_secret" [disabled]="isUpdating"/> [(ngModel)]="config.client_secret" [disabled]="isUpdating"/>
</label> </label>
</div> </div>
<div class="dialog-footer" *ngIf="!isLoading"> <div class="modal-footer">
<button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm"> <button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}} <i class="far fa-save"></i> {{'Save' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,11 +1,10 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { NebBotConfigurationDialogContext } from "../config-context";
import { AdminNebApiService } from "../../../../shared/services/admin/admin-neb-api.service"; import { AdminNebApiService } from "../../../../shared/services/admin/admin-neb-api.service";
import { FE_NebConfiguration } from "../../../../shared/models/admin-responses"; import { FE_NebConfiguration } from "../../../../shared/models/admin-responses";
import { FE_Integration } from "../../../../shared/models/integration"; import { FE_Integration } from "../../../../shared/models/integration";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
interface ImgurConfig { interface ImgurConfig {
client_id: string; client_id: string;
@ -16,7 +15,7 @@ interface ImgurConfig {
templateUrl: "./imgur.component.html", templateUrl: "./imgur.component.html",
styleUrls: ["./imgur.component.scss", "../config-dialog.scss"], styleUrls: ["./imgur.component.scss", "../config-dialog.scss"],
}) })
export class AdminNebImgurConfigComponent implements ModalComponent<NebBotConfigurationDialogContext>, OnInit { export class AdminNebImgurConfigComponent implements OnInit {
public isLoading = true; public isLoading = true;
public isUpdating = false; public isUpdating = false;
@ -24,13 +23,11 @@ export class AdminNebImgurConfigComponent implements ModalComponent<NebBotConfig
public integration: FE_Integration; public integration: FE_Integration;
public neb: FE_NebConfiguration; public neb: FE_NebConfiguration;
constructor(public dialog: DialogRef<NebBotConfigurationDialogContext>, constructor(public modal: NgbActiveModal,
private adminNebApi: AdminNebApiService, private adminNebApi: AdminNebApiService,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.neb = dialog.context.neb;
this.integration = dialog.context.integration;
} }
public ngOnInit() { public ngOnInit() {
@ -47,7 +44,7 @@ export class AdminNebImgurConfigComponent implements ModalComponent<NebBotConfig
this.isUpdating = true; this.isUpdating = true;
this.adminNebApi.setIntegrationConfiguration(this.neb.id, this.integration.type, this.config).then(() => { this.adminNebApi.setIntegrationConfiguration(this.neb.id, this.integration.type, this.config).then(() => {
this.translate.get('Configuration updated').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Configuration updated').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
this.isUpdating = false; this.isUpdating = false;
console.error(err); console.error(err);

View File

@ -5,14 +5,13 @@ import { ActivatedRoute } from "@angular/router";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { FE_Integration } from "../../../shared/models/integration"; import { FE_Integration } from "../../../shared/models/integration";
import { NEB_HAS_CONFIG, NEB_IS_COMPLEX } from "../../../shared/models/neb"; import { NEB_HAS_CONFIG, NEB_IS_COMPLEX } from "../../../shared/models/neb";
import { ContainerContent, Modal, overlayConfigFactory } from "ngx-modialog";
import { AdminNebGiphyConfigComponent } from "../config/giphy/giphy.component"; import { AdminNebGiphyConfigComponent } from "../config/giphy/giphy.component";
import { NebBotConfigurationDialogContext } from "../config/config-context"; import { NebBotConfigurationDialogContext } from "../config/config-context";
import { AdminNebGuggyConfigComponent } from "../config/guggy/guggy.component"; import { AdminNebGuggyConfigComponent } from "../config/guggy/guggy.component";
import { AdminNebGoogleConfigComponent } from "../config/google/google.component"; import { AdminNebGoogleConfigComponent } from "../config/google/google.component";
import { AdminNebImgurConfigComponent } from "../config/imgur/imgur.component"; import { AdminNebImgurConfigComponent } from "../config/imgur/imgur.component";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({ @Component({
templateUrl: "./edit.component.html", templateUrl: "./edit.component.html",
@ -30,7 +29,7 @@ export class AdminEditNebComponent implements OnInit, OnDestroy {
constructor(private nebApi: AdminNebApiService, constructor(private nebApi: AdminNebApiService,
private route: ActivatedRoute, private route: ActivatedRoute,
private modal: Modal, private modal: NgbModal,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
@ -87,7 +86,7 @@ export class AdminEditNebComponent implements OnInit, OnDestroy {
} }
public editBot(bot: FE_Integration) { public editBot(bot: FE_Integration) {
let component: ContainerContent; let component;
if (bot.type === "giphy") component = AdminNebGiphyConfigComponent; if (bot.type === "giphy") component = AdminNebGiphyConfigComponent;
if (bot.type === "guggy") component = AdminNebGuggyConfigComponent; if (bot.type === "guggy") component = AdminNebGuggyConfigComponent;
@ -95,13 +94,13 @@ export class AdminEditNebComponent implements OnInit, OnDestroy {
if (bot.type === "imgur") component = AdminNebImgurConfigComponent; if (bot.type === "imgur") component = AdminNebImgurConfigComponent;
if (!component) throw new Error("No config component for " + bot.type); if (!component) throw new Error("No config component for " + bot.type);
this.modal.open(component, overlayConfigFactory({ const nebBotRef = this.modal.open(component, {
neb: this.nebConfig, backdrop: 'static',
integration: bot,
isBlocking: true,
size: 'lg', size: 'lg',
}, NebBotConfigurationDialogContext)); });
const nebBotInstance = nebBotRef.componentInstance as NebBotConfigurationDialogContext;
nebBotInstance.neb = this.nebConfig;
nebBotInstance.integration = bot;
} }
private loadNeb(nebId: number) { private loadNeb(nebId: number) {

View File

@ -9,8 +9,8 @@ import {
AdminNebAppserviceConfigComponent, AdminNebAppserviceConfigComponent,
AppserviceConfigDialogContext AppserviceConfigDialogContext
} from "./appservice-config/appservice-config.component"; } from "./appservice-config/appservice-config.component";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({ @Component({
templateUrl: "./neb.component.html", templateUrl: "./neb.component.html",
@ -31,7 +31,7 @@ export class AdminNebComponent {
private toaster: ToasterService, private toaster: ToasterService,
private router: Router, private router: Router,
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private modal: Modal, private modal: NgbModal,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
@ -77,12 +77,12 @@ export class AdminNebComponent {
} }
public showAppserviceConfig(neb: FE_NebConfiguration) { public showAppserviceConfig(neb: FE_NebConfiguration) {
this.modal.open(AdminNebAppserviceConfigComponent, overlayConfigFactory({ const selfhostedRef = this.modal.open(AdminNebAppserviceConfigComponent, {
neb: neb, backdrop: 'static',
isBlocking: true,
size: 'lg', size: 'lg',
}, AppserviceConfigDialogContext)); });
const selfhostedInstance = selfhostedRef.componentInstance as AppserviceConfigDialogContext;
selfhostedInstance.neb = neb;
} }
public getEnabledBotsString(neb: FE_NebConfiguration): string { public getEnabledBotsString(neb: FE_NebConfiguration): string {

View File

@ -1,19 +1,16 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">Sticker Pack: {{ pack.displayName }}</h4>
<h4>Sticker Pack: {{ pack.displayName }}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
<div class="sticker" *ngFor="let sticker of pack.stickers trackById"> <div class="sticker" *ngFor="let sticker of pack.stickers trackById">
<img [src]="getThumbnailUrl(sticker.thumbnail.mxc, 120, 120)" width="120" height="120"/> <img [src]="getThumbnailUrl(sticker.thumbnail.mxc, 120, 120)" width="120" height="120"/>
<div class="caption"> <div class="caption">
<span class="name">{{ sticker.name }}</span> <span class="name">{{ sticker.name }}</span>
<span class="description">{{ sticker.description }}</span> <span class="description">{{ sticker.description }}</span>
</div>
</div> </div>
</div> </div>
<div class="dialog-footer"> </div>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <div class="modal-footer">
<i class="far fa-times-circle"></i> {{'Close' | translate}} <button type="button" class="btn btn-outline-secondary" (click)="modal.close()"><i class="far fa-times-circle"></i> {{'Close' | translate}}</button>
</button>
</div>
</div> </div>

View File

@ -1,10 +1,9 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { FE_StickerPack } from "../../../shared/models/integration"; import { FE_StickerPack } from "../../../shared/models/integration";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { MediaService } from "../../../shared/services/media.service"; import { MediaService } from "../../../shared/services/media.service";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
export class StickerPackPreviewDialogContext extends BSModalContext { export class StickerPackPreviewMoadlInstance {
public pack: FE_StickerPack; public pack: FE_StickerPack;
} }
@ -12,12 +11,12 @@ export class StickerPackPreviewDialogContext extends BSModalContext {
templateUrl: "./preview.component.html", templateUrl: "./preview.component.html",
styleUrls: ["./preview.component.scss"], styleUrls: ["./preview.component.scss"],
}) })
export class AdminStickerPackPreviewComponent implements ModalComponent<StickerPackPreviewDialogContext> { export class AdminStickerPackPreviewComponent {
public pack: FE_StickerPack; pack: FE_StickerPack;
constructor(public modal: NgbActiveModal, private media: MediaService) {
constructor(public dialog: DialogRef<StickerPackPreviewDialogContext>, private media: MediaService) {
this.pack = dialog.context.pack;
} }
public getThumbnailUrl(mxc: string, width: number, height: number, method: "crop" | "scale" = "scale"): string { public getThumbnailUrl(mxc: string, width: number, height: number, method: "crop" | "scale" = "scale"): string {

View File

@ -2,8 +2,8 @@ import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { FE_StickerPack } from "../../shared/models/integration"; import { FE_StickerPack } from "../../shared/models/integration";
import { AdminStickersApiService } from "../../shared/services/admin/admin-stickers-api-service"; import { AdminStickersApiService } from "../../shared/services/admin/admin-stickers-api-service";
import { Modal, overlayConfigFactory } from "ngx-modialog"; import { AdminStickerPackPreviewComponent, StickerPackPreviewMoadlInstance } from "./preview/preview.component";
import { AdminStickerPackPreviewComponent, StickerPackPreviewDialogContext } from "./preview/preview.component"; import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
@Component({ @Component({
@ -20,7 +20,7 @@ export class AdminStickerPacksComponent implements OnInit {
constructor(private adminStickers: AdminStickersApiService, constructor(private adminStickers: AdminStickersApiService,
private toaster: ToasterService, private toaster: ToasterService,
private modal: Modal, private modal: NgbModal,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
} }
@ -50,12 +50,10 @@ export class AdminStickerPacksComponent implements OnInit {
} }
public previewStickers(pack: FE_StickerPack) { public previewStickers(pack: FE_StickerPack) {
this.modal.open(AdminStickerPackPreviewComponent, overlayConfigFactory({ const modalRef = this.modal.open(AdminStickerPackPreviewComponent, { size: 'lg' });
pack: pack, const previewInstance = modalRef.componentInstance as StickerPackPreviewMoadlInstance;
isBlocking: false, previewInstance.pack = pack;
size: 'lg',
}, StickerPackPreviewDialogContext));
} }
public startTelegramImport() { public startTelegramImport() {

View File

@ -4,12 +4,11 @@ import { AdminTermsApiService } from "../../../shared/services/admin/admin-terms
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic'; import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import ISO6391 from "iso-639-1"; import ISO6391 from "iso-639-1";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { import {
AdminTermsNewEditPublishDialogComponent, AdminTermsNewEditPublishDialogComponent,
AdminTermsNewEditPublishDialogContext
} from "./publish/publish.component"; } from "./publish/publish.component";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
interface ILanguage { interface ILanguage {
name: string, name: string,
@ -67,7 +66,7 @@ export class AdminNewEditTermsComponent implements OnInit {
private toaster: ToasterService, private toaster: ToasterService,
private router: Router, private router: Router,
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private modal: Modal, private modal: NgbModal,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
} }
@ -119,11 +118,11 @@ export class AdminNewEditTermsComponent implements OnInit {
url: `${window.location.origin}/widgets/terms/${this.shortcode}/en/draft`, url: `${window.location.origin}/widgets/terms/${this.shortcode}/en/draft`,
}); });
const termsEditRef = this.modal.open(AdminTermsNewEditPublishDialogComponent, {
this.modal.open(AdminTermsNewEditPublishDialogComponent, overlayConfigFactory({ backdrop: 'static',
isBlocking: true,
size: 'sm', size: 'sm',
}, AdminTermsNewEditPublishDialogContext)).result.then(async (val) => { });
termsEditRef.result.then(async (val) => {
if (!val) return; // closed without publish if (!val) return; // closed without publish
try { try {

View File

@ -1,20 +1,19 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'Publish policy' | translate}}</h4>
<h4>{{'Publish policy' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
<label class="label-block"> <label class="label-block">
{{'Version number' | translate}} {{'Version number' | translate}}
<span class="text-muted ">{{'The version number of this policy' | translate}}</span> <span class="text-muted ">{{'The version number of this policy' | translate}}</span>
<input type="text" class="form-control" placeholder="eg: 1.1.0" [(ngModel)]="version"/> <input type="text" class="form-control" placeholder="eg: 1.1.0" [(ngModel)]="version"/>
</label> </label>
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="publish()" title="close" class="btn btn-primary btn-sm"> <button type="button" (click)="publish()" title="close" class="btn btn-primary btn-sm">
<i class="fas fa-upload"></i> {{'Publish' | translate}} <i class="fas fa-upload"></i> {{'Publish' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="save" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}} <i class="far fa-times-circle"></i> {{'Close' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,21 +1,20 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
export class AdminTermsNewEditPublishDialogContext extends BSModalContext { export interface AdminTermsNewEditPublishDialogContext {
} }
@Component({ @Component({
templateUrl: "./publish.component.html", templateUrl: "./publish.component.html",
styleUrls: ["./publish.component.scss"], styleUrls: ["./publish.component.scss"],
}) })
export class AdminTermsNewEditPublishDialogComponent implements ModalComponent<AdminTermsNewEditPublishDialogContext> { export class AdminTermsNewEditPublishDialogComponent {
public version: string; public version: string;
constructor(public dialog: DialogRef<AdminTermsNewEditPublishDialogContext>, private toaster: ToasterService, public translate: TranslateService) { constructor(public modal: NgbActiveModal, private toaster: ToasterService, public translate: TranslateService) {
} }
public publish() { public publish() {
@ -23,6 +22,6 @@ export class AdminTermsNewEditPublishDialogComponent implements ModalComponent<A
this.translate.get('Please enter a version number').subscribe((res: string) => {this.toaster.pop("warning", res); }); this.translate.get('Please enter a version number').subscribe((res: string) => {this.toaster.pop("warning", res); });
return; return;
} }
this.dialog.close(this.version); this.modal.close(this.version);
} }
} }

View File

@ -1,22 +1,21 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'Etherpad Widget Configuration' | translate}}</h4>
<h4>{{'Etherpad Widget Configuration' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
<label class="label-block"> <label class="label-block">
{{'Default Pad URL Template' | translate}} {{'Default Pad URL Template' | translate}}
<span class="text-muted ">{{'$padName and $roomId will be replaced during creation to help create a unique pad URL.' | translate}}</span> <span class="text-muted ">{{'$padName and $roomId will be replaced during creation to help create a unique pad URL.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="https://scalar.vector.im/etherpad/p/$padName_$roomId" placeholder="https://scalar.vector.im/etherpad/p/$padName_$roomId"
[(ngModel)]="widget.options.defaultUrl" [disabled]="isUpdating"/> [(ngModel)]="widget.options.defaultUrl" [disabled]="isUpdating"/>
</label> </label>
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm"> <button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}} <i class="far fa-save"></i> {{'Save' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,27 +1,29 @@
import { Component } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { FE_EtherpadWidget } from "../../../shared/models/integration"; import { FE_EtherpadWidget } from "../../../shared/models/integration";
import { ToasterService } from "angular2-toaster"; 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"; import { AdminIntegrationsApiService } from "../../../shared/services/admin/admin-integrations-api.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
@Component({ @Component({
templateUrl: "./etherpad.component.html", templateUrl: "./etherpad.component.html",
styleUrls: ["./etherpad.component.scss", "../config-dialog.scss"], styleUrls: ["./etherpad.component.scss", "../config-dialog.scss"],
}) })
export class AdminWidgetEtherpadConfigComponent implements ModalComponent<WidgetConfigDialogContext> { export class AdminWidgetEtherpadConfigComponent implements OnInit {
public isUpdating = false; public isUpdating = false;
public widget: FE_EtherpadWidget; public widget: FE_EtherpadWidget;
private originalWidget: FE_EtherpadWidget; private originalWidget = <FE_EtherpadWidget>{};
constructor(public dialog: DialogRef<WidgetConfigDialogContext>, constructor(public modal: NgbActiveModal,
private adminIntegrationsApi: AdminIntegrationsApiService, private adminIntegrationsApi: AdminIntegrationsApiService,
private toaster: ToasterService, private toaster: ToasterService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.originalWidget = dialog.context.widget; }
ngOnInit() {
this.originalWidget = this.widget;
this.widget = JSON.parse(JSON.stringify(this.originalWidget)); this.widget = JSON.parse(JSON.stringify(this.originalWidget));
} }
@ -30,7 +32,7 @@ export class AdminWidgetEtherpadConfigComponent implements ModalComponent<Widget
this.adminIntegrationsApi.setIntegrationOptions(this.widget.category, this.widget.type, this.widget.options).then(() => { this.adminIntegrationsApi.setIntegrationOptions(this.widget.category, this.widget.type, this.widget.options).then(() => {
this.originalWidget.options = this.widget.options; this.originalWidget.options = this.widget.options;
this.translate.get('Widget updated').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Widget updated').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
this.isUpdating = false; this.isUpdating = false;
console.error(err); console.error(err);

View File

@ -1,35 +1,34 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'Jitsi Widget Configuration' | translate}}</h4>
<h4>{{'Jitsi Widget Configuration' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
<label class="label-block"> <label class="label-block">
{{'Jitsi Domain' | translate}} {{'Jitsi Domain' | translate}}
<span class="text-muted ">{{'This is the domain that is used to host the conference.' | translate}}</span> <span class="text-muted ">{{'This is the domain that is used to host the conference.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="jitsi.riot.im" placeholder="jitsi.riot.im"
[(ngModel)]="widget.options.jitsiDomain" [disabled]="isUpdating"/> [(ngModel)]="widget.options.jitsiDomain" [disabled]="isUpdating"/>
</label> </label>
<label class="label-block"> <label class="label-block">
{{'Use this domain as the default conference domain' | translate}} {{'Use this domain as the default conference domain' | translate}}
<span class="text-muted ">{{'Some clients can create widgets that are not compatible with Dimension, making Dimension use jitsi.riot.im by default. By enabling this option, you\'ll force Dimension to use your Jitsi domain at risk of having clients not respect it.' | translate}}</span> <span class="text-muted ">{{'Some clients can create widgets that are not compatible with Dimension, making Dimension use jitsi.riot.im by default. By enabling this option, you\'ll force Dimension to use your Jitsi domain at risk of having clients not respect it.' | translate}}</span>
<ui-switch [checked]="widget.options.useDomainAsDefault" size="medium" [disabled]="isUpdating" <ui-switch [checked]="widget.options.useDomainAsDefault" size="medium" [disabled]="isUpdating"
(change)="toggleForcedJitsi()"></ui-switch> (change)="toggleForcedJitsi()"></ui-switch>
</label> </label>
<label class="label-block"> <label class="label-block">
{{'Jitsi Script URL' | translate}} {{'Jitsi Script URL' | translate}}
<span class="text-muted ">{{'This is used to create the Jitsi widget. It is normally at /libs/external_api.min.js from your domain.' | translate}}</span> <span class="text-muted ">{{'This is used to create the Jitsi widget. It is normally at /libs/external_api.min.js from your domain.' | translate}}</span>
<input type="text" class="form-control" <input type="text" class="form-control"
placeholder="https://jitsi.riot.im/libs/external_api.min.js" placeholder="https://jitsi.riot.im/libs/external_api.min.js"
[(ngModel)]="widget.options.scriptUrl" [disabled]="isUpdating"/> [(ngModel)]="widget.options.scriptUrl" [disabled]="isUpdating"/>
</label> </label>
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm"> <button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}} <i class="far fa-save"></i> {{'Save' | translate}}
</button> </button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}} <i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,24 +1,29 @@
import { Component } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { FE_JitsiWidget } from "../../../shared/models/integration"; import { FE_JitsiWidget } from "../../../shared/models/integration";
import { ToasterService } from "angular2-toaster"; 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"; import { AdminIntegrationsApiService } from "../../../shared/services/admin/admin-integrations-api.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
@Component({ @Component({
templateUrl: "./jitsi.component.html", templateUrl: "./jitsi.component.html",
styleUrls: ["./jitsi.component.scss", "../config-dialog.scss"], styleUrls: ["./jitsi.component.scss", "../config-dialog.scss"],
}) })
export class AdminWidgetJitsiConfigComponent implements ModalComponent<WidgetConfigDialogContext> { export class AdminWidgetJitsiConfigComponent implements OnInit {
public isUpdating = false; public isUpdating = false;
public widget: FE_JitsiWidget; public widget: FE_JitsiWidget;
private originalWidget: FE_JitsiWidget; private originalWidget = <FE_JitsiWidget>{};
constructor(public dialog: DialogRef<WidgetConfigDialogContext>, private adminIntegrationsApi: AdminIntegrationsApiService, private toaster: ToasterService, public translate: TranslateService) { constructor(public modal: NgbActiveModal,
this.translate = translate; private adminIntegrationsApi: AdminIntegrationsApiService,
this.originalWidget = dialog.context.widget; private toaster: ToasterService,
public translate: TranslateService) {
this.translate = translate;
}
ngOnInit() {
this.originalWidget = this.widget;
this.widget = JSON.parse(JSON.stringify(this.originalWidget)); this.widget = JSON.parse(JSON.stringify(this.originalWidget));
// Fix the ui-switch not picking up a boolean value // Fix the ui-switch not picking up a boolean value
@ -30,7 +35,7 @@ export class AdminWidgetJitsiConfigComponent implements ModalComponent<WidgetCon
this.adminIntegrationsApi.setIntegrationOptions(this.widget.category, this.widget.type, this.widget.options).then(() => { this.adminIntegrationsApi.setIntegrationOptions(this.widget.category, this.widget.type, this.widget.options).then(() => {
this.originalWidget.options = this.widget.options; this.originalWidget.options = this.widget.options;
this.translate.get('Widget updated').subscribe((res: string) => {this.toaster.pop("success", res); }); this.translate.get('Widget updated').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
this.isUpdating = false; this.isUpdating = false;
console.error(err); console.error(err);

View File

@ -1,22 +1,20 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">Whiteboard Widget Configuration</h4>
<h4>Whiteboard Widget Configuration</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div>
<div class="dialog-content">
<label class="label-block">
Default Board URL Template
<span class="text-muted ">$boardName and $roomId will be replaced during creation to help create a unique pad URL.</span>
<input type="text" class="form-control"
placeholder="https://cloud13.de/testwhiteboard/?whiteboardid=$roomId_$boardName"
[(ngModel)]="widget.options.defaultUrl" [disabled]="isUpdating"/>
</label>
</div>
<div class="dialog-footer">
<button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> Save
</button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> Cancel
</button>
</div>
</div> </div>
<div class="modal-body">
Default Board URL Template
<span class="text-muted ">$boardName and $roomId will be replaced during creation to help create a unique pad URL.</span>
<input type="text" class="form-control"
placeholder="https://cloud13.de/testwhiteboard/?whiteboardid=$roomId_$boardName"
[(ngModel)]="widget.options.defaultUrl" [disabled]="isUpdating"/>
</label>
</div>
<div class="modal-footer">
<button type="button" (click)="save()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}}
</button>
<button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
</div>

View File

@ -1,22 +1,29 @@
import { Component } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { FE_WhiteBoardWidget } from "../../../shared/models/integration"; import { FE_WhiteBoardWidget } from "../../../shared/models/integration";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog"; import { TranslateService } from "@ngx-translate/core";
import { WidgetConfigDialogContext } from "../widgets.component"; import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { AdminIntegrationsApiService } from "../../../shared/services/admin/admin-integrations-api.service"; import { AdminIntegrationsApiService } from "../../../shared/services/admin/admin-integrations-api.service";
@Component({ @Component({
templateUrl: "./whiteboard.component.html", templateUrl: "./whiteboard.component.html",
styleUrls: ["./whiteboard.component.scss", "../config-dialog.scss"], styleUrls: ["./whiteboard.component.scss", "../config-dialog.scss"],
}) })
export class AdminWidgetWhiteboardConfigComponent implements ModalComponent<WidgetConfigDialogContext> { export class AdminWidgetWhiteboardConfigComponent implements OnInit {
public isUpdating = false; public isUpdating = false;
public widget: FE_WhiteBoardWidget; public widget: FE_WhiteBoardWidget;
private originalWidget: FE_WhiteBoardWidget; private originalWidget: FE_WhiteBoardWidget;
constructor(public dialog: DialogRef<WidgetConfigDialogContext>, private adminIntegrationsApi: AdminIntegrationsApiService, private toaster: ToasterService) { constructor(public modal: NgbActiveModal,
this.originalWidget = dialog.context.widget; private adminIntegrationsApi: AdminIntegrationsApiService,
private toaster: ToasterService,
public translate: TranslateService) {
this.translate = translate;
}
ngOnInit() {
this.originalWidget = this.widget;
this.widget = JSON.parse(JSON.stringify(this.originalWidget)); this.widget = JSON.parse(JSON.stringify(this.originalWidget));
} }
@ -25,7 +32,7 @@ export class AdminWidgetWhiteboardConfigComponent implements ModalComponent<Widg
this.adminIntegrationsApi.setIntegrationOptions(this.widget.category, this.widget.type, this.widget.options).then(() => { this.adminIntegrationsApi.setIntegrationOptions(this.widget.category, this.widget.type, this.widget.options).then(() => {
this.originalWidget.options = this.widget.options; this.originalWidget.options = this.widget.options;
this.toaster.pop("success", "Widget updated"); this.toaster.pop("success", "Widget updated");
this.dialog.close(); this.modal.close();
}).catch(err => { }).catch(err => {
this.isUpdating = false; this.isUpdating = false;
console.error(err); console.error(err);

View File

@ -2,15 +2,14 @@ import { Component } from "@angular/core";
import { FE_Widget } from "../../shared/models/integration"; import { FE_Widget } from "../../shared/models/integration";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { AdminWidgetEtherpadConfigComponent } from "./etherpad/etherpad.component"; import { AdminWidgetEtherpadConfigComponent } from "./etherpad/etherpad.component";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { AdminWidgetJitsiConfigComponent } from "./jitsi/jitsi.component"; import { AdminWidgetJitsiConfigComponent } from "./jitsi/jitsi.component";
import { AdminIntegrationsApiService } from "../../shared/services/admin/admin-integrations-api.service"; import { AdminIntegrationsApiService } from "../../shared/services/admin/admin-integrations-api.service";
import { AdminWidgetWhiteboardConfigComponent } from "./whiteboard/whiteboard.component"; import { AdminWidgetWhiteboardConfigComponent } from "./whiteboard/whiteboard.component";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
export class WidgetConfigDialogContext extends BSModalContext { export interface WidgetConfigDialogContext {
public widget: FE_Widget; widget: FE_Widget;
} }
@Component({ @Component({
@ -23,7 +22,7 @@ export class AdminWidgetsComponent {
public isUpdating = false; public isUpdating = false;
public widgets: FE_Widget[]; public widgets: FE_Widget[];
constructor(private adminIntegrationsApi: AdminIntegrationsApiService, private toaster: ToasterService, private modal: Modal, public translate: TranslateService) { constructor(private adminIntegrationsApi: AdminIntegrationsApiService, private toaster: ToasterService, private modal: NgbModal, public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.adminIntegrationsApi.getAllWidgets().then(widgets => { this.adminIntegrationsApi.getAllWidgets().then(widgets => {
this.isLoading = false; this.isLoading = false;
@ -61,12 +60,11 @@ export class AdminWidgetsComponent {
return; return;
} }
this.modal.open(component, overlayConfigFactory({ const widgetConfigRef = this.modal.open(component, {
widget: widget, backdrop: 'static'
});
isBlocking: true, const widgetConfigInterface = widgetConfigRef.componentInstance as WidgetConfigDialogContext;
size: 'lg', widgetConfigInterface.widget = widget;
}, WidgetConfigDialogContext));
} }
public hasConfiguration(widget: FE_Widget) { public hasConfiguration(widget: FE_Widget) {

View File

@ -1,6 +1,4 @@
import { ApplicationRef, Injector, NgModule } from "@angular/core"; import { ApplicationRef, Injector, NgModule } from "@angular/core";
import { ModalModule } from "ngx-modialog";
import { BootstrapModalModule } from "ngx-modialog/plugins/bootstrap";
import { BreadcrumbModule } from "xng-breadcrumb"; import { BreadcrumbModule } from "xng-breadcrumb";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { UiSwitchModule } from "ngx-ui-switch"; import { UiSwitchModule } from "ngx-ui-switch";
@ -137,9 +135,7 @@ export function HttpLoaderFactory(http: HttpClient) {
UiSwitchModule, UiSwitchModule,
ToasterModule, ToasterModule,
BrowserAnimationsModule, BrowserAnimationsModule,
ModalModule.forRoot(), BreadcrumbModule,
BootstrapModalModule,
BreadcrumbsModule,
CKEditorModule, CKEditorModule,
FontAwesomeModule, FontAwesomeModule,
TranslateModule.forRoot({ TranslateModule.forRoot({

View File

@ -1,17 +1,15 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'Telegram chat is already bridged' | translate}}</h4>
<h4>{{'Telegram chat is already bridged' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
{{'You have the appropriate permissions to be able to unbridge the chat, however. Would you like to unbridge the other room and instead bridge it here?' | translate}} {{'You have the appropriate permissions to be able to unbridge the chat, however. Would you like to unbridge the other room and instead bridge it here?' | translate}}
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="unbridgeAndContinue()" title="unbridge and continue" <button type="button" (click)="unbridgeAndContinue()" title="unbridge and continue" class="btn btn-danger btn-sm">
class="btn btn-danger btn-sm"> {{'Unbridge and continue' | translate}}
{{'Unbridge and continue' | translate}} </button>
</button> <button type="button" (click)="cancel()" title="cancel" class="btn btn-primary btn-sm">
<button type="button" (click)="cancel()" title="cancel" class="btn btn-primary btn-sm"> <i class="far fa-times-circle"></i> {{'No, don\'t bridge' | translate}}
<i class="far fa-times-circle"></i> {{'No, don\'t bridge' | translate}} </button>
</button>
</div>
</div> </div>

View File

@ -1,24 +1,23 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { DialogRef, ModalComponent } from "ngx-modialog"; import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
export class AskUnbridgeDialogContext extends BSModalContext { export interface AskUnbridgeDialogContext {
} }
@Component({ @Component({
templateUrl: "./ask-unbridge.component.html", templateUrl: "./ask-unbridge.component.html",
styleUrls: ["./ask-unbridge.component.scss"], styleUrls: ["./ask-unbridge.component.scss"],
}) })
export class TelegramAskUnbridgeComponent implements ModalComponent<AskUnbridgeDialogContext> { export class TelegramAskUnbridgeComponent {
constructor(public dialog: DialogRef<AskUnbridgeDialogContext>) { constructor(public modal: NgbActiveModal) {
} }
public unbridgeAndContinue(): void { public unbridgeAndContinue(): void {
this.dialog.close({unbridge: true}); this.modal.close({unbridge: true});
} }
public cancel(): void { public cancel(): void {
this.dialog.close({unbridge: false}); this.modal.close({unbridge: false});
} }
} }

View File

@ -1,13 +1,12 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{'Telegram chat is already bridged' | translate}}</h4>
<h4>{{'Telegram chat is already bridged' | translate}}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content"> <div class="modal-body">
{{'That Telegram chat is bridged to another Matrix room and cannot be bridged here. Unfortunately, you do not have the required permissions to be able to unbridge the other room.' | translate}} {{'That Telegram chat is bridged to another Matrix room and cannot be bridged here. Unfortunately, you do not have the required permissions to be able to unbridge the other room.' | translate}}
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="dialog.close()" title="cancel" class="btn btn-primary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-primary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}} <i class="far fa-times-circle"></i> {{'Close' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,16 +1,15 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { DialogRef, ModalComponent } from "ngx-modialog"; import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
export class CannotUnbridgeDialogContext extends BSModalContext { export interface CannotUnbridgeDialogContext {
} }
@Component({ @Component({
templateUrl: "./cannot-unbridge.component.html", templateUrl: "./cannot-unbridge.component.html",
styleUrls: ["./cannot-unbridge.component.scss"], styleUrls: ["./cannot-unbridge.component.scss"],
}) })
export class TelegramCannotUnbridgeComponent implements ModalComponent<CannotUnbridgeDialogContext> { export class TelegramCannotUnbridgeComponent {
constructor(public dialog: DialogRef<CannotUnbridgeDialogContext>) { constructor(public modal: NgbActiveModal) {
} }
} }

View File

@ -2,13 +2,12 @@ import { Component } from "@angular/core";
import { BridgeComponent } from "../bridge.component"; import { BridgeComponent } from "../bridge.component";
import { TelegramApiService } from "../../../shared/services/integrations/telegram-api.service"; import { TelegramApiService } from "../../../shared/services/integrations/telegram-api.service";
import { FE_PortalInfo } from "../../../shared/models/telegram"; import { FE_PortalInfo } from "../../../shared/models/telegram";
import { Modal, overlayConfigFactory } from "ngx-modialog"; import { TelegramAskUnbridgeComponent } from "./ask-unbridge/ask-unbridge.component";
import { AskUnbridgeDialogContext, TelegramAskUnbridgeComponent } from "./ask-unbridge/ask-unbridge.component";
import { import {
CannotUnbridgeDialogContext,
TelegramCannotUnbridgeComponent TelegramCannotUnbridgeComponent
} from "./cannot-unbridge/cannot-unbridge.component"; } from "./cannot-unbridge/cannot-unbridge.component";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
interface TelegramConfig { interface TelegramConfig {
puppet: { puppet: {
@ -40,7 +39,7 @@ export class TelegramBridgeConfigComponent extends BridgeComponent<TelegramConfi
public isUpdating: boolean; public isUpdating: boolean;
constructor(private telegram: TelegramApiService, private modal: Modal, public translate: TranslateService) { constructor(private telegram: TelegramApiService, private modal: NgbModal, public translate: TranslateService) {
super("telegram", translate); super("telegram", translate);
this.translate = translate; this.translate = translate;
} }
@ -80,21 +79,22 @@ export class TelegramBridgeConfigComponent extends BridgeComponent<TelegramConfi
this.telegram.getPortalInfo(this.bridge.config.portalInfo.chatId, this.roomId).then(async (chatInfo) => { this.telegram.getPortalInfo(this.bridge.config.portalInfo.chatId, this.roomId).then(async (chatInfo) => {
let forceUnbridge = false; let forceUnbridge = false;
if (chatInfo.bridged && chatInfo.canUnbridge) { if (chatInfo.bridged && chatInfo.canUnbridge) {
const response = await this.modal.open(TelegramAskUnbridgeComponent, overlayConfigFactory({ const askUnbridgeRef = this.modal.open(TelegramAskUnbridgeComponent, {
isBlocking: true, backdrop: 'static',
size: 'lg', size: 'lg',
}, AskUnbridgeDialogContext)).result; });
askUnbridgeRef.result.then((response) => {
if (response.unbridge) { if (response.unbridge) {
forceUnbridge = true; forceUnbridge = true;
} else { } else {
return {aborted: true}; return {aborted: true};
} }
});
} else if (chatInfo.bridged) { } else if (chatInfo.bridged) {
this.modal.open(TelegramCannotUnbridgeComponent, overlayConfigFactory({ this.modal.open(TelegramCannotUnbridgeComponent, {
isBlocking: true, backdrop: 'static',
size: 'lg', size: 'lg',
}, CannotUnbridgeDialogContext)); });
return {aborted: true}; return {aborted: true};
} }

View File

@ -1,14 +1,13 @@
<div class="dialog"> <div class="modal-header">
<div class="dialog-header"> <h4 class="modal-title" id="modal-basic-title">{{ bot.displayName }}</h4>
<h4>{{ bot.displayName }}</h4> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div> </div>
<div class="dialog-content" style="text-align: center;"> <div class="modal-body">
<p>{{ bot.description }}</p> <p>{{ bot.description }}</p>
<ui-switch [checked]="bot._inRoom" [disabled]="bot._isUpdating" (change)="toggle()"></ui-switch> <ui-switch [checked]="bot._inRoom" [disabled]="bot._isUpdating" (change)="toggle()"></ui-switch>
</div> </div>
<div class="dialog-footer"> <div class="modal-footer">
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm"> <button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}} <i class="far fa-times-circle"></i> {{'Close' | translate}}
</button> </button>
</div>
</div> </div>

View File

@ -1,36 +1,35 @@
import { Component } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { FE_SimpleBot } from "../../shared/models/integration"; import { FE_SimpleBot } from "../../shared/models/integration";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { ScalarClientApiService } from "../../shared/services/scalar/scalar-client-api.service"; import { ScalarClientApiService } from "../../shared/services/scalar/scalar-client-api.service";
import { IntegrationsApiService } from "../../shared/services/integrations/integrations-api.service"; import { IntegrationsApiService } from "../../shared/services/integrations/integrations-api.service";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { DialogRef } from "ngx-modialog";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
export class SimpleBotConfigDialogContext extends BSModalContext { export interface SimpleBotConfigDialogContext {
public bot: FE_SimpleBot; bot: FE_SimpleBot;
public roomId: string; roomId: string;
} }
@Component({ @Component({
templateUrl: "simple-bot.component.html", templateUrl: "simple-bot.component.html",
styleUrls: ["simple-bot.component.scss"], styleUrls: ["simple-bot.component.scss"],
}) })
export class ConfigSimpleBotComponent { export class ConfigSimpleBotComponent implements OnInit {
public bot: FE_SimpleBot; public bot: FE_SimpleBot;
private roomId: string; private roomId: string;
constructor(public dialog: DialogRef<SimpleBotConfigDialogContext>, constructor(public modal: NgbActiveModal,
private toaster: ToasterService, private toaster: ToasterService,
private scalar: ScalarClientApiService, private scalar: ScalarClientApiService,
private integrationsApi: IntegrationsApiService, private integrationsApi: IntegrationsApiService,
public translate: TranslateService) { public translate: TranslateService) {
this.translate = translate; this.translate = translate;
this.bot = dialog.context.bot; }
this.roomId = dialog.context.roomId;
ngOnInit() {
this.bot._isUpdating = false; this.bot._isUpdating = false;
} }

View File

@ -3,322 +3,481 @@ import { ActivatedRoute, Router } from "@angular/router";
import { ScalarClientApiService } from "../../shared/services/scalar/scalar-client-api.service"; import { ScalarClientApiService } from "../../shared/services/scalar/scalar-client-api.service";
import * as _ from "lodash"; import * as _ from "lodash";
import { ScalarServerApiService } from "../../shared/services/scalar/scalar-server-api.service"; import { ScalarServerApiService } from "../../shared/services/scalar/scalar-server-api.service";
import { FE_Integration, FE_IntegrationRequirement, FE_SimpleBot } from "../../shared/models/integration"; import {
FE_Integration,
FE_IntegrationRequirement,
FE_SimpleBot,
} from "../../shared/models/integration";
import { IntegrationsRegistry } from "../../shared/registry/integrations.registry"; import { IntegrationsRegistry } from "../../shared/registry/integrations.registry";
import { SessionStorage } from "../../shared/SessionStorage"; import { SessionStorage } from "../../shared/SessionStorage";
import { AdminApiService } from "../../shared/services/admin/admin-api.service"; import { AdminApiService } from "../../shared/services/admin/admin-api.service";
import { IntegrationsApiService } from "../../shared/services/integrations/integrations-api.service"; import { IntegrationsApiService } from "../../shared/services/integrations/integrations-api.service";
import { Modal, overlayConfigFactory } from "ngx-modialog"; import {
import { ConfigSimpleBotComponent, SimpleBotConfigDialogContext } from "../../configs/simple-bot/simple-bot.component"; ConfigSimpleBotComponent,
SimpleBotConfigDialogContext,
} from "../../configs/simple-bot/simple-bot.component";
import { ToasterService } from "angular2-toaster"; import { ToasterService } from "angular2-toaster";
import { StickerApiService } from "../../shared/services/integrations/sticker-api.service"; import { StickerApiService } from "../../shared/services/integrations/sticker-api.service";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
const CATEGORY_MAP = { const CATEGORY_MAP = {
"Widgets": ["widget"], Widgets: ["widget"],
"Bots": ["complex-bot", "bot"], Bots: ["complex-bot", "bot"],
"Bridges": ["bridge"], Bridges: ["bridge"],
}; };
@Component({ @Component({
selector: "my-riot-home", selector: "my-riot-home",
templateUrl: "./home.component.html", templateUrl: "./home.component.html",
styleUrls: ["./home.component.scss"], styleUrls: ["./home.component.scss"],
}) })
export class RiotHomeComponent { export class RiotHomeComponent {
public isLoading = true; public isLoading = true;
public isError = false; public isError = false;
public errorMessage: string; public errorMessage: string;
public isRoomEncrypted: boolean; public isRoomEncrypted: boolean;
public hasStickerPacks = false; public hasStickerPacks = false;
private roomId: string; private roomId: string;
private userId: string; private userId: string;
private requestedScreen: string = null; private requestedScreen: string = null;
private requestedIntegrationId: string = null; private requestedIntegrationId: string = null;
public integrationsForCategory: { [category: string]: FE_Integration[] } = {}; public integrationsForCategory: { [category: string]: FE_Integration[] } = {};
private categoryMap: { [categoryName: string]: string[] } = CATEGORY_MAP; private categoryMap: { [categoryName: string]: string[] } = CATEGORY_MAP;
constructor(private activatedRoute: ActivatedRoute, constructor(
private scalarApi: ScalarServerApiService, private activatedRoute: ActivatedRoute,
private scalar: ScalarClientApiService, private scalarApi: ScalarServerApiService,
private integrationsApi: IntegrationsApiService, private scalar: ScalarClientApiService,
private stickerApi: StickerApiService, private integrationsApi: IntegrationsApiService,
private adminApi: AdminApiService, private stickerApi: StickerApiService,
private router: Router, private adminApi: AdminApiService,
private modal: Modal, private router: Router,
private toaster: ToasterService, private modal: NgbModal,
public translate: TranslateService) { private toaster: ToasterService,
this.translate = translate; public translate: TranslateService
let params: any = this.activatedRoute.snapshot.queryParams; ) {
this.translate = translate;
let params: any = this.activatedRoute.snapshot.queryParams;
this.requestedScreen = params.screen; this.requestedScreen = params.screen;
this.requestedIntegrationId = params.integ_id; this.requestedIntegrationId = params.integ_id;
if (SessionStorage.roomId && SessionStorage.userId) { if (SessionStorage.roomId && SessionStorage.userId) {
this.roomId = SessionStorage.roomId; this.roomId = SessionStorage.roomId;
this.userId = SessionStorage.userId; this.userId = SessionStorage.userId;
console.log("Already checked scalar token and other params - continuing startup"); console.log(
"Already checked scalar token and other params - continuing startup"
);
this.prepareIntegrations();
return;
}
if (!params.scalar_token || !params.room_id) {
console.error(
"Unable to load Dimension. Missing room ID or scalar token."
);
this.isError = true;
this.isLoading = false;
this.translate
.get("Unable to load Dimension - missing room ID or token.")
.subscribe((res: string) => {
this.errorMessage = res;
});
} else {
this.roomId = params.room_id;
SessionStorage.scalarToken = params.scalar_token;
SessionStorage.roomId = this.roomId;
this.scalarApi
.getAccount()
.then((response) => {
const userId = response.user_id;
SessionStorage.userId = userId;
if (!userId) {
console.error(
"No user returned for token. Is the token registered in Dimension?"
);
this.isError = true;
this.isLoading = false;
this.translate
.get(
"Could not verify your token. Please try logging out of Element and back in. Be sure to back up your encryption keys!"
)
.subscribe((res: string) => {
this.errorMessage = res;
});
} else {
this.userId = userId;
console.log("Scalar token belongs to " + userId);
this.checkAdmin();
this.prepareIntegrations(); this.prepareIntegrations();
return; }
} })
.catch((err) => {
if (!params.scalar_token || !params.room_id) { console.error(err);
console.error("Unable to load Dimension. Missing room ID or scalar token."); this.isError = true;
this.isError = true; this.isLoading = false;
this.isLoading = false; this.translate
this.translate.get('Unable to load Dimension - missing room ID or token.').subscribe((res: string) => {this.errorMessage = res}); .get(
} else { "Unable to communicate with Dimension due to an unknown error."
this.roomId = params.room_id; )
SessionStorage.scalarToken = params.scalar_token; .subscribe((res: string) => {
SessionStorage.roomId = this.roomId; this.errorMessage = res;
this.scalarApi.getAccount().then(response => {
const userId = response.user_id;
SessionStorage.userId = userId;
if (!userId) {
console.error("No user returned for token. Is the token registered in Dimension?");
this.isError = true;
this.isLoading = false;
this.translate.get('Could not verify your token. Please try logging out of Element and back in. Be sure to back up your encryption keys!').subscribe((res: string) => {this.errorMessage = res});
} else {
this.userId = userId;
console.log("Scalar token belongs to " + userId);
this.checkAdmin();
this.prepareIntegrations();
}
}).catch(err => {
console.error(err);
this.isError = true;
this.isLoading = false;
this.translate.get('Unable to communicate with Dimension due to an unknown error.').subscribe((res: string) => {this.errorMessage = res});
}); });
} });
}
}
private checkAdmin() {
this.adminApi
.isAdmin()
.then(() => {
console.log(
SessionStorage.userId + " is an admin for this Dimension instance"
);
SessionStorage.isAdmin = true;
})
.catch(() => (SessionStorage.isAdmin = false));
}
public hasIntegrations(): boolean {
for (const category of this.getCategories()) {
if (this.getIntegrationsIn(category).length > 0) return true;
} }
private checkAdmin() { return false;
this.adminApi.isAdmin().then(() => { }
console.log(SessionStorage.userId + " is an admin for this Dimension instance");
SessionStorage.isAdmin = true; public getCategories(): string[] {
}).catch(() => SessionStorage.isAdmin = false); return Object.keys(this.categoryMap);
}
public getIntegrationsIn(category: string): FE_Integration[] {
return this.integrationsForCategory[category];
}
private getIntegrations(): FE_Integration[] {
const result: FE_Integration[] = [];
for (const category of this.getCategories()) {
for (const integration of this.getIntegrationsIn(category)) {
result.push(integration);
}
} }
public hasIntegrations(): boolean { return result;
for (const category of this.getCategories()) { }
if (this.getIntegrationsIn(category).length > 0) return true;
}
return false; public modifyIntegration(integration: FE_Integration) {
if (!integration._isSupported) {
console.log(
this.userId +
" tried to modify " +
integration.displayName +
" with error: " +
integration._notSupportedReason
);
this.translate
.get(
"You do not appear to have permission to modify widgets in this room"
)
.subscribe((res: string) => {
const reason =
integration.category === "widget"
? res
: integration._notSupportedReason;
this.toaster.pop("error", reason);
});
return;
} }
public getCategories(): string[] { SessionStorage.editIntegration = integration;
return Object.keys(this.categoryMap); SessionStorage.editsRequested++;
console.log(
this.userId + " is trying to modify " + integration.displayName
);
if (integration.category === "bot") {
const widgetConfigRef = this.modal.open(ConfigSimpleBotComponent, {
backdrop: "static",
size: "lg",
});
const widgetConfigInterface =
widgetConfigRef.componentInstance as SimpleBotConfigDialogContext;
widgetConfigInterface.bot = <FE_SimpleBot>integration;
widgetConfigInterface.roomId = this.roomId;
} else {
console.log(
"Navigating to edit screen for " +
integration.category +
" " +
integration.type
);
this.router.navigate(
["riot-app", integration.category, integration.type],
{ queryParams: { roomId: this.roomId } }
);
} }
}
public getIntegrationsIn(category: string): FE_Integration[] { private prepareIntegrations() {
return this.integrationsForCategory[category]; this.scalar
} .isRoomEncrypted(this.roomId)
.then((payload) => {
this.isRoomEncrypted = payload.response;
return this.integrationsApi.getIntegrations(this.roomId);
})
.then((response) => {
const integrations: FE_Integration[] = _.flatten(
Object.keys(response).map((k) => response[k])
);
const supportedIntegrations: FE_Integration[] = _.filter(
integrations,
(i) => IntegrationsRegistry.isSupported(i)
);
private getIntegrations(): FE_Integration[] { // Flag integrations that aren't supported in encrypted rooms
const result: FE_Integration[] = []; if (this.isRoomEncrypted) {
for (const integration of supportedIntegrations) {
for (const category of this.getCategories()) { if (!integration.isEncryptionSupported) {
for (const integration of this.getIntegrationsIn(category)) { integration._isSupported = false;
result.push(integration); integration._notSupportedReason =
"This integration is not supported in encrypted rooms";
} }
}
} }
return result; // 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.category) !== -1
);
}
let promises = supportedIntegrations.map((i) =>
this.updateIntegrationState(i)
);
return Promise.all(promises);
})
.then(() => {
this.isLoading = false;
// HACK: We wait for the digest cycle so we actually have components to look at
setTimeout(() => this.tryOpenConfigScreen(), 20);
})
.catch((err) => {
console.error(err);
this.isError = true;
this.isLoading = false;
this.translate
.get(
"Unable to set up Dimension. This version of Element may not supported or there may be a problem with the server."
)
.subscribe((res: string) => {
this.errorMessage = res;
});
});
this.stickerApi
.getPacks()
.then((packs) => {
this.hasStickerPacks = packs.length > 0;
})
.catch((err) => {
console.error(err);
});
}
private tryOpenConfigScreen() {
let category = null;
let type = null;
if (!this.requestedScreen) return;
if (this.requestedScreen === "type_m.stickerpicker") {
console.log(
"Intercepting config screen handling to open sticker picker config"
);
this.router.navigate(["riot-app", "stickerpicker"]);
return;
} }
public modifyIntegration(integration: FE_Integration) { const targetIntegration = IntegrationsRegistry.getIntegrationForScreen(
if (!integration._isSupported) { this.requestedScreen
console.log(this.userId + " tried to modify " + integration.displayName + " with error: " + integration._notSupportedReason); );
this.translate.get('You do not appear to have permission to modify widgets in this room').subscribe((res: string) => { if (targetIntegration) {
const reason = integration.category === "widget" ? res : integration._notSupportedReason; category = targetIntegration.category;
this.toaster.pop("error", reason); type = targetIntegration.type;
}); } else {
return; console.log("Unknown screen requested: " + this.requestedScreen);
} }
console.log("Searching for integration for requested screen");
for (const integration of this.getIntegrations()) {
if (integration.category === category && integration.type === type) {
console.log(
"Configuring integration " +
this.requestedIntegrationId +
" category=" +
category +
" type=" +
type
);
SessionStorage.editIntegration = integration; SessionStorage.editIntegration = integration;
SessionStorage.editsRequested++; SessionStorage.editIntegrationId = this.requestedIntegrationId;
console.log(this.userId + " is trying to modify " + integration.displayName); this.modifyIntegration(integration);
return;
if (integration.category === "bot") { }
this.modal.open(ConfigSimpleBotComponent, overlayConfigFactory({
bot: <FE_SimpleBot>integration,
roomId: this.roomId,
isBlocking: true,
size: 'lg',
}, SimpleBotConfigDialogContext));
} else {
console.log("Navigating to edit screen for " + integration.category + " " + integration.type);
this.router.navigate(['riot-app', integration.category, integration.type], {queryParams: {roomId: this.roomId}});
}
} }
private prepareIntegrations() { console.log(
this.scalar.isRoomEncrypted(this.roomId).then(payload => { "Failed to find integration component for category=" +
this.isRoomEncrypted = payload.response; category +
return this.integrationsApi.getIntegrations(this.roomId); " type=" +
}).then(response => { type
const integrations: FE_Integration[] = _.flatten(Object.keys(response).map(k => response[k])); );
const supportedIntegrations: FE_Integration[] = _.filter(integrations, i => IntegrationsRegistry.isSupported(i)); }
// Flag integrations that aren't supported in encrypted rooms private async updateIntegrationState(integration: FE_Integration) {
if (this.isRoomEncrypted) { if (!integration.isOnline) {
for (const integration of supportedIntegrations) { integration._isSupported = false;
if (!integration.isEncryptionSupported) { this.translate
integration._isSupported = false; .get("This integration is offline or unavailable")
integration._notSupportedReason = "This integration is not supported in encrypted rooms"; .subscribe((res: string) => {
} integration._notSupportedReason = res;
}
}
// 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.category) !== -1);
}
let promises = supportedIntegrations.map(i => this.updateIntegrationState(i));
return Promise.all(promises);
}).then(() => {
this.isLoading = false;
// HACK: We wait for the digest cycle so we actually have components to look at
setTimeout(() => this.tryOpenConfigScreen(), 20);
}).catch(err => {
console.error(err);
this.isError = true;
this.isLoading = false;
this.translate.get('Unable to set up Dimension. This version of Element may not supported or there may be a problem with the server.').subscribe((res: string) => {this.errorMessage = res});
}); });
return;
}
this.stickerApi.getPacks().then(packs => { if (!integration.requirements) return;
this.hasStickerPacks = packs.length > 0;
}).catch(err => { let promises = integration.requirements.map((r) =>
console.error(err); this.checkRequirement(r)
);
if (integration.category === "bot") {
const state = await this.scalar.getMembershipState(
this.roomId,
(<FE_SimpleBot>integration).userId
);
if (state && state.response && state.response.membership) {
integration._inRoom =
["join", "invite"].indexOf(state.response.membership) !== -1;
} else integration._inRoom = false;
}
return Promise.all(promises).then(
() => {
integration._isSupported = true;
integration._notSupportedReason = null;
},
(error) => {
console.error(error);
integration._isSupported = false;
integration._notSupportedReason = error;
}
);
}
private checkRequirement(requirement: FE_IntegrationRequirement) {
switch (requirement.condition) {
case "publicRoom":
return this.scalar.getJoinRule(this.roomId).then((payload) => {
if (!payload.response) {
let message: string;
this.translate
.get("Could not communicate with Element")
.subscribe((res: string) => {
message = res;
});
return Promise.reject(message);
}
const isPublic = payload.response.join_rule === "public";
if (isPublic !== requirement.expectedValue) {
let message: string;
let message1: string;
this.translate
.get(["The room must be", "to use this integration"])
.subscribe((res: string) => {
message = res[0];
message1 = res[1];
});
return Promise.reject(
message + (isPublic ? "non-public" : "public") + message1
);
} else return Promise.resolve();
}); });
} case "canSendEventTypes":
const processPayload = (payload) => {
const response = <any>payload.response;
if (response === true) return Promise.resolve();
if (response.error || response.error.message) {
let message: string;
this.translate
.get("You cannot modify widgets in this room")
.subscribe((res: string) => {
message = res;
});
return Promise.reject(message);
}
let message: string;
this.translate
.get("Error communicating with Element")
.subscribe((res: string) => {
message = res;
});
return Promise.reject(message);
};
private tryOpenConfigScreen() { let promiseChain = Promise.resolve();
let category = null; requirement.argument.forEach(
let type = null; (e) =>
if (!this.requestedScreen) return; (promiseChain = promiseChain.then(() =>
this.scalar
if (this.requestedScreen === "type_m.stickerpicker") { .canSendEvent(this.roomId, e.type, e.isState)
console.log("Intercepting config screen handling to open sticker picker config"); .then(processPayload)
this.router.navigate(['riot-app', 'stickerpicker']); .catch(processPayload)
return; ))
} );
return promiseChain
const targetIntegration = IntegrationsRegistry.getIntegrationForScreen(this.requestedScreen); .then(() => {
if (targetIntegration) { if (!requirement.expectedValue) {
category = targetIntegration.category; let message: string;
type = targetIntegration.type; this.translate
} else { .get("Expected to not be able to send specific event types")
console.log("Unknown screen requested: " + this.requestedScreen); .subscribe((res: string) => {
} message = res;
console.log("Searching for integration for requested screen");
for (const integration of this.getIntegrations()) {
if (integration.category === category && integration.type === type) {
console.log("Configuring integration " + this.requestedIntegrationId + " category=" + category + " type=" + type);
SessionStorage.editIntegration = integration;
SessionStorage.editIntegrationId = this.requestedIntegrationId;
this.modifyIntegration(integration);
return;
}
}
console.log("Failed to find integration component for category=" + category + " type=" + type);
}
private async updateIntegrationState(integration: FE_Integration) {
if (!integration.isOnline) {
integration._isSupported = false;
this.translate.get('This integration is offline or unavailable').subscribe((res: string) => {integration._notSupportedReason = res});
return;
}
if (!integration.requirements) return;
let promises = integration.requirements.map(r => this.checkRequirement(r));
if (integration.category === "bot") {
const state = await this.scalar.getMembershipState(this.roomId, (<FE_SimpleBot>integration).userId);
if (state && state.response && state.response.membership) {
integration._inRoom = ["join", "invite"].indexOf(state.response.membership) !== -1;
} else integration._inRoom = false;
}
return Promise.all(promises).then(() => {
integration._isSupported = true;
integration._notSupportedReason = null;
}, error => {
console.error(error);
integration._isSupported = false;
integration._notSupportedReason = error;
});
}
private checkRequirement(requirement: FE_IntegrationRequirement) {
switch (requirement.condition) {
case "publicRoom":
return this.scalar.getJoinRule(this.roomId).then(payload => {
if (!payload.response) {
let message: string;
this.translate.get('Could not communicate with Element').subscribe((res: string) => {message = res});
return Promise.reject(message);
}
const isPublic = payload.response.join_rule === "public";
if (isPublic !== requirement.expectedValue) {
let message: string;
let message1: string;
this.translate.get(['The room must be', 'to use this integration']).subscribe((res: string) => {message = res[0]; message1 = res[1]});
return Promise.reject(message + (isPublic ? "non-public" : "public") + message1);
} else return Promise.resolve();
}); });
case "canSendEventTypes": return Promise.reject(message);
const processPayload = payload => { }
const response = <any>payload.response; })
if (response === true) return Promise.resolve(); .catch((err) => {
if (response.error || response.error.message) { console.error(err);
let message: string; if (requirement.expectedValue) {
this.translate.get('You cannot modify widgets in this room').subscribe((res: string) => {message = res}); let message: string;
return Promise.reject(message); this.translate
} .get("Expected to be able to send specific event types")
let message: string; .subscribe((res: string) => {
this.translate.get('Error communicating with Element').subscribe((res: string) => {message = res}); message = res;
return Promise.reject(message);
};
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) {
let message: string;
this.translate.get('Expected to not be able to send specific event types').subscribe((res: string) => {message = res});
return Promise.reject(message);
}
}).catch(err => {
console.error(err);
if (requirement.expectedValue) {
let message: string;
this.translate.get('Expected to be able to send specific event types').subscribe((res: string) => {message = res});
return Promise.reject(message);
}
}); });
case "userInRoom": return Promise.reject(message);
// TODO: Implement }
default: });
let message: string; case "userInRoom":
let message1: string; // TODO: Implement
this.translate.get(['Requirement', 'not found']).subscribe((res: string) => {message = res[0]; message1 = res[1]}); default:
return Promise.reject(message + requirement.condition + message1); let message: string;
} let message1: string;
this.translate
.get(["Requirement", "not found"])
.subscribe((res: string) => {
message = res[0];
message1 = res[1];
});
return Promise.reject(message + requirement.condition + message1);
} }
}
} }

View File

@ -1,7 +1,7 @@
// styles in src/style directory are applied to the whole page // styles in src/style directory are applied to the whole page
@import "../assets//fonts/opensans100-roboto300"; @import "../assets//fonts/opensans100-roboto300";
@import "../../node_modules/angular2-toaster/toaster"; @import "../../node_modules/angular2-toaster/toaster";
@import "../../node_modules/bootstrap/dist/css/bootstrap.min.css"; @import "~bootstrap/scss/bootstrap";
@import "~ngx-ui-switch/ui-switch.component.scss"; @import "~ngx-ui-switch/ui-switch.component.scss";
@import "themes/themes"; @import "themes/themes";
@import "components/ibox"; @import "components/ibox";