added jitsi api + improvements
This commit is contained in:
@@ -20,6 +20,7 @@ import {SecurityComponent} from './pages/account/security/security.component';
|
||||
import {UnavailableComponent} from './pages/unavailable/unavailable.component';
|
||||
import {NotfoundComponent} from './pages/notfound/notfound.component';
|
||||
import {UserComponent} from './pages/user/user.component'
|
||||
import {JitsiComponent} from './pages/jitsi/jitsi.component'
|
||||
import {AliasesComponent} from './pages/account/aliases/aliases.component';
|
||||
import {DomainsComponent} from './pages/account/domains/domains.component';
|
||||
|
||||
@@ -48,13 +49,14 @@ const routes: Routes = [
|
||||
},
|
||||
{path: 'register', component: RegisterComponent, canActivate: [AnonymousGuard]},
|
||||
{path: 'tokens', component: TokensComponent, canActivate: [AuthGuard]},
|
||||
{path: 'jitsi', component: JitsiComponent, canActivate: [AuthenticatedGuard]},
|
||||
{path: 'unavailable', component: UnavailableComponent},
|
||||
{path: 'p/:username', component: UserComponent, canActivate: [AuthUpdateGuard]},
|
||||
{path: '**', component: NotfoundComponent, pathMatch: 'full', canActivate: [AuthUpdateGuard]},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, {onSameUrlNavigation: 'reload'})],
|
||||
imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload', relativeLinkResolution: 'legacy' })],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
|
||||
@@ -19,6 +19,7 @@ export class AppComponent {
|
||||
darkTheme = "false";
|
||||
title = 'we.bstly';
|
||||
currentLocale: String;
|
||||
datetimeformat: String;
|
||||
locales;
|
||||
auth;
|
||||
|
||||
@@ -34,7 +35,7 @@ export class AppComponent {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
this.datetimeformat = this.i18n.get('format.datetime', []);
|
||||
this.currentLocale = this.i18n.getLocale();
|
||||
this.locales = this.i18n.getLocales();
|
||||
this.authService.auth.subscribe(data => {
|
||||
|
||||
@@ -37,6 +37,7 @@ import {NotfoundComponent} from './pages/notfound/notfound.component';
|
||||
import {HtmlComponent} from './utils/html/html.component';
|
||||
import {ConfirmDialog} from './ui/confirm/confirm.component'
|
||||
import {UserComponent} from './pages/user/user.component'
|
||||
import {JitsiComponent, JitsiShareDialog} from './pages/jitsi/jitsi.component'
|
||||
|
||||
|
||||
import {I18nService} from './services/i18n.service';
|
||||
@@ -89,7 +90,8 @@ export class XhrInterceptor implements HttpInterceptor {
|
||||
NotfoundComponent,
|
||||
HtmlComponent,
|
||||
ConfirmDialog,
|
||||
UserComponent
|
||||
UserComponent,
|
||||
JitsiComponent, JitsiShareDialog
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@@ -29,7 +29,7 @@ export class AuthGuard implements CanActivate {
|
||||
return this.authService.getAuth().then(response => {
|
||||
return true;
|
||||
}).catch(function(error) {
|
||||
return that.router.parseUrl('/unavailable');
|
||||
return that.router.parseUrl('/unavailable?target=' + state.url);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -44,11 +44,9 @@ export class AuthenticatedGuard implements CanActivate {
|
||||
const that = this;
|
||||
return this.authService.getAuth().then((data: any) => {
|
||||
if(!data.authenticated) {
|
||||
this.router.navigateByUrl('/login');
|
||||
return false;
|
||||
return that.router.parseUrl('/login?target=' + state.url);
|
||||
}
|
||||
|
||||
|
||||
this.profileService.get(["locale", "darkTheme"]).subscribe((profileFields: any) => {
|
||||
let updateLocale = false;
|
||||
let darktheme = 'false';
|
||||
@@ -76,7 +74,7 @@ export class AuthenticatedGuard implements CanActivate {
|
||||
|
||||
return true;
|
||||
}).catch(function(error) {
|
||||
return that.router.parseUrl('/unavailable');
|
||||
return that.router.parseUrl('/unavailable?target=' + state.url);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -96,7 +94,7 @@ export class AnonymousGuard implements CanActivate {
|
||||
}
|
||||
return true;
|
||||
}).catch(function(error) {
|
||||
return that.router.parseUrl('/unavailable');
|
||||
return that.router.parseUrl('/unavailable?target=' + state.url);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +1,55 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
// Material Form Controls
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSliderModule } from '@angular/material/slider';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import {MatAutocompleteModule} from '@angular/material/autocomplete';
|
||||
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||
import {MatDatepickerModule} from '@angular/material/datepicker';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatRadioModule} from '@angular/material/radio';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatSliderModule} from '@angular/material/slider';
|
||||
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
||||
// Material Navigation
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {MatSidenavModule} from '@angular/material/sidenav';
|
||||
import {MatToolbarModule} from '@angular/material/toolbar';
|
||||
// Material Layout
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatGridListModule } from '@angular/material/grid-list';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatStepperModule } from '@angular/material/stepper';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatTreeModule } from '@angular/material/tree';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatDividerModule} from '@angular/material/divider';
|
||||
import {MatExpansionModule} from '@angular/material/expansion';
|
||||
import {MatGridListModule} from '@angular/material/grid-list';
|
||||
import {MatListModule} from '@angular/material/list';
|
||||
import {MatStepperModule} from '@angular/material/stepper';
|
||||
import {MatTabsModule} from '@angular/material/tabs';
|
||||
import {MatTreeModule} from '@angular/material/tree';
|
||||
// Material Buttons & Indicators
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatButtonToggleModule} from '@angular/material/button-toggle';
|
||||
import {MatBadgeModule} from '@angular/material/badge';
|
||||
import {MatChipsModule} from '@angular/material/chips';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
|
||||
import {MatProgressBarModule} from '@angular/material/progress-bar';
|
||||
import {MatRippleModule} from '@angular/material/core';
|
||||
// Material Popups & Modals
|
||||
import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import {MatBottomSheetModule} from '@angular/material/bottom-sheet';
|
||||
import {MatDialogModule} from '@angular/material/dialog';
|
||||
import {MatSnackBarModule} from '@angular/material/snack-bar';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
// Material Data tables
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatMomentDateModule } from '@angular/material-moment-adapter';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import {MatPaginatorModule} from '@angular/material/paginator';
|
||||
import {MatSortModule} from '@angular/material/sort';
|
||||
import {MatTableModule} from '@angular/material/table';
|
||||
import {MatMomentDateModule} from '@angular/material-moment-adapter';
|
||||
import {FlexLayoutModule} from '@angular/flex-layout';
|
||||
|
||||
import {
|
||||
NgxMatDatetimePickerModule,
|
||||
NgxMatNativeDateModule,
|
||||
NgxMatTimepickerModule
|
||||
} from '@angular-material-components/datetime-picker';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
@@ -85,7 +91,10 @@ import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
MatSortModule,
|
||||
MatTableModule,
|
||||
MatMomentDateModule,
|
||||
FlexLayoutModule
|
||||
FlexLayoutModule,
|
||||
NgxMatDatetimePickerModule,
|
||||
NgxMatNativeDateModule,
|
||||
NgxMatTimepickerModule
|
||||
],
|
||||
exports: [
|
||||
MatAutocompleteModule,
|
||||
@@ -123,7 +132,10 @@ import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
MatPaginatorModule,
|
||||
MatSortModule,
|
||||
MatTableModule,
|
||||
FlexLayoutModule
|
||||
FlexLayoutModule,
|
||||
NgxMatDatetimePickerModule,
|
||||
NgxMatNativeDateModule,
|
||||
NgxMatTimepickerModule
|
||||
]
|
||||
})
|
||||
export class MaterialModule { }
|
||||
export class MaterialModule {}
|
||||
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
@@ -0,0 +1,4 @@
|
||||
.mat-raised-button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -71,8 +71,11 @@ export class I18nService {
|
||||
if(!from) {
|
||||
return key;
|
||||
} else if(from[key]) {
|
||||
if(from[key]["."]) {
|
||||
return this.insertArguments(from[key]["."], args);
|
||||
if(typeof from[key] === 'object') {
|
||||
if(from[key]["."]) {
|
||||
return this.insertArguments(from[key]["."], args);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
return this.insertArguments(from[key], args);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
|
||||
import {environment} from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class JitsiService {
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
}
|
||||
|
||||
get() {
|
||||
return this.http.get(environment.apiUrl + "/jitsi/rooms");
|
||||
}
|
||||
|
||||
create(jitsiRoom) {
|
||||
return this.http.post(environment.apiUrl + "/jitsi/rooms", jitsiRoom);
|
||||
}
|
||||
|
||||
delete(id) {
|
||||
return this.http.delete(environment.apiUrl + "/jitsi/rooms/" + id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export class PermissionsComponent implements OnInit {
|
||||
constructor(private i18n: I18nService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.datetimeformat = this.i18n.get('date-time-format', []);
|
||||
this.datetimeformat = this.i18n.get('format.datetime', []);
|
||||
}
|
||||
|
||||
sortData(sort: Sort) {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<mat-form-field>
|
||||
<input matInput type="text" min="3" [(ngModel)]="profileField.name" formControlName="name"
|
||||
placeholder="{{'profileField.name' | i18n}}">
|
||||
<mat-error>
|
||||
{{'profileField.error.name' | i18n}}
|
||||
</mat-error>
|
||||
<mat-error>
|
||||
{{'profileField.error.name' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
@@ -29,27 +29,36 @@
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field *ngSwitchCase="'DATE'">
|
||||
<input matInput [matDatepicker]="picker" [(ngModel)]="profileField.value" formControlName="value"
|
||||
<input matInput [matDatepicker]="datePicker" [(ngModel)]="profileField.value" formControlName="value"
|
||||
placeholder="{{'profileField.value' | i18n}}">
|
||||
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker></mat-datepicker>
|
||||
<mat-datepicker-toggle matSuffix [for]="datePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #datePicker></mat-datepicker>
|
||||
<mat-error>
|
||||
{{'profileField.error.DATE' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field *ngSwitchCase="'DATETIME'">
|
||||
<input matInput [ngxMatDatetimePicker]="datetimePicker" [(ngModel)]="profileField.value" formControlName="value"
|
||||
placeholder="{{'profileField.value' | i18n}}">
|
||||
<mat-datepicker-toggle matSuffix [for]="datetimePicker"></mat-datepicker-toggle>
|
||||
<ngx-mat-datetime-picker #datetimePicker></ngx-mat-datetime-picker>
|
||||
<mat-error>
|
||||
{{'profileField.error.DATETIME' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field *ngSwitchCase="'URL'">
|
||||
<input matInput type="url" [(ngModel)]="profileField.value" formControlName="value"
|
||||
placeholder="{{'profileField.value' | i18n}}">
|
||||
<mat-error>
|
||||
{{'profileField.error.URL' | i18n}}
|
||||
</mat-error>
|
||||
<mat-error>
|
||||
{{'profileField.error.URL' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field *ngSwitchCase="'EMAIL'">
|
||||
<input matInput type="email" [(ngModel)]="profileField.value" formControlName="value"
|
||||
placeholder="{{'profileField.value' | i18n}}">
|
||||
<mat-error>
|
||||
{{'profileField.error.EMAIL' | i18n}}
|
||||
</mat-error>
|
||||
<mat-error>
|
||||
{{'profileField.error.EMAIL' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-slide-toggle *ngSwitchCase="'BOOL'" (change)="booleanChange(profileField)"
|
||||
[checked]="profileField.value == 'true'">
|
||||
@@ -58,16 +67,16 @@
|
||||
<mat-form-field *ngSwitchCase="'NUMBER'">
|
||||
<input matInput type="number" [(ngModel)]="profileField.value" formControlName="value"
|
||||
placeholder="{{'profileField.value' | i18n}}">
|
||||
<mat-error>
|
||||
{{'profileField.error.NUMBER' | i18n}}
|
||||
</mat-error>
|
||||
<mat-error>
|
||||
{{'profileField.error.NUMBER' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field *ngSwitchCase="'BLOB'">
|
||||
<textarea matInput [(ngModel)]="profileField.blob" formControlName="blob"
|
||||
placeholder="{{'profileField.value' | i18n}}"></textarea>
|
||||
<mat-error>
|
||||
{{'profileField.error.BLOB' | i18n}}
|
||||
</mat-error>
|
||||
<mat-error>
|
||||
{{'profileField.error.BLOB' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@@ -81,7 +90,8 @@
|
||||
</mat-option>
|
||||
|
||||
<mat-select-trigger>
|
||||
<mat-icon inline="true">{{'visibility.' + profileField.visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' +
|
||||
<mat-icon inline="true">{{'visibility.' + profileField.visibility + '.icon' | i18n}}</mat-icon>
|
||||
{{'visibility.' +
|
||||
profileField.visibility | i18n}}
|
||||
</mat-select-trigger>
|
||||
</mat-select>
|
||||
@@ -93,9 +103,9 @@
|
||||
<mat-form-field>
|
||||
<input matInput type="number" min="0" [(ngModel)]="profileField.index" formControlName="index"
|
||||
placeholder="{{'profileField.index' | i18n}}">
|
||||
<mat-error>
|
||||
{{'profileField.error.index' | i18n}}
|
||||
</mat-error>
|
||||
<mat-error>
|
||||
{{'profileField.error.index' | i18n}}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</mat-dialog-content>
|
||||
|
||||
@@ -11,11 +11,14 @@
|
||||
<td mat-cell *matCellDef="let profileField">
|
||||
<div [ngSwitch]="profileField.type">
|
||||
<span *ngSwitchCase="'TEXT'">{{profileField.value}}</span>
|
||||
<span *ngSwitchCase="'DATE'">{{profileField.value | date:datetimeformat}}</span>
|
||||
<a *ngSwitchCase="'URL'" href="{{profileField.value}}">{{profileField.value}}</a>
|
||||
<a *ngSwitchCase="'EMAIL'" href="mailto:{{profileField.value}}">{{profileField.value}}</a>
|
||||
<span *ngSwitchCase="'DATE'">{{profileField.value | date:dateformat}}</span>
|
||||
<span *ngSwitchCase="'DATETIME'">{{profileField.value | date:datetimeformat}}</span>
|
||||
<span *ngSwitchCase="'TIME'">{{profileField.value | date:timeformat}}</span>
|
||||
<a *ngSwitchCase="'URL'" class="accent" href="{{profileField.value}}">{{profileField.value}}</a>
|
||||
<a *ngSwitchCase="'EMAIL'" class="accent"
|
||||
href="mailto:{{profileField.value}}">{{profileField.value}}</a>
|
||||
<span *ngSwitchCase="'NUMBER'">{{profileField.value}}</span>
|
||||
<button *ngSwitchCase="'BLOB'" mat-raised-button
|
||||
<button *ngSwitchCase="'BLOB'" mat-raised-buttonu
|
||||
(click)="openBlob(profileField)">{{'profileField.openBlob' | i18n}}</button>
|
||||
<mat-slide-toggle *ngSwitchCase="'BOOL'" [checked]="profileField.value == 'true'" disabled>
|
||||
</mat-slide-toggle>
|
||||
@@ -25,7 +28,9 @@
|
||||
|
||||
<ng-container matColumnDef="visibility" *ngIf="edit">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header="visibility"> {{'visibility' | i18n}} </th>
|
||||
<td mat-cell *matCellDef="let profileField"> <mat-icon inline="true">{{'visibility.' + profileField.visibility + '.icon' | i18n}}</mat-icon> {{'visibility.' + profileField.visibility | i18n}}
|
||||
<td mat-cell *matCellDef="let profileField">
|
||||
<mat-icon inline="true">{{'visibility.' + profileField.visibility + '.icon' | i18n}}</mat-icon>
|
||||
{{'visibility.' + profileField.visibility | i18n}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ export class ProfileFieldsComponent implements OnInit {
|
||||
@Input() edit;
|
||||
profileFieldColumns = ["name", "value"];
|
||||
profileFields: Array<any> = [];
|
||||
datetimeformat: String;
|
||||
dateformat: String;
|
||||
timeformat: String;
|
||||
|
||||
constructor(private i18n: I18nService, private profileService: ProfileService, public dialog: MatDialog) {}
|
||||
|
||||
@@ -27,6 +30,11 @@ export class ProfileFieldsComponent implements OnInit {
|
||||
this.profileFieldColumns.push("delete");
|
||||
}
|
||||
this.update();
|
||||
|
||||
|
||||
this.dateformat = this.i18n.get('format.date', []);
|
||||
this.datetimeformat = this.i18n.get('format.datetime', []);
|
||||
this.timeformat = this.i18n.get('format.time', []);
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +148,7 @@ export class ProfileFieldDialog {
|
||||
form: FormGroup;
|
||||
profileField;
|
||||
|
||||
types = ["TEXT", "NUMBER", "DATE", "URL", "EMAIL", "BOOL", "BLOB"];
|
||||
types = ["TEXT", "NUMBER", "DATE", "DATETIME", "URL", "EMAIL", "BOOL", "BLOB"];
|
||||
visibilities = ["PRIVATE", "PROTECTED", "PUBLIC"];
|
||||
|
||||
constructor(
|
||||
|
||||
Reference in New Issue
Block a user