update partey timeslots + minetest

This commit is contained in:
_Bastler 2021-07-29 17:41:25 +02:00
parent 72178dd7c4
commit a11c74b91b
13 changed files with 723 additions and 3 deletions

View File

@ -19,10 +19,12 @@ import {UnavailableComponent} from './pages/unavailable/unavailable.component';
import {NotfoundComponent} from './pages/notfound/notfound.component'; import {NotfoundComponent} from './pages/notfound/notfound.component';
import {UserComponent} from './pages/user/user.component' import {UserComponent} from './pages/user/user.component'
import {JitsiComponent} from './pages/jitsi/jitsi.component' import {JitsiComponent} from './pages/jitsi/jitsi.component'
import {ParteyTimeslotsComponent} from './pages/partey/timeslots/timeslots.compontent'
import {AliasesComponent} from './pages/account/aliases/aliases.component'; import {AliasesComponent} from './pages/account/aliases/aliases.component';
import {DomainsComponent} from './pages/account/domains/domains.component'; import {DomainsComponent} from './pages/account/domains/domains.component';
import {InvitesComponent} from './pages/invites/invites.component'; import {InvitesComponent} from './pages/invites/invites.component';
import {UrlShortenerComponent, UrlShortenerPasswordComponent} from './pages/urlshortener/urlshortener.component'; import {UrlShortenerComponent, UrlShortenerPasswordComponent} from './pages/urlshortener/urlshortener.component';
import {MinetestAccountsComponent} from './pages/minetest/accounts/accounts.component';
const routes: Routes = [ const routes: Routes = [
{path: '', redirectTo: "/services", pathMatch: 'full'}, {path: '', redirectTo: "/services", pathMatch: 'full'},
@ -50,6 +52,8 @@ const routes: Routes = [
{path: 'register', component: RegisterComponent, canActivate: [AnonymousGuard]}, {path: 'register', component: RegisterComponent, canActivate: [AnonymousGuard]},
{path: 'tokens', component: TokensComponent, canActivate: [AuthGuard]}, {path: 'tokens', component: TokensComponent, canActivate: [AuthGuard]},
{path: 'jitsi', component: JitsiComponent, canActivate: [AuthenticatedGuard]}, {path: 'jitsi', component: JitsiComponent, canActivate: [AuthenticatedGuard]},
{path: 'partey/timeslots', component: ParteyTimeslotsComponent, canActivate: [AuthenticatedGuard]},
{path: 'minetest/accounts', component: MinetestAccountsComponent, canActivate: [AuthenticatedGuard]},
{path: 'urlshortener', component: UrlShortenerComponent, canActivate: [AuthenticatedGuard]}, {path: 'urlshortener', component: UrlShortenerComponent, canActivate: [AuthenticatedGuard]},
{path: 'urlshortener/:code', component: UrlShortenerPasswordComponent, canActivate: [AuthUpdateGuard]}, {path: 'urlshortener/:code', component: UrlShortenerPasswordComponent, canActivate: [AuthUpdateGuard]},
{path: 'invites/:quota', component: InvitesComponent, canActivate: [AuthenticatedGuard]}, {path: 'invites/:quota', component: InvitesComponent, canActivate: [AuthenticatedGuard]},

View File

@ -7,10 +7,10 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {HttpClientModule, HttpInterceptor, HttpHandler, HttpRequest, HTTP_INTERCEPTORS} from '@angular/common/http'; import {HttpClientModule, HttpInterceptor, HttpHandler, HttpRequest, HTTP_INTERCEPTORS} from '@angular/common/http';
import {MaterialModule} from './material/material.module'; import {MaterialModule} from './material/material.module';
import {QRCodeModule} from 'angularx-qrcode'; import {QRCodeModule} from 'angularx-qrcode';
import { DatePipe } from '@angular/common'; import {DatePipe} from '@angular/common';
import { AutofocusDirective} from './material/autofocus'; import {AutofocusDirective} from './material/autofocus';
import {I18nPipe} from './utils/i18n.pipe'; import {I18nPipe} from './utils/i18n.pipe';
import {ImprintComponent, PrivacyPolicyComponent, TermsOfServiceComponent} from './pages/general/general.component'; import {ImprintComponent, PrivacyPolicyComponent, TermsOfServiceComponent} from './pages/general/general.component';
@ -43,10 +43,12 @@ import {HtmlComponent} from './utils/html/html.component';
import {ConfirmDialog} from './ui/confirm/confirm.component' import {ConfirmDialog} from './ui/confirm/confirm.component'
import {UserComponent} from './pages/user/user.component' import {UserComponent} from './pages/user/user.component'
import {JitsiComponent, JitsiShareDialog} from './pages/jitsi/jitsi.component' import {JitsiComponent, JitsiShareDialog} from './pages/jitsi/jitsi.component'
import {ParteyTimeslotsComponent, ParteyTimeslotDialog} from './pages/partey/timeslots/timeslots.compontent'
import {UrlShortenerComponent, UrlShortenerPasswordComponent, UrlShortenerShareDialog} from './pages/urlshortener/urlshortener.component' import {UrlShortenerComponent, UrlShortenerPasswordComponent, UrlShortenerShareDialog} from './pages/urlshortener/urlshortener.component'
import {I18nService} from './services/i18n.service'; import {I18nService} from './services/i18n.service';
import {MinetestAccountsComponent} from './pages/minetest/accounts/accounts.component';
export function init_app(i18n: I18nService) { export function init_app(i18n: I18nService) {
@ -99,6 +101,8 @@ export class XhrInterceptor implements HttpInterceptor {
ConfirmDialog, ConfirmDialog,
UserComponent, UserComponent,
JitsiComponent, JitsiShareDialog, JitsiComponent, JitsiShareDialog,
ParteyTimeslotsComponent, ParteyTimeslotDialog,
MinetestAccountsComponent,
UrlShortenerComponent, UrlShortenerShareDialog, UrlShortenerPasswordComponent UrlShortenerComponent, UrlShortenerShareDialog, UrlShortenerPasswordComponent
], ],
imports: [ imports: [

View File

@ -0,0 +1,43 @@
<h3>{{'minetest.accounts' | i18n}}</h3>
<table mat-table [dataSource]="minetestAccounts">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> {{'minetest.accounts.name' | i18n}} </th>
<td mat-cell *matCellDef="let minetestAccount">
{{minetestAccount.name}}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="minetestAccountsColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: minetestAccountsColumns"></tr>
</table>
<form [formGroup]="form" (ngSubmit)="create()" #formDirective="ngForm">
<mat-card>
<mat-card-content>
<p>{{'minetest.accounts.info' | i18n}}</p>
<p *ngIf="!minetestAccountsQuota">{{'minetest.accounts.noQuota' | i18n}}</p>
<div *ngIf="minetestAccountsQuota">
<p>{{'minetest.accounts.left' | i18n:minetestAccountsQuota}}</p>
<mat-form-field>
<input matInput placeholder="{{'minetest.accounts.name' | i18n}}" formControlName="name"
[(ngModel)]="minetestAccount.name" required pattern="[-_a-zA-Z0-9]+">
<mat-error>
{{'minetest.accounts.error.name' | i18n}}
</mat-error>
</mat-form-field>
<mat-card class="warn">
<mat-card-header>
<mat-card-subtitle>{{'minetest.accounts.deletion' | i18n}}</mat-card-subtitle>
</mat-card-header>
</mat-card>
</div>
</mat-card-content>
<mat-card-actions>
<button *ngIf="minetestAccountsQuota && !working" mat-raised-button color="primary" [disabled]="form.invalid">
{{'minetest.accounts.create' | i18n}}
</button>
</mat-card-actions>
</mat-card>
</form>

View File

@ -0,0 +1,30 @@
mat-form-field {
display: block;
}
.mat-header-cell,
.mat-cell {
&.text-right {
text-align: right;
}
}
.mat-cell .mat-button {
padding-left: 0px;
padding-right: 0px;
}
.align-right{
display: flex;
padding: 21px 0;
justify-content: flex-end;
}
.url {
display: inline-block;
width: 200px;
max-width: 200px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}

View File

@ -0,0 +1,77 @@
import {Component, OnInit, ViewChild} from '@angular/core';
import {FormBuilder, FormGroup, Validators, NgForm} from '@angular/forms';
import {QuotaService} from '../../../services/quota.service';
import {MinetestAccountsService} from '../../../services/minetest.accounts.service';
@Component({
selector: 'app-minetest-accounts',
templateUrl: './accounts.component.html',
styleUrls: ['./accounts.component.scss']
})
export class MinetestAccountsComponent implements OnInit {
form: FormGroup;
@ViewChild('formDirective') private formDirective: NgForm;
minetestAccountsQuota: number = 0;
minetestAccounts: any;
minetestAccount: any = {};
success: boolean;
working: boolean;
minetestAccountsColumns = ["name"];
constructor(
private quotaService: QuotaService,
private formBuilder: FormBuilder,
private minetestAccountsService: MinetestAccountsService) {}
ngOnInit(): void {
this.form = this.formBuilder.group({
name: ['', Validators.required],
});
this.update();
}
create(): void {
this.working = true;
this.minetestAccountsService.create(this.minetestAccount).subscribe(response => {
this.update();
this.formDirective.resetForm();
this.minetestAccount = {};
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.minetestAccountsQuota = 0;
this.quotaService.quotas().subscribe((data: any) => {
for(let quota of data) {
if(quota.name == "minetest_accounts") {
this.minetestAccountsQuota = quota.value;
}
}
})
this.minetestAccountsService.get().subscribe((data: any) => {
this.minetestAccounts = data;
})
}
}

View File

@ -0,0 +1,48 @@
<h1 mat-dialog-title>
<mat-icon inline="true">{{'partey.timeslots.type.' + timeslot.type + '.icon' | i18n}}</mat-icon> {{(timeslot.id ? 'partey.timeslots.edit' : 'partey.timeslots.create.' +
timeslot.type) | i18n}}
</h1>
<mat-dialog-content>
<h3 *ngIf="timeslot.id">{{'partey.timeslots.type.' + timeslot.type | i18n}}</h3>
<form [formGroup]="form">
<mat-form-field *ngIf="timeslot.type == 'VIDEO'">
<input matInput [(ngModel)]="timeslot.stream" formControlName="stream"
placeholder="{{'partey.timeslots.stream' | i18n}}">
</mat-form-field>
<mat-form-field *ngIf="timeslot.type == 'AUDIO' && timeslot.secret">
<textarea [mat-autosize] matInput [value]="timeslot.secret" readonly placeholder="{{'partey.timeslots.secret' | i18n}}"></textarea>
</mat-form-field>
<mat-form-field>
<input matInput [ngxMatDatetimePicker]="startsPicker" [(ngModel)]="timeslot.starts" formControlName="starts"
placeholder="{{'partey.timeslots.starts' | i18n}}">
<mat-datepicker-toggle matSuffix [for]="startsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker>
<mat-error>
{{'partey.timeslots.error.starts' | i18n}}
</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput [ngxMatDatetimePicker]="endsPicker" [(ngModel)]="timeslot.ends" formControlName="ends"
placeholder="{{'partey.timeslots.ends' | i18n}}">
<mat-datepicker-toggle matSuffix [for]="endsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #endsPicker></ngx-mat-datetime-picker>
<mat-error>
{{'partey.timeslots.error.ends' | i18n}}
</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput [(ngModel)]="timeslot.title" formControlName="title"
placeholder="{{'partey.timeslots.title' | i18n}}">
</mat-form-field>
<mat-form-field>
<textarea matInput [(ngModel)]="timeslot.description" formControlName="description"
placeholder="{{'partey.timeslots.description' | i18n}}"></textarea>
</mat-form-field>
</form>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button [mat-dialog-close]="false">{{'cancel' | i18n}}</button>
<button [disabled]="form.invalid" mat-raised-button (click)="timeslot.id ? save(timeslot) : create(timeslot)"
color="accent">{{ (timeslot.id ? 'partey.timeslots.save' : 'partey.timeslots.create') |
i18n}}</button>
</mat-dialog-actions>

View File

@ -0,0 +1,7 @@
mat-form-field {
display: block;
}
mat-slide-toggle {
margin-bottom: 24px;
}

View File

@ -0,0 +1,117 @@
<h3>{{'partey.timeslots' | i18n}}</h3>
<div *ngIf="timeslots">
<mat-form-field>
<input matInput [formControl]="searchFormControl" placeholder="{{'partey.timeslots.filter.search' | i18n}}">
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>{{'partey.timeslots.filter.owner' | i18n}}</mat-label>
<mat-select [formControl]="ownerFormControl">
<mat-option value="">{{'partey.timeslots.filter.owner.mine' | i18n}}</mat-option>
<mat-option value="others">{{'partey.timeslots.filter.owner.others' | i18n}} </mat-option>
<mat-option value="all">{{'partey.timeslots.filter.owner.all' | i18n}} </mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>{{'partey.timeslots.filter.type' | i18n}}</mat-label>
<mat-select [formControl]="typeFormControl">
<mat-option value="">{{'partey.timeslots.filter.type.all' | i18n}}</mat-option>
<mat-option *ngFor="let type of types" [value]="type">
<mat-icon inline="true">{{'partey.timeslots.type.' + type + '.icon' | i18n}}</mat-icon>
{{'partey.timeslots.filter.type.' + type | i18n}}
</mat-option>
<mat-select-trigger>
<mat-icon inline="true" *ngIf="typeFormControl.value ">{{'partey.timeslots.type.' + typeFormControl.value +
'.icon' | i18n}}</mat-icon> {{'partey.timeslots.filter.type.' + (typeFormControl.value ? typeFormControl.value
: 'all') | i18n}}
</mat-select-trigger>
</mat-select>
</mat-form-field>
<mat-form-field>
<input matInput [ngxMatDatetimePicker]="afterPicker" [formControl]="afterFormControl"
placeholder="{{'partey.timeslots.filter.after' | i18n}}">
<mat-datepicker-toggle matSuffix [for]="afterPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #afterPicker></ngx-mat-datetime-picker>
</mat-form-field>
<table mat-table matSort [dataSource]="timeslots.content" (matSortChange)="updateSort($event)">
<ng-container matColumnDef="starts">
<th mat-header-cell *matHeaderCellDef mat-sort-header="starts"> {{'partey.timeslots.starts' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot">{{ timeslot.starts | date:datetimeformat}} </td>
</ng-container>
<ng-container matColumnDef="ends">
<th mat-header-cell *matHeaderCellDef mat-sort-header="ends"> {{'partey.timeslots.ends' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot"> {{ timeslot.ends | date:datetimeformat}} </td>
</ng-container>
<ng-container matColumnDef="title">
<th mat-header-cell *matHeaderCellDef mat-sort-header="title"> {{'partey.timeslots.title' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot"> {{ timeslot.title }} </td>
</ng-container>
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef mat-sort-header="description"> {{'partey.timeslots.description' | i18n}}
</th>
<td mat-cell *matCellDef="let timeslot"> {{ timeslot.description }} </td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef mat-sort-header="type"> {{'partey.timeslots.type' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot">
<mat-icon inline="true" matTooltip="{{'partey.timeslots.type.' + timeslot.type | i18n}}">
{{'partey.timeslots.type.' + timeslot.type + '.icon' | i18n}}</mat-icon>
</td>
</ng-container>
<ng-container matColumnDef="stream">
<th mat-header-cell *matHeaderCellDef mat-sort-header="stream"> {{'partey.timeslots.stream' | i18n}}
</th>
<td mat-cell *matCellDef="let timeslot">
<button *ngIf="timeslot.type == 'AUDIO'" mat-raised-button
(click)="copySecretToClipboard(timeslot.secret)">{{'partey.timeslots.secret.copy' | i18n}}</button>
<span *ngIf="timeslot.type == 'VIDEO'"> {{ timeslot.stream }} </span>
</td>
</ng-container>
<ng-container matColumnDef="edit">
<th mat-header-cell *matHeaderCellDef> {{'partey.timeslots.edit' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot" class="text-right">
<a mat-icon-button>
<mat-icon (click)="openEdit(timeslot)">edit</mat-icon>
</a>
</td>
</ng-container>
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef class="align-right"> {{'partey.timeslots.delete' | i18n}} </th>
<td mat-cell *matCellDef="let timeslot" class="text-right">
<a mat-icon-button>
<mat-icon (click)="confirmDelete(timeslot)">delete</mat-icon>
</a>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="timeslotsColumns"></tr>
<tr mat-row *matRowDef="let myRowData; columns: timeslotsColumns"></tr>
</table>
<mat-paginator [pageSizeOptions]="pageSizeOptions" [length]="timeslots.totalElements" [pageSize]="timeslots.size"
(page)="updatePages($event)" showFirstLastButtons></mat-paginator>
</div>
<mat-card>
<mat-card-content>
<p>{{'partey.timeslots.info' | i18n}}</p>
<p *ngIf="!timeslotsQuota">{{'partey.timeslots.noQuota' | i18n}}</p>
<div *ngIf="timeslotsQuota">
<p>{{'partey.timeslots.left' | i18n:timeslotsQuota}}</p>
</div>
</mat-card-content>
<mat-card-actions *ngIf="timeslotsQuota">
<button mat-button *ngFor="let type of types" (click)="openCreate(type)">
<mat-icon inline="true">{{'partey.timeslots.type.' + type + '.icon' | i18n}}</mat-icon>
{{'partey.timeslots.create.' + type | i18n}}
</button>
</mat-card-actions>
</mat-card>

View File

@ -0,0 +1,21 @@
.mat-header-cell,
.mat-cell {
&.text-right {
text-align: right;
}
}
.mat-cell .mat-button {
padding-left: 0px;
padding-right: 0px;
}
.align-right{
display: flex;
padding: 21px 0;
justify-content: flex-end;
}
.mat-form-field+.mat-form-field {
margin-left: 8px;
}

View File

@ -0,0 +1,263 @@
import {Component, OnInit, ViewChild, Inject} from '@angular/core';
import {Sort} from '@angular/material/sort';
import {MatSnackBar} from '@angular/material/snack-bar';
import {FormBuilder, FormGroup, Validators, NgForm} from '@angular/forms';
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
import {DatePipe} from '@angular/common';
import {FormControl} from '@angular/forms';
import {debounceTime} from 'rxjs/operators';
import {PageEvent} from '@angular/material/paginator';
import {ParteyTimeslotsService} from '../../../services/partey.timeslots.service';
import {ConfirmDialog} from '../../../ui/confirm/confirm.component';
import {I18nService} from './../../../services/i18n.service';
@Component({
selector: 'app-partey-timeslots',
templateUrl: './timeslots.component.html',
styleUrls: ['./timeslots.component.scss']
})
export class ParteyTimeslotsComponent implements OnInit {
form: FormGroup;
@ViewChild('formDirective') private formDirective: NgForm;
timeslotsQuota: number = 0;
timeslots: any;
timeslot: any = {};
success: boolean;
working: boolean;
datetimeformat: string;
page: any = {page: 0, size: 10, sort: "id", desc: false};
filter: any = {};
pageSizeOptions: number[] = [5, 10, 25, 50];
visibilities = ["PRIVATE", "PROTECTED", "PUBLIC"];
types = ["VIDEO", "AUDIO"];
timeslotsColumns = ["type", "starts", "ends", "title", "description", "stream", "edit", "delete"];
searchFormControl = new FormControl();
ownerFormControl = new FormControl();
afterFormControl = new FormControl();
typeFormControl = new FormControl();
constructor(
private formBuilder: FormBuilder,
private parteyTimeslotsService: ParteyTimeslotsService,
private i18n: I18nService,
private datePipe: DatePipe,
private snackBar: MatSnackBar,
public dialog: MatDialog) {}
ngOnInit(): void {
this.datetimeformat = this.i18n.get('format.datetime', []);
this.form = this.formBuilder.group({
room: ['', Validators.required],
starts: ['', Validators.nullValidator],
moderationStarts: ['', Validators.nullValidator],
expires: ['', Validators.nullValidator],
});
this.filter.owner = "";
this.ownerFormControl.setValue("");
this.filter.type = "";
this.typeFormControl.setValue("");
this.filter.after = new Date().toUTCString();
this.afterFormControl.setValue(new Date());
this.searchFormControl.valueChanges.pipe(debounceTime(500)).subscribe(value => {
this.filter.search = value;
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
this.timeslots = data;
}, (error) => {})
})
this.ownerFormControl.valueChanges.subscribe(value => {
this.filter.owner = value;
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
this.timeslots = data;
}, (error) => {})
})
this.typeFormControl.valueChanges.subscribe(value => {
this.filter.type = value;
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
this.timeslots = data;
}, (error) => {})
})
this.afterFormControl.valueChanges.subscribe((value: Date) => {
this.filter.after = value && value.toUTCString() || undefined;
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
this.timeslots = data;
}, (error) => {})
})
this.refresh();
}
refresh() {
this.timeslotsQuota = 0;
this.parteyTimeslotsService.quota().subscribe((data: number) => {
this.timeslotsQuota = data;
})
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
this.timeslots = data;
})
}
updatePages(event: PageEvent) {
this.page.page = event.pageIndex;
this.page.size = event.pageSize;
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
this.timeslots = data;
}, (error) => {})
}
updateSort(sort: Sort) {
if(sort.direction == "") {
this.page.sort = "id";
this.page.desc = false;
} else {
this.page.sort = sort.active;
this.page.desc = sort.direction == "desc";
}
this.parteyTimeslotsService.get(this.page.page, this.page.size, this.page.sort, this.page.desc, this.filter).subscribe((data: any) => {
this.timeslots = data;
}, (error) => {})
}
confirmDelete(timeslot) {
const dialogRef = this.dialog.open(ConfirmDialog, {
data: {
'label': 'partey.timeslots.confirmDelete',
'args': [timeslot.title, this.datePipe.transform(new Date(timeslot.starts), this.datetimeformat)]
}
})
dialogRef.afterClosed().subscribe(result => {
if(result) {
this.parteyTimeslotsService.delete(timeslot.id).subscribe((result: any) => {
this.refresh();
})
}
});
}
openCreate(type) {
const dialogRef = this.dialog.open(ParteyTimeslotDialog, {
data: {"type": type ? type : "VIDEO", "visibility": "PROTECTED"},
minWidth: '400px'
});
dialogRef.afterClosed().subscribe(result => {
if(result) {
this.refresh();
}
});
}
openEdit(timeslot) {
const timeslotClone = JSON.parse(JSON.stringify(timeslot));
const dialogRef = this.dialog.open(ParteyTimeslotDialog, {
data: timeslotClone,
minWidth: '400px'
});
dialogRef.afterClosed().subscribe(result => {
if(result) {
this.refresh();
}
});
}
copySecretToClipboard(secret) {
const selBox = document.createElement('textarea');
selBox.value = secret;
document.body.appendChild(selBox);
selBox.focus();
selBox.select();
document.execCommand('copy');
document.body.removeChild(selBox);
this.snackBar.open(this.i18n.get("partey.timeslots.secret.copied", []), this.i18n.get("close", []), {
duration: 3000
});
}
}
@Component({
selector: 'app-timeslot-dialog',
templateUrl: 'timeslot.dialog.html',
styleUrls: ['./timeslot.dialog.scss']
})
export class ParteyTimeslotDialog {
form: FormGroup;
timeslot;
visibilities = ["PRIVATE", "PROTECTED", "PUBLIC"];
types = ["VIDEO", "AUDIO"];
constructor(
private parteyTimeslotsService: ParteyTimeslotsService,
private formBuilder: FormBuilder,
private dialog: MatDialog,
public dialogRef: MatDialogRef<ParteyTimeslotDialog>,
@Inject(MAT_DIALOG_DATA) public data: any) {
this.timeslot = data;
}
ngOnInit() {
this.form = this.formBuilder.group({
starts: ['', Validators.required],
ends: ['', Validators.required],
title: ['', Validators.nullValidator],
description: ['', Validators.nullValidator],
stream: ['', this.timeslot.type == 'VIDEO' ? Validators.required : Validators.nullValidator],
});
}
create(timeslot) {
this.parteyTimeslotsService.create(timeslot).subscribe((result: any) => {
this.dialogRef.close(timeslot);
}, (error) => {
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]);
}
}
});
}
save(timeslot) {
this.parteyTimeslotsService.update(timeslot).subscribe((result: any) => {
this.dialogRef.close(timeslot);
}, (error) => {
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]);
}
}
});
}
}

