added jitsi api + improvements

This commit is contained in:
_Bastler
2021-04-16 08:57:53 +02:00
parent 56362da724
commit ffe9e6f5ab
31 changed files with 4682 additions and 3202 deletions
+94
View File
@@ -0,0 +1,94 @@
<h3>{{'jitsi.rooms' | i18n}}</h3>
<table mat-table matSort [dataSource]="jitsiRooms" (matSortChange)="sortData($event)">
<ng-container matColumnDef="room">
<th mat-header-cell *matHeaderCellDef mat-sort-header="room"> {{'jitsi.rooms.room' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom">
<a mat-button color="accent" href="{{ jitsiRoom.url }}" target="_blank">{{ jitsiRoom.room }}<mat-icon
style="font-size: 1em;">open_in_new
</mat-icon></a> <button mat-icon-button (click)="share(jitsiRoom)">
<mat-icon>share</mat-icon>
</button>
</td>
</ng-container>
<ng-container matColumnDef="starts">
<th mat-header-cell *matHeaderCellDef mat-sort-header="starts"> {{'jitsi.rooms.starts' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom">{{ jitsiRoom.starts | date:datetimeformat}} </td>
</ng-container>
<ng-container matColumnDef="expires">
<th mat-header-cell *matHeaderCellDef mat-sort-header="expires"> {{'jitsi.rooms.expires' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom"> {{ jitsiRoom.expires | date:datetimeformat}} </td>
</ng-container>
<ng-container matColumnDef="moderationUrl">
<th mat-header-cell *matHeaderCellDef mat-sort-header="moderationUrl"> {{'jitsi.rooms.moderationUrl' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom">
<a mat-button color="primary" class="url" href="{{ jitsiRoom.moderationUrl}}" target="_blank">{{
jitsiRoom.moderationUrl }}
<mat-icon style="font-size: 1em;">open_in_new
</mat-icon>
</a>
</td>
</ng-container>
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'jitsi.rooms.delete' | i18n}} </th>
<td mat-cell *matCellDef="let jitsiRoom" class="text-right">
<a mat-icon-button>
<mat-icon (click)="confirmDelete(jitsiRoom)">delete</mat-icon>
</a>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="jitsiRoomsColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: jitsiRoomsColumns"></tr>
</table>
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm">
<mat-card>
<mat-card-content>
<p>{{'jitsi.rooms.info' | i18n}}</p>
<p *ngIf="!jitsiRoomsQuota">{{'jitsi.rooms.noQuota' | i18n}}</p>
<div *ngIf="jitsiRoomsQuota">
<p>{{'jitsi.rooms.left' | i18n:jitsiRoomsQuota}}</p>
<mat-form-field>
<input matInput placeholder="{{'jitsi.rooms.room' | i18n}}" formControlName="room"
[(ngModel)]="jitsiRoom.room" required pattern="[a-zA-Z0-9]+">
<mat-error>
{{'jitsi.rooms.error.room' | i18n}}
</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="jitsiRoom.expires"
formControlName="expires" placeholder="{{'jitsi.rooms.expires' | i18n}}" required>
<mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker>
<mat-error>
{{'jitsi.rooms.error.expires' | i18n}}
</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput [ngxMatDatetimePicker]="startsPicker" [(ngModel)]="jitsiRoom.starts" formControlName="starts"
placeholder="{{'jitsi.rooms.starts' | i18n}}">
<mat-datepicker-toggle matSuffix [for]="startsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker>
<mat-error>
{{'jitsi.rooms.error.starts' | i18n}}
</mat-error>
</mat-form-field>
</div>
</mat-card-content>
<mat-card-actions>
<button *ngIf="jitsiRoomsQuota && !working" mat-raised-button color="primary" [disabled]="form.invalid">
{{'jitsi.rooms.create' | i18n}}
</button>
</mat-card-actions>
</mat-card>
</form>
+25
View File
@@ -0,0 +1,25 @@
mat-form-field {
display: block;
}
.mat-header-cell,
.mat-cell {
&.text-right {
text-align: right;
}
}
.align-right{
display: flex;
padding: 21px 0;
justify-content: flex-end;
}
.url {
display: block;
width: 200px;
max-width: 200px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { JitsiComponent } from './jitsi.component';
describe('JitsiComponent', () => {
let component: JitsiComponent;
let fixture: ComponentFixture<JitsiComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ JitsiComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(JitsiComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
+165
View File
@@ -0,0 +1,165 @@
import {Component, OnInit, ViewChild, Inject} from '@angular/core';
import {MatSnackBar} from '@angular/material/snack-bar';
import {Sort} from '@angular/material/sort';
import {FormBuilder, FormGroup, Validators, NgForm} from '@angular/forms';
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
import {QuotaService} from '../../services/quota.service';
import {JitsiService} from '../../services/jitsi.service';
import {ConfirmDialog} from '../../ui/confirm/confirm.component';
import {I18nService} from './../../services/i18n.service';
@Component({
selector: 'app-account-jitsi',
templateUrl: './jitsi.component.html',
styleUrls: ['./jitsi.component.scss']
})
export class JitsiComponent implements OnInit {
form: FormGroup;
@ViewChild('formDirective') private formDirective: NgForm;
jitsiRoomsQuota: number = 0;
jitsiRooms: any[] = [];
jitsiRoom: any = {};
success: boolean;
working: boolean;
datetimeformat: String;
jitsiRoomsColumns = ["room", "starts", "expires", "moderationUrl", "delete"];
constructor(
private quotaService: QuotaService,
private formBuilder: FormBuilder,
private jitsiService: JitsiService,
private i18n: I18nService,
public dialog: MatDialog) {}
ngOnInit(): void {
this.datetimeformat = this.i18n.get('format.datetime', []);
this.form = this.formBuilder.group({
room: ['', Validators.required],
starts: ['', Validators.nullValidator],
expires: ['', Validators.required],
});
this.update();
}
create(): void {
this.working = true;
this.jitsiService.create(this.jitsiRoom).subscribe(response => {
this.update();
this.formDirective.resetForm();
this.jitsiRoom = {};
this.working = false;
}, (error) => {
this.working = false;
if(error.status == 409) {
let errors = {};
for(let code of error.error) {
errors[code.field] = errors[code.field] || {};
errors[code.field][code.code] = true;
}
for(let code in errors) {
this.form.get(code).setErrors(errors[code]);
}
}
})
}
update() {
this.jitsiRoomsQuota = 0;
this.quotaService.quotas().subscribe((data: any) => {
for(let quota of data) {
if(quota.name == "jitsi") {
this.jitsiRoomsQuota = quota.value;
}
}
})
this.jitsiService.get().subscribe((data: any) => {
this.jitsiRooms = data;
})
}
confirmDelete(jitsiRoom) {
const dialogRef = this.dialog.open(ConfirmDialog, {
data: {
'label': 'jitsi.rooms.confirmDelete',
'args': [jitsiRoom.room]
}
})
dialogRef.afterClosed().subscribe(result => {
if(result) {
this.jitsiService.delete(jitsiRoom.id).subscribe((result: any) => {
this.update();
})
}
});
}
sortData(sort: Sort) {
const data = this.jitsiRooms.slice();
if(!sort.active || sort.direction === '') {
this.jitsiRooms = data;
return;
}
this.jitsiRooms = data.sort((a, b) => {
const isAsc = sort.direction === 'asc';
switch(sort.active) {
case 'room': return this.compare(a.room, b.room, isAsc);
case 'starts': return this.compare(a.room, b.room, isAsc);
case 'expires': return this.compare(a.room, b.room, isAsc);
default: return 0;
}
});
}
compare(a: number | string | String, b: number | string | String, isAsc: boolean) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
share(jitsiRoom) {
const dialogRef = this.dialog.open(JitsiShareDialog, {
data: jitsiRoom,
minWidth: '300px',
});
}
}
@Component({
selector: 'app-jitsi-share-dialog',
templateUrl: 'jitsi.share.html',
styleUrls: ['./jitsi.share.scss']
})
export class JitsiShareDialog {
jitsiRoom: any;
constructor(
private i18n: I18nService,
private snackBar: MatSnackBar,
public dialogRef: MatDialogRef<JitsiShareDialog>,
@Inject(MAT_DIALOG_DATA) public data: any) {
this.jitsiRoom = data;
}
copyToClipboard(jitsiRoom: any) {
const selBox = document.createElement('textarea');
selBox.value = jitsiRoom.url;
document.body.appendChild(selBox);
selBox.focus();
selBox.select();
document.execCommand('copy');
document.body.removeChild(selBox);
this.snackBar.open(this.i18n.get("jitsi.share.clipboard.copied", []), this.i18n.get("close", []), {
duration: 3000
});
}
}
+20
View File
@@ -0,0 +1,20 @@
<h1 mat-dialog-title><mat-icon inline="true">share</mat-icon> {{'jitsi.share' | i18n}}</h1>
<div mat-dialog-content>
<mat-list>
<mat-list-item>
<a mat-raised-button color="accent"
href="mailto:?&subject={{'jitsi.share.email.subject' | i18n:jitsiRoom.room}}&body={{jitsiRoom.url}}">
<mat-icon>mail</mat-icon> {{'jitsi.share.email' | i18n}}
</a>
</mat-list-item>
<mat-list-item>
<a mat-raised-button color="accent" (click)="copyToClipboard(jitsiRoom)">
<mat-icon>content_paste</mat-icon> {{'jitsi.share.clipboard' | i18n}}
</a>
</mat-list-item>
</mat-list>
</div>
<div mat-dialog-actions>
<button mat-button mat-dialog-close>{{'close' | i18n}}</button>
</div>
+4
View File
@@ -0,0 +1,4 @@
.mat-raised-button {
display: block;
width: 100%;
}
+15 -10
View File
@@ -1,9 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AuthService } from './../../services/auth.service';
import { Router, ActivatedRoute } from '@angular/router';
import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {AuthService} from './../../services/auth.service';
import {Router, ActivatedRoute} from '@angular/router';
import { environment } from './../../../environments/environment';
import {environment} from './../../../environments/environment';
@Component({
selector: 'app-login',
@@ -18,7 +18,11 @@ export class LoginComponent implements OnInit {
targetRoute = '/services';
loginModel = {};
constructor(private formBuilder: FormBuilder, private authService: AuthService, private router: Router, private route: ActivatedRoute) { }
constructor(
private formBuilder: FormBuilder,
private authService: AuthService,
private router: Router,
private route: ActivatedRoute) {}
async ngOnInit() {
this.form = this.formBuilder.group({
@@ -28,15 +32,16 @@ export class LoginComponent implements OnInit {
});
this.route.queryParams.subscribe(params => {
if (params['target']) {
if(params['target']) {
this.targetRoute = params['target'];
this.router.navigate([], {queryParams: {target: null}, queryParamsHandling: 'merge'});
}
});
}
async login() {
this.loginInvalid = false;
if (this.form.valid) {
if(this.form.valid) {
const loginModel = {
username: this.form.get('username').value,
@@ -47,8 +52,8 @@ export class LoginComponent implements OnInit {
this.authService.login(loginModel).subscribe((response: any) => {
this.router.navigate([this.targetRoute]);
}, error => {
if (error.status == 428) {
this.router.navigate(["/login/totp"], { queryParams: { target: this.targetRoute } });
if(error.status == 428) {
this.router.navigate(["/login/totp"], {queryParams: {target: this.targetRoute}});
} else {
this.loginInvalid = true;
}
@@ -16,8 +16,9 @@
</p>
</mat-card-content>
<mat-card-actions>
<a href="{{service.url}}" target="_blank" mat-raised-button color="primary">{{'services.goto' |
i18n}}</a>
<a href="{{service.url}}" [target]="service.sameSite ? '_self' : '_blank'" mat-raised-button
color="accent">{{'services.goto' | i18n}} <mat-icon *ngIf="!service.sameSite" inline="true">
open_in_new</mat-icon></a>
</mat-card-actions>
</mat-card>
</div>
@@ -1,5 +1,6 @@
import {Component, OnInit} from '@angular/core';
import { Location } from '@angular/common'
import {Location} from '@angular/common'
import {Router, ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-unavailable',
@@ -7,14 +8,29 @@ import { Location } from '@angular/common'
})
export class UnavailableComponent implements OnInit {
targetRoute = '';
constructor(
private location: Location) {}
private location: Location,
private router: Router,
private route: ActivatedRoute) {}
ngOnInit(): void {
this.route.queryParams.subscribe(params => {
if(params['target']) {
this.targetRoute = params['target'];
this.router.navigate([], {queryParams: {target: null}, queryParamsHandling: 'merge'});
}
});
}
retry() {
this.location.back();
if(!this.targetRoute || this.targetRoute === "unavailable" || this.targetRoute === "/unavailable") {
this.location.back;
} else {
this.router.navigate([this.targetRoute]);
}
}
}