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",
"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": {
"version": "12.0.1",
"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",
"json-loader": "^0.5.7",
"mini-css-extract-plugin": "^2.1.0",
"ngx-modialog": "^5.0.1",
"postcss-cssnext": "^3.1.0",
"postcss-import": "^14.0.2",
"postcss-loader": "^6.1.1",

View File

@ -1,24 +1,23 @@
<div class="dialog">
<div class="dialog-header">
<h4>{{'Add a new self-hosted IRC Bridge' | translate}}</h4>
</div>
<div class="dialog-content">
<p>{{'Self-hosted IRC bridges must have' | translate}}<code>{{'provisioning' | translate}}</code> {{'enabled in the configuration.' | translate}}</p>
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="http://localhost:9999"
[(ngModel)]="provisionUrl" [disabled]="isSaving"/>
</label>
</div>
<div class="dialog-footer">
<button type="button" (click)="add()" title="close" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}}
</button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'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 class="modal-body">
<p>{{'Self-hosted IRC bridges must have' | translate}}<code>{{'provisioning' | translate}}</code> {{'enabled in the configuration.' | translate}}</p>
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="http://localhost:9999"
[(ngModel)]="provisionUrl" [disabled]="isSaving"/>
</label>
</div>
<div class="modal-footer">
<button type="button" (click)="add()" title="close" 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,23 +1,24 @@
import { Component } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
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 { TranslateService } from "@ngx-translate/core";
export class AddSelfhostedIrcBridgeDialogContext extends BSModalContext {
export interface AddSelfhostedIrcBridgeDialogContextt {
isSaving: boolean;
provisionUrl: string;
}
@Component({
templateUrl: "./add-selfhosted.component.html",
styleUrls: ["./add-selfhosted.component.scss"],
})
export class AdminIrcBridgeAddSelfhostedComponent implements ModalComponent<AddSelfhostedIrcBridgeDialogContext> {
export class AdminIrcBridgeAddSelfhostedComponent {
public isSaving = false;
public provisionUrl: string;
constructor(public dialog: DialogRef<AddSelfhostedIrcBridgeDialogContext>,
constructor(public modal: NgbActiveModal,
private ircApi: AdminIrcApiService,
private toaster: ToasterService,
public translate: TranslateService) {
@ -28,7 +29,7 @@ export class AdminIrcBridgeAddSelfhostedComponent implements ModalComponent<AddS
this.isSaving = true;
this.ircApi.newSelfhosted(this.provisionUrl).then(() => {
this.translate.get('IRC Bridge added').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close();
this.modal.close();
}).catch(err => {
console.error(err);
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 { AdminUpstreamApiService } from "../../../shared/services/admin/admin-upstream-api.service";
import { FE_IrcBridge } from "../../../shared/models/irc";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { AdminIrcBridgeNetworksComponent, IrcNetworksDialogContext } from "./networks/networks.component";
import {
AddSelfhostedIrcBridgeDialogContext,
AdminIrcBridgeAddSelfhostedComponent
} from "./add-selfhosted/add-selfhosted.component";
import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({
templateUrl: "./irc.component.html",
@ -28,7 +27,7 @@ export class AdminIrcBridgeComponent implements OnInit {
constructor(private upstreamApi: AdminUpstreamApiService,
private ircApi: AdminIrcApiService,
private toaster: ToasterService,
private modal: Modal,
private modal: NgbModal,
public translate: TranslateService) {
this.translate = translate;
}
@ -94,23 +93,34 @@ export class AdminIrcBridgeComponent implements OnInit {
}
public addSelfHostedBridge() {
this.modal.open(AdminIrcBridgeAddSelfhostedComponent, overlayConfigFactory({
isBlocking: true,
const selfhostedRef = this.modal.open(AdminIrcBridgeAddSelfhostedComponent, {
backdrop: 'static',
size: 'lg',
}, AddSelfhostedIrcBridgeDialogContext)).result.then(() => {
this.reload().catch(err => {
});
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); });
});
});
}
})
}
public editNetworks(bridge: FE_IrcBridge) {
this.modal.open(AdminIrcBridgeNetworksComponent, overlayConfigFactory({
bridge: bridge,
isBlocking: true,
const selfhostedRef = this.modal.open(AdminIrcBridgeNetworksComponent, {
backdrop: 'static',
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="dialog-header">
<h4>{{ bridge.upstreamId ? "matrix.org's" : "Self-hosted" }} IRC Bridge Networks</h4>
</div>
<div class="dialog-content">
<table class="table table-striped table-condensed table-bordered">
<thead>
<tr>
<th>{{'Network' | translate}}</th>
<th>{{'Enabled' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let network of networks trackById">
<td>{{ network.name }}</td>
<td>
<ui-switch [checked]="network.isEnabled" size="small" [disabled]="isUpdating"
(change)="toggleNetwork(network)"></ui-switch>
</td>
</tr>
</tbody>
</table>
</div>
<div class="dialog-footer">
<button type="button" (click)="dialog.close()" title="close" class="btn btn-primary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{ 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 class="modal-body">
<table class="table table-striped table-condensed table-bordered">
<thead>
<tr>
<th>{{'Network' | translate}}</th>
<th>{{'Enabled' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let network of networks trackById">
<td>{{ network.name }}</td>
<td>
<ui-switch [checked]="network.isEnabled" size="small" [disabled]="isUpdating"
(change)="toggleNetwork(network)"></ui-switch>
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" (click)="modal.close()" title="close" class="btn btn-outline-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}}
</button>
</div>

View File

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

View File

@ -1,26 +1,25 @@
<div class="dialog">
<div class="dialog-header">
<h4> {{'self-hosted Slack bridge' | translate}} ({{ isAdding ? "Add a new" : "Edit" }})</h4>
</div>
<div class="dialog-content">
<p>
{{'Self-hosted Slack bridges already have provisioning enabled. Be careful not to expose the API to the public internet.' | translate}}
</p>
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="http://localhost:9000"
[(ngModel)]="provisionUrl" [disabled]="isSaving"/>
</label>
</div>
<div class="dialog-footer">
<button type="button" (click)="add()" title="close" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}}
</button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'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 class="modal-body">
<p>
{{'Self-hosted Slack bridges already have provisioning enabled. Be careful not to expose the API to the public internet.' | translate}}
</p>
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="http://localhost:9000"
[(ngModel)]="provisionUrl" [disabled]="isSaving"/>
</label>
</div>
<div class="modal-footer">
<button type="button" (click)="add()" title="close" 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,34 +1,31 @@
import { Component } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
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 { TranslateService } from "@ngx-translate/core";
export class ManageSelfhostedSlackBridgeDialogContext extends BSModalContext {
public provisionUrl: string;
public bridgeId: number;
export interface ManageSelfhostedSlackBridgeDialogContext {
provisionUrl: string;
bridgeId: number;
isAdding: boolean;
}
@Component({
templateUrl: "./manage-selfhosted.component.html",
styleUrls: ["./manage-selfhosted.component.scss"],
})
export class AdminSlackBridgeManageSelfhostedComponent implements ModalComponent<ManageSelfhostedSlackBridgeDialogContext> {
export class AdminSlackBridgeManageSelfhostedComponent {
public isSaving = false;
public provisionUrl: string;
public bridgeId: number;
public isAdding = false;
public isAdding = true;
constructor(public dialog: DialogRef<ManageSelfhostedSlackBridgeDialogContext>,
constructor(public modal: NgbActiveModal,
private slackApi: AdminSlackApiService,
private toaster: ToasterService,
public translate: TranslateService) {
this.translate = translate;
this.provisionUrl = dialog.context.provisionUrl;
this.bridgeId = dialog.context.bridgeId;
this.isAdding = !this.bridgeId;
}
public add() {
@ -36,7 +33,7 @@ export class AdminSlackBridgeManageSelfhostedComponent implements ModalComponent
if (this.isAdding) {
this.slackApi.newSelfhosted(this.provisionUrl).then(() => {
this.translate.get('Slack bridge added').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close();
this.modal.close();
}).catch(err => {
console.error(err);
this.isSaving = false;
@ -45,7 +42,7 @@ export class AdminSlackBridgeManageSelfhostedComponent implements ModalComponent
} else {
this.slackApi.updateSelfhosted(this.bridgeId, this.provisionUrl).then(() => {
this.translate.get('Slack bridge updated').subscribe((res: string) => this.toaster.pop("success", res));
this.dialog.close();
this.modal.close();
}).catch(err => {
console.error(err);
this.isSaving = false;

View File

@ -1,6 +1,5 @@
import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { FE_Upstream } from "../../../shared/models/admin-responses";
import { AdminUpstreamApiService } from "../../../shared/services/admin/admin-upstream-api.service";
import {
@ -10,6 +9,7 @@ import {
import { FE_SlackBridge } from "../../../shared/models/slack";
import { AdminSlackApiService } from "../../../shared/services/admin/admin-slack-api.service";
import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({
templateUrl: "./slack.component.html",
@ -26,7 +26,7 @@ export class AdminSlackBridgeComponent implements OnInit {
constructor(private slackApi: AdminSlackApiService,
private upstreamApi: AdminUpstreamApiService,
private toaster: ToasterService,
private modal: Modal,
private modal: NgbModal,
public translate: TranslateService) {
this.translate = translate;
}
@ -75,31 +75,38 @@ export class AdminSlackBridgeComponent implements OnInit {
}
public addSelfHostedBridge() {
this.modal.open(AdminSlackBridgeManageSelfhostedComponent, overlayConfigFactory({
isBlocking: true,
const selfhostedRef = this.modal.open(AdminSlackBridgeManageSelfhostedComponent, {
backdrop: 'static',
size: 'lg',
provisionUrl: '',
}, ManageSelfhostedSlackBridgeDialogContext)).result.then(() => {
this.reload().catch(err => {
});
selfhostedRef.result.then(() => {
try {
this.reload()
} catch (err) {
console.error(err);
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) {
this.modal.open(AdminSlackBridgeManageSelfhostedComponent, overlayConfigFactory({
isBlocking: true,
const selfhostedRef = this.modal.open(AdminSlackBridgeManageSelfhostedComponent, {
backdrop: 'static',
size: 'lg',
provisionUrl: bridge.provisionUrl,
bridgeId: bridge.id,
}, ManageSelfhostedSlackBridgeDialogContext)).result.then(() => {
this.reload().catch(err => {
});
selfhostedRef.result.then(() => {
try {
this.reload()
} catch (err) {
console.error(err);
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="dialog-header">
<h4> {{'self-hosted Telegram bridge' | translate}} ({{ isAdding ? "Add a new" : "Edit" }})</h4>
</div>
<div class="dialog-content">
<p>{{'Self-hosted Telegram bridges must have' | translate}} <code>{{'provisioning' | translate}}</code> {{'enabled in the configuration.' | translate}}</p>
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="http://localhost:9999/_matrix/provision/v1"
[(ngModel)]="provisionUrl" [disabled]="isSaving"/>
</label>
<label class="label-block">
{{'Shared Secret' | translate}}
<span class="text-muted ">{{'The shared secret defined in the configuration for provisioning.' | translate}}</span>
<input type="text" class="form-control"
placeholder="some_secret_value"
[(ngModel)]="sharedSecret" [disabled]="isSaving"/>
</label>
<label class="label-block">
{{'Promote Telegram Puppeting' | translate}}
<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"
(change)="allowTgPuppets = !allowTgPuppets"></ui-switch>
</label>
<label class="label-block">
{{'Promote Matrix Puppeting' | translate}}
<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"
(change)="allowMxPuppets = !allowMxPuppets"></ui-switch>
</label>
</div>
<div class="dialog-footer">
<button type="button" (click)="add()" title="close" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}}
</button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'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 class="modal-body">
<p>{{'Self-hosted Telegram bridges must have' | translate}} <code>{{'provisioning' | translate}}</code> {{'enabled in the configuration.' | translate}}</p>
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="http://localhost:9999/_matrix/provision/v1"
[(ngModel)]="provisionUrl" [disabled]="isSaving"/>
</label>
<label class="label-block">
{{'Shared Secret' | translate}}
<span class="text-muted ">{{'The shared secret defined in the configuration for provisioning.' | translate}}</span>
<input type="text" class="form-control"
placeholder="some_secret_value"
[(ngModel)]="sharedSecret" [disabled]="isSaving"/>
</label>
<label class="label-block">
{{'Promote Telegram Puppeting' | translate}}
<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"
(change)="allowTgPuppets = !allowTgPuppets"></ui-switch>
</label>
<label class="label-block">
{{'Promote Matrix Puppeting' | translate}}
<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"
(change)="allowMxPuppets = !allowMxPuppets"></ui-switch>
</label>
</div>
<div class="modal-footer">
<button type="button" (click)="add()" 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,43 +1,37 @@
import { Component } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
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 { TranslateService } from "@ngx-translate/core";
export class ManageSelfhostedTelegramBridgeDialogContext extends BSModalContext {
public provisionUrl: string;
public sharedSecret: string;
public allowTgPuppets = false;
public allowMxPuppets = false;
public bridgeId: number;
export interface ManageSelfhostedTelegramBridgeDialogContext {
provisionUrl: string;
sharedSecret: string;
allowTgPuppets: boolean;
allowMxPuppets: boolean;
bridgeId: number;
isAdding: boolean;
}
@Component({
templateUrl: "./manage-selfhosted.component.html",
styleUrls: ["./manage-selfhosted.component.scss"],
})
export class AdminTelegramBridgeManageSelfhostedComponent implements ModalComponent<ManageSelfhostedTelegramBridgeDialogContext> {
export class AdminTelegramBridgeManageSelfhostedComponent {
public isSaving = false;
public provisionUrl: string;
public sharedSecret: string;
public allowTgPuppets = false;
public allowMxPuppets = false;
public bridgeId: number;
public isAdding = false;
isSaving = false;
provisionUrl: string;
sharedSecret: string;
allowTgPuppets = false;
allowMxPuppets = false;
bridgeId: number;
isAdding = true;
constructor(public dialog: DialogRef<ManageSelfhostedTelegramBridgeDialogContext>,
constructor(public modal: NgbActiveModal,
private telegramApi: AdminTelegramApiService,
private toaster: ToasterService,
public translate: TranslateService) {
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() {
@ -49,7 +43,7 @@ export class AdminTelegramBridgeManageSelfhostedComponent implements ModalCompon
if (this.isAdding) {
this.telegramApi.newSelfhosted(this.provisionUrl, this.sharedSecret, options).then(() => {
this.translate.get('Telegram bridge added').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close();
this.modal.close();
}).catch(err => {
console.error(err);
this.isSaving = false;
@ -58,7 +52,7 @@ export class AdminTelegramBridgeManageSelfhostedComponent implements ModalCompon
} else {
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.dialog.close();
this.modal.close();
}).catch(err => {
console.error(err);
this.isSaving = false;

View File

@ -1,6 +1,5 @@
import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import {
AdminTelegramBridgeManageSelfhostedComponent,
ManageSelfhostedTelegramBridgeDialogContext
@ -8,6 +7,7 @@ import {
import { FE_TelegramBridge } from "../../../shared/models/telegram";
import { AdminTelegramApiService } from "../../../shared/services/admin/admin-telegram-api.service";
import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({
templateUrl: "./telegram.component.html",
@ -21,7 +21,7 @@ export class AdminTelegramBridgeComponent implements OnInit {
constructor(private telegramApi: AdminTelegramApiService,
private toaster: ToasterService,
private modal: Modal,
private modal: NgbModal,
public translate: TranslateService) {
this.translate = translate;
}
@ -40,19 +40,23 @@ export class AdminTelegramBridgeComponent implements OnInit {
}
public addSelfHostedBridge() {
this.modal.open(AdminTelegramBridgeManageSelfhostedComponent, overlayConfigFactory({
isBlocking: true,
const selfhostedRef = this.modal.open(AdminTelegramBridgeManageSelfhostedComponent, {
backdrop: 'static',
size: 'lg',
provisionUrl: '',
sharedSecret: '',
allowPuppets: false,
}, ManageSelfhostedTelegramBridgeDialogContext)).result.then(() => {
this.reload().catch(err => {
});
selfhostedRef.result.then(() => {
try {
this.reload()
} catch (err) {
console.error(err);
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 {
@ -63,20 +67,24 @@ export class AdminTelegramBridgeComponent implements OnInit {
}
public editBridge(bridge: FE_TelegramBridge) {
this.modal.open(AdminTelegramBridgeManageSelfhostedComponent, overlayConfigFactory({
isBlocking: true,
const selfhostedRef = this.modal.open(AdminTelegramBridgeManageSelfhostedComponent, {
backdrop: 'static',
size: 'lg',
provisionUrl: bridge.provisionUrl,
sharedSecret: bridge.sharedSecret,
allowTgPuppets: bridge.options ? bridge.options.allowTgPuppets : false,
allowMxPuppets: bridge.options ? bridge.options.allowMxPuppets : false,
bridgeId: bridge.id,
}, ManageSelfhostedTelegramBridgeDialogContext)).result.then(() => {
this.reload().catch(err => {
});
selfhostedRef.result.then(() => {
try {
this.reload()
} catch (err) {
console.error(err);
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="dialog-header">
<h4> {{'self-hosted webhook bridge' | translate}} ({{ isAdding ? "Add a new" : "Edit" }})</h4>
</div>
<div class="dialog-content">
<p>{{'Self-hosted webhook bridges must have' | translate}} <code>{{'provisioning' | translate}}</code> {{'enabled in the configuration.' | translate}}</p>
<label class="label-block">
{{'Provisioning URL' | translate}}
<span class="text-muted ">{{'The public URL for the bridge.' | translate}}</span>
<input type="text" class="form-control"
placeholder="https://webhooks.example.org:9000/"
[(ngModel)]="provisionUrl" [disabled]="isSaving"/>
</label>
<label class="label-block">
{{'Shared Secret' | translate}}
<span class="text-muted ">{{'The provisioning secret defined in the configuration.' | translate}}</span>
<input type="text" class="form-control"
placeholder="some_secret_value"
[(ngModel)]="sharedSecret" [disabled]="isSaving"/>
</label>
</div>
<div class="dialog-footer">
<button type="button" (click)="add()" title="close" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}}
</button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'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 class="modal-body">
<p>{{'Self-hosted webhook bridges must have' | translate}} <code>{{'provisioning' | translate}}</code> {{'enabled in the configuration.' | translate}}</p>
<label class="label-block">
{{'Provisioning URL' | translate}}
<span class="text-muted ">{{'The public URL for the bridge.' | translate}}</span>
<input type="text" class="form-control"
placeholder="https://webhooks.example.org:9000/"
[(ngModel)]="provisionUrl" [disabled]="isSaving"/>
</label>
<label class="label-block">
{{'Shared Secret' | translate}}
<span class="text-muted ">{{'The provisioning secret defined in the configuration.' | translate}}</span>
<input type="text" class="form-control"
placeholder="some_secret_value"
[(ngModel)]="sharedSecret" [disabled]="isSaving"/>
</label>
</div>
<div class="modal-footer">
<button type="button" (click)="add()" 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,39 +1,37 @@
import { Component } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
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 { TranslateService } from "@ngx-translate/core";
export class ManageSelfhostedWebhooksBridgeDialogContext extends BSModalContext {
public provisionUrl: string;
public sharedSecret: string;
public allowTgPuppets = false;
public allowMxPuppets = false;
public bridgeId: number;
export interface ManageSelfhostedWebhooksBridgeDialogContext {
provisionUrl: string;
sharedSecret: string;
allowTgPuppets: boolean;
allowMxPuppets: boolean;
bridgeId: number;
isAdding: boolean;
}
@Component({
templateUrl: "./manage-selfhosted.component.html",
styleUrls: ["./manage-selfhosted.component.scss"],
})
export class AdminWebhooksBridgeManageSelfhostedComponent implements ModalComponent<ManageSelfhostedWebhooksBridgeDialogContext> {
export class AdminWebhooksBridgeManageSelfhostedComponent {
public isSaving = false;
public provisionUrl: string;
public sharedSecret: string;
public provisionUrl: '';
public sharedSecret: '';
public allowTgPuppets = false;
public allowMxPuppets = false;
public bridgeId: number;
public isAdding = false;
public isAdding = true;
constructor(public dialog: DialogRef<ManageSelfhostedWebhooksBridgeDialogContext>,
constructor(public modal: NgbActiveModal,
private webhooksApi: AdminWebhooksApiService,
private toaster: ToasterService,
public translate: TranslateService) {
this.translate = translate;
this.provisionUrl = dialog.context.provisionUrl;
this.sharedSecret = dialog.context.sharedSecret;
this.bridgeId = dialog.context.bridgeId;
this.isAdding = !this.bridgeId;
}
public add() {
@ -41,7 +39,7 @@ export class AdminWebhooksBridgeManageSelfhostedComponent implements ModalCompon
if (this.isAdding) {
this.webhooksApi.newSelfhosted(this.provisionUrl, this.sharedSecret).then(() => {
this.translate.get('Webhook bridge added').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close();
this.modal.close();
}).catch(err => {
console.error(err);
this.isSaving = false;
@ -50,7 +48,7 @@ export class AdminWebhooksBridgeManageSelfhostedComponent implements ModalCompon
} else {
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.dialog.close();
this.modal.close();
}).catch(err => {
console.error(err);
this.isSaving = false;

View File

@ -1,6 +1,5 @@
import { Component, OnInit } from "@angular/core";
import { ToasterService } from "angular2-toaster";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import {
AdminWebhooksBridgeManageSelfhostedComponent,
ManageSelfhostedWebhooksBridgeDialogContext
@ -8,6 +7,7 @@ import {
import { FE_WebhooksBridge } from "../../../shared/models/webhooks";
import { AdminWebhooksApiService } from "../../../shared/services/admin/admin-webhooks-api.service";
import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({
templateUrl: "./webhooks.component.html",
@ -21,7 +21,7 @@ export class AdminWebhooksBridgeComponent implements OnInit {
constructor(private webhooksApi: AdminWebhooksApiService,
private toaster: ToasterService,
private modal: Modal,
private modal: NgbModal,
public translate: TranslateService) {
this.translate = translate;
}
@ -40,34 +40,42 @@ export class AdminWebhooksBridgeComponent implements OnInit {
}
public addSelfHostedBridge() {
this.modal.open(AdminWebhooksBridgeManageSelfhostedComponent, overlayConfigFactory({
isBlocking: true,
const selfhostedRef = this.modal.open(AdminWebhooksBridgeManageSelfhostedComponent, {
backdrop: 'static',
size: 'lg',
provisionUrl: '',
sharedSecret: '',
allowPuppets: false,
}, ManageSelfhostedWebhooksBridgeDialogContext)).result.then(() => {
this.reload().catch(err => {
});
selfhostedRef.result.then(() => {
try {
this.reload()
} catch (err) {
console.error(err);
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) {
this.modal.open(AdminWebhooksBridgeManageSelfhostedComponent, overlayConfigFactory({
isBlocking: true,
const selfhostedRef = this.modal.open(AdminWebhooksBridgeManageSelfhostedComponent, {
backdrop: 'static',
size: 'lg',
provisionUrl: bridge.provisionUrl,
sharedSecret: bridge.sharedSecret,
bridgeId: bridge.id,
}, ManageSelfhostedWebhooksBridgeDialogContext)).result.then(() => {
this.reload().catch(err => {
});
selfhostedRef.result.then(() => {
try {
this.reload()
} catch (err) {
console.error(err);
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="dialog-header">
<h4>{{ isAdding ? "Add a new" : "Edit" }} {{ 'custom bot' | translate}}</h4>
</div>
<div class="dialog-content">
<label class="label-block">
User ID
<span class="text-muted">{{'The user ID that Dimension will invite to rooms.' | translate}}</span>
<input type="text" class="form-control"
placeholder="@yourbot:example.org"
[(ngModel)]="bot.userId" [disabled]="isSaving" (blur)="loadProfile()"/>
</label>
<label class="label-block">
Description
<span class="text-muted ">{{'A few words here will help people understand what the bot does.' | translate}}</span>
<input type="text" class="form-control"
placeholder="Does awesome things"
[(ngModel)]="bot.description" [disabled]="isSaving"/>
</label>
<label class="label-block">
{{'Display Name' | translate}}
<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"
placeholder="Cool Bot"
[(ngModel)]="bot.name" [disabled]="isSaving"/>
</label>
<label class="label-block">
Avatar URL
<span class="text-muted ">{{'This can either be an MXC URI or a plain URL.' | translate}}</span>
<input type="text" class="form-control"
placeholder="mxc://example.org/C00lAvat4r"
[(ngModel)]="bot.avatarUrl" [disabled]="isSaving"/>
</label>
<label class="label-block">
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}}
<a href="https://t2bot.io/docs/access_tokens/" target="_blank">{{'Learn more about access tokens.' | translate}}</a>.
</span>
<input type="text" class="form-control"
placeholder="MDaX..."
[(ngModel)]="bot.accessToken" [disabled]="isSaving"/>
</label>
</div>
<div class="dialog-footer">
<button type="button" (click)="add()" title="close" class="btn btn-primary btn-sm">
<i class="far fa-save"></i> {{'Save' | translate}}
</button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i>{{'Cancel' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{ 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 class="modal-body">
<label class="label-block">
User ID
<span class="text-muted">{{'The user ID that Dimension will invite to rooms.' | translate}}</span>
<input type="text" class="form-control"
placeholder="@yourbot:example.org"
[(ngModel)]="bot.userId" [disabled]="isSaving" (blur)="loadProfile()"/>
</label>
<label class="label-block">
Description
<span class="text-muted ">{{'A few words here will help people understand what the bot does.' | translate}}</span>
<input type="text" class="form-control"
placeholder="Does awesome things"
[(ngModel)]="bot.description" [disabled]="isSaving"/>
</label>
<label class="label-block">
{{'Display Name' | translate}}
<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"
placeholder="Cool Bot"
[(ngModel)]="bot.name" [disabled]="isSaving"/>
</label>
<label class="label-block">
Avatar URL
<span class="text-muted ">{{'This can either be an MXC URI or a plain URL.' | translate}}</span>
<input type="text" class="form-control"
placeholder="mxc://example.org/C00lAvat4r"
[(ngModel)]="bot.avatarUrl" [disabled]="isSaving"/>
</label>
<label class="label-block">
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}}
<a href="https://t2bot.io/docs/access_tokens/" target="_blank">{{'Learn more about access tokens.' | translate}}</a>.
</span>
<input type="text" class="form-control"
placeholder="MDaX..."
[(ngModel)]="bot.accessToken" [disabled]="isSaving"/>
</label>
</div>
<div class="modal-footer">
<button type="button" (click)="add()" 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,34 +1,32 @@
import { Component } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
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 { AdminCustomSimpleBotsApiService } from "../../../shared/services/admin/admin-custom-simple-bots-api.service";
import { TranslateService } from "@ngx-translate/core";
export class AddCustomBotDialogContext extends BSModalContext {
export interface AddCustomBotDialogContext {
bot: FE_CustomSimpleBot;
isAdding: boolean;
}
@Component({
templateUrl: "./add.component.html",
styleUrls: ["./add.component.scss"],
})
export class AdminAddCustomBotComponent implements ModalComponent<AddCustomBotDialogContext> {
export class AdminAddCustomBotComponent {
public bot: FE_CustomSimpleBot;
public isAdding = false;
public bot = <FE_CustomSimpleBot>{};
public isAdding = true;
public isSaving = false;
private lastProfile: FE_UserProfile;
constructor(public dialog: DialogRef<AddCustomBotDialogContext>,
constructor(public modal: NgbActiveModal,
private botApi: AdminCustomSimpleBotsApiService,
private toaster: ToasterService,
public translate: TranslateService) {
this.translate = translate;
this.bot = this.dialog.context.bot || <FE_CustomSimpleBot>{};
this.isAdding = !this.dialog.context.bot;
}
public loadProfile() {
@ -89,7 +87,7 @@ export class AdminAddCustomBotComponent implements ModalComponent<AddCustomBotDi
promise.then(() => {
this.translate.get('Bot updated').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close();
this.modal.close();
}).catch(error => {
this.isSaving = false;
console.error(error);

View File

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

View File

@ -2,12 +2,11 @@ import { Component } from "@angular/core";
import { AdminApiService } from "../../shared/services/admin/admin-api.service";
import { FE_DimensionConfig } from "../../shared/models/admin-responses";
import { ToasterService } from "angular2-toaster";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import {
AdminLogoutConfirmationDialogComponent,
LogoutConfirmationDialogContext
} from "./logout-confirmation/logout-confirmation.component";
import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({
templateUrl: "./home.component.html",
@ -20,7 +19,7 @@ export class AdminHomeComponent {
constructor(private adminApi: AdminApiService,
private toaster: ToasterService,
private modal: Modal,
private modal: NgbModal,
public translate: TranslateService) {
this.translate = translate;
adminApi.getConfig().then(config => {
@ -30,16 +29,18 @@ export class AdminHomeComponent {
}
public logoutAll(): void {
this.modal.open(AdminLogoutConfirmationDialogComponent, overlayConfigFactory({
isBlocking: true,
}, LogoutConfirmationDialogContext)).result.then(() => {
this.adminApi.logoutAll().then(() => {
const selfhostedRef = this.modal.open(AdminLogoutConfirmationDialogComponent, {
backdrop: 'static'
});
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.config.sessionInfo.numTokens = 0;
}).catch(err => {
} catch (err) {
console.error(err);
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="dialog-header">
<h4>{{'Logout confirmation' | translate}}</h4>
</div>
<div class="dialog-content">
<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}}
</p>
</div>
<div class="dialog-footer">
<button type="button" (click)="dialog.dismiss()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
<button type="button" (click)="dialog.close()" title="logout everyone" class="btn btn-danger btn-sm">
<i class="far fa-times-circle"></i> {{'Logout Everyone' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'Logout confirmation' | translate}}</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div>
<div class="modal-body">
<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}}
</p>
</div>
<div class="modal-footer">
<button type="button" (click)="modal.dismiss()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
<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}}
</button>
</div>

View File

@ -1,16 +1,15 @@
import { Component } from "@angular/core";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
export class LogoutConfirmationDialogContext extends BSModalContext {
export interface LogoutConfirmationDialogContext {
}
@Component({
templateUrl: "./logout-confirmation.component.html",
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 { ToasterService } from "angular2-toaster";
import { ActivatedRoute, Router } from "@angular/router";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import {
AdminNebAppserviceConfigComponent,
AppserviceConfigDialogContext
} from "../appservice-config/appservice-config.component";
import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
@Component({
@ -26,9 +26,9 @@ export class AdminAddSelfhostedNebComponent {
private toaster: ToasterService,
private router: Router,
private activatedRoute: ActivatedRoute,
private modal: Modal,
private modal: NgbModal,
public translate: TranslateService) {
this.translate = translate;
this.translate = translate;{
}
public save(): void {
@ -37,12 +37,14 @@ export class AdminAddSelfhostedNebComponent {
return this.nebApi.newAppserviceConfiguration(this.adminUrl, appservice);
}).then(neb => {
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',
}, 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 => {
console.error(err);
this.isSaving = false;

View File

@ -1,22 +1,21 @@
<div class="dialog">
<div class="dialog-header">
<h4>{{'go-neb appservice configuration' | translate}}</h4>
</div>
<div class="dialog-content" *ngIf="isLoading">
<my-spinner></my-spinner>
</div>
<div class="dialog-content" *ngIf="!isLoading">
{{'Copy and paste this configuration to' | translate}} <code>appservice-{{appservice.id}}.yaml</code>
{{'on your homeserver and register it as an application service.' | translate}}
<br/>
<pre>{{appserviceConfig}}</pre>
</div>
<div class="dialog-footer" *ngIf="!isLoading">
<button type="button" (click)="dialog.close()" title="save" class="btn btn-primary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}}
</button>
<button type="button" (click)="test()" title="close" class="btn btn-secondary btn-sm">
<i class="fa fa-exchange-alt"></i> {{'Test Configuration' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'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 class="modal-body" *ngif="isLoading">
<my-spinner></my-spinner>
</div>
<div class="modal-body" *ngif="!isLoading">
{{'Copy and paste this configuration to' | translate}} <code>appservice-{{appservice.id}}.yaml</code>
{{'on your homeserver and register it as an application service.' | translate}}
<br/>
<pre>{{appserviceConfig}}</pre>
</div>
<div class="modal-footer">
<button type="button" (click)="test()" title="save" class="btn btn-primary btn-sm">
<i class="fa fa-exchange-alt"></i> {{'Test Configuration' | 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,32 +1,29 @@
import { Component } from "@angular/core";
import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { FE_Appservice, FE_NebConfiguration } from "../../../shared/models/admin-responses";
import { AdminAppserviceApiService } from "../../../shared/services/admin/admin-appservice-api.service";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
export class AppserviceConfigDialogContext extends BSModalContext {
public neb: FE_NebConfiguration;
export interface AppserviceConfigDialogContext {
neb: FE_NebConfiguration;
}
@Component({
templateUrl: "./appservice-config.component.html",
styleUrls: ["./appservice-config.component.scss"],
})
export class AdminNebAppserviceConfigComponent implements ModalComponent<AppserviceConfigDialogContext> {
export class AdminNebAppserviceConfigComponent {
public isLoading = true;
public neb: FE_NebConfiguration;
public appservice: FE_Appservice;
constructor(public dialog: DialogRef<AppserviceConfigDialogContext>,
constructor(public modal: NgbActiveModal,
private adminAppserviceApi: AdminAppserviceApiService,
private toaster: ToasterService,
public translate: TranslateService) {
this.translate = translate;
this.neb = dialog.context.neb;
this.adminAppserviceApi.getAppservice(this.neb.appserviceId).then(appservice => {
this.appservice = appservice;
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_Integration } from "../../../shared/models/integration";
export class NebBotConfigurationDialogContext extends BSModalContext {
public integration: FE_Integration;
public neb: FE_NebConfiguration;
export interface NebBotConfigurationDialogContext {
integration: FE_Integration;
neb: FE_NebConfiguration;
}

View File

@ -1,33 +1,32 @@
<div class="dialog">
<div class="dialog-header">
<h4>{{'Giphy Configuration' | translate}}</h4>
</div>
<div class="dialog-content" *ngIf="isLoading">
<my-spinner></my-spinner>
</div>
<div class="dialog-content" *ngIf="!isLoading">
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="your_api_key_here"
[(ngModel)]="config.api_key" [disabled]="isUpdating"/>
</label>
<label class="label-block">
{{'Image Size' | translate}}
<span class="text-muted ">{{'GIFs can be large, and sometimes it is more desirable to have them downsized.' | translate}}</span>
<label class="checkbox">
<input type="checkbox" [(ngModel)]="config.use_downsized" [disabled]="isUpdating"/>
{{'Use downsized images' | translate}}
</label>
</label>
</div>
<div class="dialog-footer" *ngIf="!isLoading">
<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)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'Giphy Configuration' | translate}}</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div>
<div class="modal-body" *ngIf="isLoading">
<my-spinner></my-spinner>
</div>
<div class="modal-body" *ngIf="!isLoading">
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="your_api_key_here"
[(ngModel)]="config.api_key" [disabled]="isUpdating"/>
</label>
<label class="label-block">
{{'Image Size' | translate}}
<span class="text-muted ">{{'GIFs can be large, and sometimes it is more desirable to have them downsized.' | translate}}</span>
<label class="checkbox">
<input type="checkbox" [(ngModel)]="config.use_downsized" [disabled]="isUpdating"/>
{{'Use downsized images' | translate}}
</label>
</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,11 +1,10 @@
import { Component, OnInit } from "@angular/core";
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 { FE_NebConfiguration } from "../../../../shared/models/admin-responses";
import { FE_Integration } from "../../../../shared/models/integration";
import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
interface GiphyConfig {
api_key: string;
@ -16,7 +15,7 @@ interface GiphyConfig {
templateUrl: "./giphy.component.html",
styleUrls: ["./giphy.component.scss", "../config-dialog.scss"],
})
export class AdminNebGiphyConfigComponent implements ModalComponent<NebBotConfigurationDialogContext>, OnInit {
export class AdminNebGiphyConfigComponent implements OnInit {
public isLoading = true;
public isUpdating = false;
@ -24,13 +23,11 @@ export class AdminNebGiphyConfigComponent implements ModalComponent<NebBotConfig
public integration: FE_Integration;
public neb: FE_NebConfiguration;
constructor(public dialog: DialogRef<NebBotConfigurationDialogContext>,
constructor(public modal: NgbActiveModal,
private adminNebApi: AdminNebApiService,
private toaster: ToasterService,
public translate: TranslateService) {
this.translate = translate;
this.neb = dialog.context.neb;
this.integration = dialog.context.integration;
}
public ngOnInit() {
@ -47,7 +44,7 @@ export class AdminNebGiphyConfigComponent implements ModalComponent<NebBotConfig
this.isUpdating = true;
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.dialog.close();
this.modal.close();
}).catch(err => {
this.isUpdating = false;
console.error(err);

View File

@ -1,32 +1,31 @@
<div class="dialog">
<div class="dialog-header">
<h4>{{'Google Configuration' | translate}}</h4>
</div>
<div class="dialog-content" *ngIf="isLoading">
<my-spinner></my-spinner>
</div>
<div class="dialog-content" *ngIf="!isLoading">
<label class="label-block">
{{'Api Key' | translate}}
<span class="text-muted ">{{'The API key for your Google Application.' | translate}}</span>
<input type="text" class="form-control"
placeholder="your_api_key_here"
[(ngModel)]="config.api_key" [disabled]="isUpdating"/>
</label>
<label class="label-block">
Search Engine ID
<span class="text-muted ">{{'The search engine ID' | translate}}</span>
<input type="text" class="form-control"
placeholder="your_cx_id_here"
[(ngModel)]="config.cx" [disabled]="isUpdating"/>
</label>
</div>
<div class="dialog-footer" *ngIf="!isLoading">
<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)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'Google Configuration' | translate}}</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div>
<div class="modal-body" *ngIf="isLoading">
<my-spinner></my-spinner>
</div>
<div class="modal-body" *ngIf="!isLoading">
<label class="label-block">
{{'Api Key' | translate}}
<span class="text-muted ">{{'The API key for your Google Application.' | translate}}</span>
<input type="text" class="form-control"
placeholder="your_api_key_here"
[(ngModel)]="config.api_key" [disabled]="isUpdating"/>
</label>
<label class="label-block">
{{'Search Engine ID' | translate}}
<span class="text-muted ">{{'The search engine ID' | translate}}</span>
<input type="text" class="form-control"
placeholder="your_cx_id_here"
[(ngModel)]="config.cx" [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,11 +1,10 @@
import { Component, OnInit } from "@angular/core";
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 { FE_NebConfiguration } from "../../../../shared/models/admin-responses";
import { FE_Integration } from "../../../../shared/models/integration";
import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
interface GoogleConfig {
api_key: string;
@ -16,7 +15,7 @@ interface GoogleConfig {
templateUrl: "./google.component.html",
styleUrls: ["./google.component.scss", "../config-dialog.scss"],
})
export class AdminNebGoogleConfigComponent implements ModalComponent<NebBotConfigurationDialogContext>, OnInit {
export class AdminNebGoogleConfigComponent implements OnInit {
public isLoading = true;
public isUpdating = false;
@ -24,13 +23,11 @@ export class AdminNebGoogleConfigComponent implements ModalComponent<NebBotConfi
public integration: FE_Integration;
public neb: FE_NebConfiguration;
constructor(public dialog: DialogRef<NebBotConfigurationDialogContext>,
constructor(public modal: NgbActiveModal,
private adminNebApi: AdminNebApiService,
private toaster: ToasterService,
public translate: TranslateService) {
this.translate = translate;
this.neb = dialog.context.neb;
this.integration = dialog.context.integration;
}
public ngOnInit() {
@ -47,7 +44,7 @@ export class AdminNebGoogleConfigComponent implements ModalComponent<NebBotConfi
this.isUpdating = true;
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.dialog.close();
this.modal.close();
}).catch(err => {
this.isUpdating = false;
console.error(err);

View File

@ -1,25 +1,24 @@
<div class="dialog">
<div class="dialog-header">
<h4>{{'Guggy Configuration' | translate}}</h4>
</div>
<div class="dialog-content" *ngIf="isLoading">
<my-spinner></my-spinner>
</div>
<div class="dialog-content" *ngIf="!isLoading">
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="your_api_key_here"
[(ngModel)]="config.api_key" [disabled]="isUpdating"/>
</label>
</div>
<div class="dialog-footer" *ngIf="!isLoading">
<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)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'Guggy Configuration' | translate}}</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div>
<div class="modal-body" *ngIf="isLoading">
<my-spinner></my-spinner>
</div>
<div class="modal-body" *ngIf="!isLoading">
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="your_api_key_here"
[(ngModel)]="config.api_key" [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,11 +1,10 @@
import { Component, OnInit } from "@angular/core";
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 { FE_NebConfiguration } from "../../../../shared/models/admin-responses";
import { FE_Integration } from "../../../../shared/models/integration";
import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
interface GuggyConfig {
api_key: string;
@ -15,7 +14,7 @@ interface GuggyConfig {
templateUrl: "./guggy.component.html",
styleUrls: ["./guggy.component.scss", "../config-dialog.scss"],
})
export class AdminNebGuggyConfigComponent implements ModalComponent<NebBotConfigurationDialogContext>, OnInit {
export class AdminNebGuggyConfigComponent implements OnInit {
public isLoading = true;
public isUpdating = false;
@ -23,13 +22,11 @@ export class AdminNebGuggyConfigComponent implements ModalComponent<NebBotConfig
public integration: FE_Integration;
public neb: FE_NebConfiguration;
constructor(public dialog: DialogRef<NebBotConfigurationDialogContext>,
constructor(public modal: NgbActiveModal,
private adminNebApi: AdminNebApiService,
private toaster: ToasterService,
public translate: TranslateService) {
this.translate = translate;
this.neb = dialog.context.neb;
this.integration = dialog.context.integration;
}
public ngOnInit() {
@ -46,7 +43,7 @@ export class AdminNebGuggyConfigComponent implements ModalComponent<NebBotConfig
this.isUpdating = true;
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.dialog.close();
this.modal.close();
}).catch(err => {
this.isUpdating = false;
console.error(err);

View File

@ -1,32 +1,31 @@
<div class="dialog">
<div class="dialog-header">
<h4>{{'Imgur Configuration' | translate}}</h4>
</div>
<div class="dialog-content" *ngIf="isLoading">
<my-spinner></my-spinner>
</div>
<div class="dialog-content" *ngIf="!isLoading">
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="your_client_id"
[(ngModel)]="config.client_id" [disabled]="isUpdating"/>
</label>
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="your_client_secret"
[(ngModel)]="config.client_secret" [disabled]="isUpdating"/>
</label>
</div>
<div class="dialog-footer" *ngIf="!isLoading">
<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)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'Imgur Configuration' | translate}}</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div>
<div class="modal-body" *ngIf="isLoading">
<my-spinner></my-spinner>
</div>
<div class="modal-body" *ngIf="!isLoading">
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="your_client_id"
[(ngModel)]="config.client_id" [disabled]="isUpdating"/>
</label>
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="your_client_secret"
[(ngModel)]="config.client_secret" [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,11 +1,10 @@
import { Component, OnInit } from "@angular/core";
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 { FE_NebConfiguration } from "../../../../shared/models/admin-responses";
import { FE_Integration } from "../../../../shared/models/integration";
import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
interface ImgurConfig {
client_id: string;
@ -16,7 +15,7 @@ interface ImgurConfig {
templateUrl: "./imgur.component.html",
styleUrls: ["./imgur.component.scss", "../config-dialog.scss"],
})
export class AdminNebImgurConfigComponent implements ModalComponent<NebBotConfigurationDialogContext>, OnInit {
export class AdminNebImgurConfigComponent implements OnInit {
public isLoading = true;
public isUpdating = false;
@ -24,13 +23,11 @@ export class AdminNebImgurConfigComponent implements ModalComponent<NebBotConfig
public integration: FE_Integration;
public neb: FE_NebConfiguration;
constructor(public dialog: DialogRef<NebBotConfigurationDialogContext>,
constructor(public modal: NgbActiveModal,
private adminNebApi: AdminNebApiService,
private toaster: ToasterService,
public translate: TranslateService) {
this.translate = translate;
this.neb = dialog.context.neb;
this.integration = dialog.context.integration;
}
public ngOnInit() {
@ -47,7 +44,7 @@ export class AdminNebImgurConfigComponent implements ModalComponent<NebBotConfig
this.isUpdating = true;
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.dialog.close();
this.modal.close();
}).catch(err => {
this.isUpdating = false;
console.error(err);

View File

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

View File

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

View File

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

View File

@ -1,10 +1,9 @@
import { Component } from "@angular/core";
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 { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
export class StickerPackPreviewDialogContext extends BSModalContext {
export class StickerPackPreviewMoadlInstance {
public pack: FE_StickerPack;
}
@ -12,12 +11,12 @@ export class StickerPackPreviewDialogContext extends BSModalContext {
templateUrl: "./preview.component.html",
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 {

View File

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

View File

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

View File

@ -1,20 +1,19 @@
<div class="dialog">
<div class="dialog-header">
<h4>{{'Publish policy' | translate}}</h4>
</div>
<div class="dialog-content">
<label class="label-block">
{{'Version number' | translate}}
<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"/>
</label>
</div>
<div class="dialog-footer">
<button type="button" (click)="publish()" title="close" class="btn btn-primary btn-sm">
<i class="fas fa-upload"></i> {{'Publish' | translate}}
</button>
<button type="button" (click)="dialog.close()" title="save" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'Publish policy' | translate}}</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div>
<div class="modal-body">
<label class="label-block">
{{'Version number' | translate}}
<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"/>
</label>
</div>
<div class="modal-footer">
<button type="button" (click)="publish()" title="close" class="btn btn-primary btn-sm">
<i class="fas fa-upload"></i> {{'Publish' | translate}}
</button>
<button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}}
</button>
</div>

View File

@ -1,21 +1,20 @@
import { Component } from "@angular/core";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { ToasterService } from "angular2-toaster";
import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
export class AdminTermsNewEditPublishDialogContext extends BSModalContext {
export interface AdminTermsNewEditPublishDialogContext {
}
@Component({
templateUrl: "./publish.component.html",
styleUrls: ["./publish.component.scss"],
})
export class AdminTermsNewEditPublishDialogComponent implements ModalComponent<AdminTermsNewEditPublishDialogContext> {
export class AdminTermsNewEditPublishDialogComponent {
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() {
@ -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); });
return;
}
this.dialog.close(this.version);
this.modal.close(this.version);
}
}

View File

@ -1,22 +1,21 @@
<div class="dialog">
<div class="dialog-header">
<h4>{{'Etherpad Widget Configuration' | translate}}</h4>
</div>
<div class="dialog-content">
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="https://scalar.vector.im/etherpad/p/$padName_$roomId"
[(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' | translate}}
</button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'Etherpad Widget Configuration' | translate}}</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div>
<div class="modal-body">
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="https://scalar.vector.im/etherpad/p/$padName_$roomId"
[(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,27 +1,29 @@
import { Component } from "@angular/core";
import { Component, OnInit } from "@angular/core";
import { FE_EtherpadWidget } from "../../../shared/models/integration";
import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { WidgetConfigDialogContext } from "../widgets.component";
import { AdminIntegrationsApiService } from "../../../shared/services/admin/admin-integrations-api.service";
import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
@Component({
templateUrl: "./etherpad.component.html",
styleUrls: ["./etherpad.component.scss", "../config-dialog.scss"],
})
export class AdminWidgetEtherpadConfigComponent implements ModalComponent<WidgetConfigDialogContext> {
export class AdminWidgetEtherpadConfigComponent implements OnInit {
public isUpdating = false;
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 toaster: ToasterService,
public translate: TranslateService) {
this.translate = translate;
this.originalWidget = dialog.context.widget;
}
ngOnInit() {
this.originalWidget = this.widget;
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.originalWidget.options = this.widget.options;
this.translate.get('Widget updated').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close();
this.modal.close();
}).catch(err => {
this.isUpdating = false;
console.error(err);

View File

@ -1,35 +1,34 @@
<div class="dialog">
<div class="dialog-header">
<h4>{{'Jitsi Widget Configuration' | translate}}</h4>
</div>
<div class="dialog-content">
<label class="label-block">
{{'Jitsi Domain' | translate}}
<span class="text-muted ">{{'This is the domain that is used to host the conference.' | translate}}</span>
<input type="text" class="form-control"
placeholder="jitsi.riot.im"
[(ngModel)]="widget.options.jitsiDomain" [disabled]="isUpdating"/>
</label>
<label class="label-block">
{{'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>
<ui-switch [checked]="widget.options.useDomainAsDefault" size="medium" [disabled]="isUpdating"
(change)="toggleForcedJitsi()"></ui-switch>
</label>
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="https://jitsi.riot.im/libs/external_api.min.js"
[(ngModel)]="widget.options.scriptUrl" [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' | translate}}
</button>
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Cancel' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'Jitsi Widget Configuration' | translate}}</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div>
<div class="modal-body">
<label class="label-block">
{{'Jitsi Domain' | translate}}
<span class="text-muted ">{{'This is the domain that is used to host the conference.' | translate}}</span>
<input type="text" class="form-control"
placeholder="jitsi.riot.im"
[(ngModel)]="widget.options.jitsiDomain" [disabled]="isUpdating"/>
</label>
<label class="label-block">
{{'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>
<ui-switch [checked]="widget.options.useDomainAsDefault" size="medium" [disabled]="isUpdating"
(change)="toggleForcedJitsi()"></ui-switch>
</label>
<label class="label-block">
{{'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>
<input type="text" class="form-control"
placeholder="https://jitsi.riot.im/libs/external_api.min.js"
[(ngModel)]="widget.options.scriptUrl" [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,24 +1,29 @@
import { Component } from "@angular/core";
import { Component, OnInit } from "@angular/core";
import { FE_JitsiWidget } from "../../../shared/models/integration";
import { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { WidgetConfigDialogContext } from "../widgets.component";
import { AdminIntegrationsApiService } from "../../../shared/services/admin/admin-integrations-api.service";
import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
@Component({
templateUrl: "./jitsi.component.html",
styleUrls: ["./jitsi.component.scss", "../config-dialog.scss"],
})
export class AdminWidgetJitsiConfigComponent implements ModalComponent<WidgetConfigDialogContext> {
export class AdminWidgetJitsiConfigComponent implements OnInit {
public isUpdating = false;
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) {
this.translate = translate;
this.originalWidget = dialog.context.widget;
constructor(public modal: NgbActiveModal,
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));
// 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.originalWidget.options = this.widget.options;
this.translate.get('Widget updated').subscribe((res: string) => {this.toaster.pop("success", res); });
this.dialog.close();
this.modal.close();
}).catch(err => {
this.isUpdating = false;
console.error(err);

View File

@ -1,22 +1,20 @@
<div class="dialog">
<div class="dialog-header">
<h4>Whiteboard Widget Configuration</h4>
</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 class="modal-header">
<h4 class="modal-title" id="modal-basic-title">Whiteboard Widget Configuration</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</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 { ToasterService } from "angular2-toaster";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { WidgetConfigDialogContext } from "../widgets.component";
import { TranslateService } from "@ngx-translate/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { AdminIntegrationsApiService } from "../../../shared/services/admin/admin-integrations-api.service";
@Component({
templateUrl: "./whiteboard.component.html",
styleUrls: ["./whiteboard.component.scss", "../config-dialog.scss"],
})
export class AdminWidgetWhiteboardConfigComponent implements ModalComponent<WidgetConfigDialogContext> {
export class AdminWidgetWhiteboardConfigComponent implements OnInit {
public isUpdating = false;
public widget: FE_WhiteBoardWidget;
private originalWidget: FE_WhiteBoardWidget;
constructor(public dialog: DialogRef<WidgetConfigDialogContext>, private adminIntegrationsApi: AdminIntegrationsApiService, private toaster: ToasterService) {
this.originalWidget = dialog.context.widget;
constructor(public modal: NgbActiveModal,
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));
}
@ -25,7 +32,7 @@ export class AdminWidgetWhiteboardConfigComponent implements ModalComponent<Widg
this.adminIntegrationsApi.setIntegrationOptions(this.widget.category, this.widget.type, this.widget.options).then(() => {
this.originalWidget.options = this.widget.options;
this.toaster.pop("success", "Widget updated");
this.dialog.close();
this.modal.close();
}).catch(err => {
this.isUpdating = false;
console.error(err);

View File

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

View File

@ -1,6 +1,4 @@
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 { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { UiSwitchModule } from "ngx-ui-switch";
@ -137,9 +135,7 @@ export function HttpLoaderFactory(http: HttpClient) {
UiSwitchModule,
ToasterModule,
BrowserAnimationsModule,
ModalModule.forRoot(),
BootstrapModalModule,
BreadcrumbsModule,
BreadcrumbModule,
CKEditorModule,
FontAwesomeModule,
TranslateModule.forRoot({

View File

@ -1,17 +1,15 @@
<div class="dialog">
<div class="dialog-header">
<h4>{{'Telegram chat is already bridged' | translate}}</h4>
</div>
<div class="dialog-content">
{{'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 class="dialog-footer">
<button type="button" (click)="unbridgeAndContinue()" title="unbridge and continue"
class="btn btn-danger btn-sm">
{{'Unbridge and continue' | translate}}
</button>
<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}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'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 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}}
</div>
<div class="modal-footer">
<button type="button" (click)="unbridgeAndContinue()" title="unbridge and continue" class="btn btn-danger btn-sm">
{{'Unbridge and continue' | translate}}
</button>
<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}}
</button>
</div>

View File

@ -1,24 +1,23 @@
import { Component } from "@angular/core";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
export class AskUnbridgeDialogContext extends BSModalContext {
export interface AskUnbridgeDialogContext {
}
@Component({
templateUrl: "./ask-unbridge.component.html",
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 {
this.dialog.close({unbridge: true});
this.modal.close({unbridge: true});
}
public cancel(): void {
this.dialog.close({unbridge: false});
this.modal.close({unbridge: false});
}
}

View File

@ -1,13 +1,12 @@
<div class="dialog">
<div class="dialog-header">
<h4>{{'Telegram chat is already bridged' | translate}}</h4>
</div>
<div class="dialog-content">
{{'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 class="dialog-footer">
<button type="button" (click)="dialog.close()" title="cancel" class="btn btn-primary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{'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 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}}
</div>
<div class="modal-footer">
<button type="button" (click)="modal.close()" title="close" class="btn btn-primary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}}
</button>
</div>

View File

@ -1,16 +1,15 @@
import { Component } from "@angular/core";
import { DialogRef, ModalComponent } from "ngx-modialog";
import { BSModalContext } from "ngx-modialog/plugins/bootstrap";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
export class CannotUnbridgeDialogContext extends BSModalContext {
export interface CannotUnbridgeDialogContext {
}
@Component({
templateUrl: "./cannot-unbridge.component.html",
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 { TelegramApiService } from "../../../shared/services/integrations/telegram-api.service";
import { FE_PortalInfo } from "../../../shared/models/telegram";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { AskUnbridgeDialogContext, TelegramAskUnbridgeComponent } from "./ask-unbridge/ask-unbridge.component";
import { TelegramAskUnbridgeComponent } from "./ask-unbridge/ask-unbridge.component";
import {
CannotUnbridgeDialogContext,
TelegramCannotUnbridgeComponent
} from "./cannot-unbridge/cannot-unbridge.component";
import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
interface TelegramConfig {
puppet: {
@ -40,7 +39,7 @@ export class TelegramBridgeConfigComponent extends BridgeComponent<TelegramConfi
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);
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) => {
let forceUnbridge = false;
if (chatInfo.bridged && chatInfo.canUnbridge) {
const response = await this.modal.open(TelegramAskUnbridgeComponent, overlayConfigFactory({
isBlocking: true,
const askUnbridgeRef = this.modal.open(TelegramAskUnbridgeComponent, {
backdrop: 'static',
size: 'lg',
}, AskUnbridgeDialogContext)).result;
if (response.unbridge) {
forceUnbridge = true;
} else {
return {aborted: true};
}
});
askUnbridgeRef.result.then((response) => {
if (response.unbridge) {
forceUnbridge = true;
} else {
return {aborted: true};
}
});
} else if (chatInfo.bridged) {
this.modal.open(TelegramCannotUnbridgeComponent, overlayConfigFactory({
isBlocking: true,
this.modal.open(TelegramCannotUnbridgeComponent, {
backdrop: 'static',
size: 'lg',
}, CannotUnbridgeDialogContext));
});
return {aborted: true};
}

View File

@ -1,14 +1,13 @@
<div class="dialog">
<div class="dialog-header">
<h4>{{ bot.displayName }}</h4>
</div>
<div class="dialog-content" style="text-align: center;">
<p>{{ bot.description }}</p>
<ui-switch [checked]="bot._inRoom" [disabled]="bot._isUpdating" (change)="toggle()"></ui-switch>
</div>
<div class="dialog-footer">
<button type="button" (click)="dialog.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}}
</button>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{ bot.displayName }}</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" (click)="modal.close()"></button>
</div>
<div class="modal-body">
<p>{{ bot.description }}</p>
<ui-switch [checked]="bot._inRoom" [disabled]="bot._isUpdating" (change)="toggle()"></ui-switch>
</div>
<div class="modal-footer">
<button type="button" (click)="modal.close()" title="close" class="btn btn-secondary btn-sm">
<i class="far fa-times-circle"></i> {{'Close' | translate}}
</button>
</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 { ToasterService } from "angular2-toaster";
import { ScalarClientApiService } from "../../shared/services/scalar/scalar-client-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 { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
export class SimpleBotConfigDialogContext extends BSModalContext {
public bot: FE_SimpleBot;
public roomId: string;
export interface SimpleBotConfigDialogContext {
bot: FE_SimpleBot;
roomId: string;
}
@Component({
templateUrl: "simple-bot.component.html",
styleUrls: ["simple-bot.component.scss"],
})
export class ConfigSimpleBotComponent {
export class ConfigSimpleBotComponent implements OnInit {
public bot: FE_SimpleBot;
private roomId: string;
constructor(public dialog: DialogRef<SimpleBotConfigDialogContext>,
constructor(public modal: NgbActiveModal,
private toaster: ToasterService,
private scalar: ScalarClientApiService,
private integrationsApi: IntegrationsApiService,
public translate: TranslateService) {
this.translate = translate;
this.bot = dialog.context.bot;
this.roomId = dialog.context.roomId;
}
ngOnInit() {
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 * as _ from "lodash";
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 { SessionStorage } from "../../shared/SessionStorage";
import { AdminApiService } from "../../shared/services/admin/admin-api.service";
import { IntegrationsApiService } from "../../shared/services/integrations/integrations-api.service";
import { Modal, overlayConfigFactory } from "ngx-modialog";
import { ConfigSimpleBotComponent, SimpleBotConfigDialogContext } from "../../configs/simple-bot/simple-bot.component";
import {
ConfigSimpleBotComponent,
SimpleBotConfigDialogContext,
} from "../../configs/simple-bot/simple-bot.component";
import { ToasterService } from "angular2-toaster";
import { StickerApiService } from "../../shared/services/integrations/sticker-api.service";
import { TranslateService } from "@ngx-translate/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
const CATEGORY_MAP = {
"Widgets": ["widget"],
"Bots": ["complex-bot", "bot"],
"Bridges": ["bridge"],
Widgets: ["widget"],
Bots: ["complex-bot", "bot"],
Bridges: ["bridge"],
};
@Component({
selector: "my-riot-home",
templateUrl: "./home.component.html",
styleUrls: ["./home.component.scss"],
selector: "my-riot-home",
templateUrl: "./home.component.html",
styleUrls: ["./home.component.scss"],
})
export class RiotHomeComponent {
public isLoading = true;
public isError = false;
public errorMessage: string;
public isRoomEncrypted: boolean;
public hasStickerPacks = false;
public isLoading = true;
public isError = false;
public errorMessage: string;
public isRoomEncrypted: boolean;
public hasStickerPacks = false;
private roomId: string;
private userId: string;
private requestedScreen: string = null;
private requestedIntegrationId: string = null;
public integrationsForCategory: { [category: string]: FE_Integration[] } = {};
private categoryMap: { [categoryName: string]: string[] } = CATEGORY_MAP;
private roomId: string;
private userId: string;
private requestedScreen: string = null;
private requestedIntegrationId: string = null;
public integrationsForCategory: { [category: string]: FE_Integration[] } = {};
private categoryMap: { [categoryName: string]: string[] } = CATEGORY_MAP;
constructor(private activatedRoute: ActivatedRoute,
private scalarApi: ScalarServerApiService,
private scalar: ScalarClientApiService,
private integrationsApi: IntegrationsApiService,
private stickerApi: StickerApiService,
private adminApi: AdminApiService,
private router: Router,
private modal: Modal,
private toaster: ToasterService,
public translate: TranslateService) {
this.translate = translate;
let params: any = this.activatedRoute.snapshot.queryParams;
constructor(
private activatedRoute: ActivatedRoute,
private scalarApi: ScalarServerApiService,
private scalar: ScalarClientApiService,
private integrationsApi: IntegrationsApiService,
private stickerApi: StickerApiService,
private adminApi: AdminApiService,
private router: Router,
private modal: NgbModal,
private toaster: ToasterService,
public translate: TranslateService
) {
this.translate = translate;
let params: any = this.activatedRoute.snapshot.queryParams;
this.requestedScreen = params.screen;
this.requestedIntegrationId = params.integ_id;
this.requestedScreen = params.screen;
this.requestedIntegrationId = params.integ_id;
if (SessionStorage.roomId && SessionStorage.userId) {
this.roomId = SessionStorage.roomId;
this.userId = SessionStorage.userId;
console.log("Already checked scalar token and other params - continuing startup");
if (SessionStorage.roomId && SessionStorage.userId) {
this.roomId = SessionStorage.roomId;
this.userId = SessionStorage.userId;
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();
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();
}
}).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});
}
})
.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() {
this.adminApi.isAdmin().then(() => {
console.log(SessionStorage.userId + " is an admin for this Dimension instance");
SessionStorage.isAdmin = true;
}).catch(() => SessionStorage.isAdmin = false);
return false;
}
public getCategories(): string[] {
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 {
for (const category of this.getCategories()) {
if (this.getIntegrationsIn(category).length > 0) return true;
}
return result;
}
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[] {
return Object.keys(this.categoryMap);
SessionStorage.editIntegration = integration;
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[] {
return this.integrationsForCategory[category];
}
private prepareIntegrations() {
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[] {
const result: FE_Integration[] = [];
for (const category of this.getCategories()) {
for (const integration of this.getIntegrationsIn(category)) {
result.push(integration);
// Flag integrations that aren't supported in encrypted rooms
if (this.isRoomEncrypted) {
for (const integration of supportedIntegrations) {
if (!integration.isEncryptionSupported) {
integration._isSupported = false;
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) {
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;
}
const targetIntegration = IntegrationsRegistry.getIntegrationForScreen(
this.requestedScreen
);
if (targetIntegration) {
category = targetIntegration.category;
type = targetIntegration.type;
} else {
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.editsRequested++;
console.log(this.userId + " is trying to modify " + integration.displayName);
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}});
}
SessionStorage.editIntegrationId = this.requestedIntegrationId;
this.modifyIntegration(integration);
return;
}
}
private prepareIntegrations() {
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));
console.log(
"Failed to find integration component for category=" +
category +
" type=" +
type
);
}
// Flag integrations that aren't supported in encrypted rooms
if (this.isRoomEncrypted) {
for (const integration of supportedIntegrations) {
if (!integration.isEncryptionSupported) {
integration._isSupported = false;
integration._notSupportedReason = "This integration is not supported in encrypted rooms";
}
}
}
// 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});
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;
}
this.stickerApi.getPacks().then(packs => {
this.hasStickerPacks = packs.length > 0;
}).catch(err => {
console.error(err);
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":
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 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;
}
const targetIntegration = IntegrationsRegistry.getIntegrationForScreen(this.requestedScreen);
if (targetIntegration) {
category = targetIntegration.category;
type = targetIntegration.type;
} else {
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.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();
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;
});
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);
};
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);
}
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;
});
case "userInRoom":
// TODO: Implement
default:
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);
}
return Promise.reject(message);
}
});
case "userInRoom":
// TODO: Implement
default:
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
@import "../assets//fonts/opensans100-roboto300";
@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 "themes/themes";
@import "components/ibox";