View File

@ -12,7 +12,22 @@ export class JitsiService {
} }
get(page: number, size: number, sort: string, desc: boolean) { get(page: number, size: number, sort: string, desc: boolean) {
const httpParams = new HttpParams().set("page", "" + page).set("size", "" + size).set("sort", sort).set("desc", "" + desc); let httpParams = new HttpParams();
if(page != undefined) {
httpParams = httpParams.set("page", "" + page);
}
if(size != undefined) {
httpParams = httpParams.set("size", "" + size);
}
if(sort != undefined) {
httpParams = httpParams.set("sort", sort);
}
if(desc != undefined) {
httpParams = httpParams.set("desc", "" + desc);
}
return this.http.get(environment.apiUrl + "/jitsi/rooms", {params: httpParams}); return this.http.get(environment.apiUrl + "/jitsi/rooms", {params: httpParams});
} }

View File

@ -0,0 +1,22 @@
import {Injectable} from '@angular/core';
import {HttpClient, HttpParams} from '@angular/common/http';
import {environment} from '../../environments/environment';
@Injectable({
providedIn: 'root',
})
export class MinetestAccountsService {
constructor(private http: HttpClient) {
}
get() {
return this.http.get(environment.apiUrl + "/minetest/accounts");
}
create(minetestAccount) {
return this.http.post(environment.apiUrl + "/minetest/accounts", minetestAccount);
}
}

View File

@ -0,0 +1,69 @@
import {Injectable} from '@angular/core';
import {HttpClient, HttpParams} from '@angular/common/http';
import {environment} from '../../environments/environment';
@Injectable({
providedIn: 'root',
})
export class ParteyTimeslotsService {
constructor(private http: HttpClient) {
}
get(page: number, size: number, sort: string, desc: boolean, filter : any) {
let httpParams = new HttpParams();
if(page != undefined) {
httpParams = httpParams.set("page", "" + page);
}
if(size != undefined) {
httpParams = httpParams.set("size", "" + size);
}
if(sort != undefined) {
httpParams = httpParams.set("sort", sort);
}
if(desc != undefined) {
httpParams = httpParams.set("desc", "" + desc);
}
if (filter) {
if(filter.owner != undefined && filter.owner != "") {
httpParams = httpParams.set("owner", "" + filter.owner);
}
if(filter.after != undefined && filter.after != "") {
httpParams = httpParams.set("after", "" + filter.after);
}
if(filter.type != undefined && filter.type != "") {
httpParams = httpParams.set("type", "" + filter.type);
}
if(filter.visibility != undefined && filter.visibility != "") {
httpParams = httpParams.set("visibility", "" + filter.visibility);
}
if(filter.search != undefined && filter.search != "") {
httpParams = httpParams.set("search", "" + filter.search);
}
}
return this.http.get(environment.apiUrl + "/partey/timeslots", {params: httpParams});
}
quota() {
return this.http.get(environment.apiUrl + "/partey/timeslots/quota");
}
create(timeslot) {
return this.http.post(environment.apiUrl + "/partey/timeslots", timeslot);
}
update(timeslot) {
return this.http.patch(environment.apiUrl + "/partey/timeslots", timeslot);
}
delete(id) {
return this.http.delete(environment.apiUrl + "/partey/timeslots/" + id);
}
}