upgrade angualr, fix keep session attribute

This commit is contained in:
_Bastler 2023-12-12 16:03:42 +01:00
parent 0b3d658b10
commit 5dc98a3151
28 changed files with 9757 additions and 7245 deletions

16517
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,48 +11,45 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular-material-components/datetime-picker": "^9.0.0", "@angular/animations": "^17.0.6",
"@angular-material-components/moment-adapter": "^9.0.0", "@angular/cdk": "^17.0.3",
"@angular/animations": "^15.0.4", "@angular/common": "^17.0.6",
"@angular/cdk": "^15.0.3", "@angular/compiler": "^17.0.6",
"@angular/common": "^15.0.4", "@angular/core": "^17.0.6",
"@angular/compiler": "^15.0.4", "@angular/forms": "^17.0.6",
"@angular/core": "^15.0.4", "@angular/material": "^17.0.3",
"@angular/forms": "^15.0.4", "@angular/material-moment-adapter": "^17.0.3",
"@angular/material": "^15.0.3", "@angular/platform-browser": "^17.0.6",
"@angular/material-moment-adapter": "^15.0.3", "@angular/platform-browser-dynamic": "^17.0.6",
"@angular/platform-browser": "^15.0.4", "@angular/router": "^17.0.6",
"@angular/platform-browser-dynamic": "^15.0.4",
"@angular/router": "^15.0.4",
"moment": "^2.29.4", "moment": "^2.29.4",
"ng-qrcode": "^8.0.1", "ng-qrcode": "^17.0.0",
"ngx-mat-timepicker": "^15.1.2", "openpgp": "^5.11.0",
"openpgp": "^5.5.0",
"qr-scanner": "^1.4.2", "qr-scanner": "^1.4.2",
"rxjs": "~7.8.0", "rxjs": "~7.8.1",
"tslib": "^2.4.1", "tslib": "^2.6.2",
"unique-names-generator": "^4.7.1", "unique-names-generator": "^4.7.1",
"zone.js": "~0.12.0" "zone.js": "~0.14.2"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^15.0.4", "@angular-devkit/build-angular": "^17.0.6",
"@angular/cli": "^15.0.4", "@angular/cli": "^17.0.6",
"@angular/compiler-cli": "^15.0.4", "@angular/compiler-cli": "^17.0.6",
"@angular/localize": "^15.0.4", "@angular/localize": "^17.0.6",
"@types/jasmine": "^4.3.1", "@types/jasmine": "^5.1.4",
"@types/jasminewd2": "^2.0.10", "@types/jasminewd2": "^2.0.13",
"@types/node": "^18.11.18", "@types/node": "^20.10.4",
"@types/openpgp": "^4.4.18", "@types/openpgp": "^4.4.22",
"jasmine-core": "~4.5.0", "jasmine-core": "~5.1.1",
"jasmine-spec-reporter": "~7.0.0", "jasmine-spec-reporter": "~7.0.0",
"karma": "^6.4.1", "karma": "^6.4.2",
"karma-chrome-launcher": "~3.1.1", "karma-chrome-launcher": "~3.2.0",
"karma-coverage-istanbul-reporter": "~3.0.3", "karma-coverage-istanbul-reporter": "~3.0.3",
"karma-jasmine": "~5.1.0", "karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "^2.0.0", "karma-jasmine-html-reporter": "^2.1.0",
"protractor": "~7.0.0", "protractor": "~7.0.0",
"ts-node": "~10.9.1", "ts-node": "~10.9.2",
"tslint": "~6.1.0", "tslint": "~6.1.0",
"typescript": "~4.8.4" "typescript": "~5.2.2"
} }
} }

View File

@ -34,6 +34,7 @@ import { BorrowComponent } from './pages/borrow/borrow.component';
import { InviteCodeComponent } from './pages/invites/code/code.component'; import { InviteCodeComponent } from './pages/invites/code/code.component';
import { JukeboxComponent } from './pages/jukebox/jukebox.compontent'; import { JukeboxComponent } from './pages/jukebox/jukebox.compontent';
import { FormLoginOidcComponent } from './pages/form-login-oidc/form-login-oidc.component'; import { FormLoginOidcComponent } from './pages/form-login-oidc/form-login-oidc.component';
import { DyndnsComponent } from './pages/account/dyndns/dyndns.component';
const routes: Routes = [ const routes: Routes = [
{ path: 'profile/:username', component: UserComponent, canActivate: [AuthUpdateGuard] }, { path: 'profile/:username', component: UserComponent, canActivate: [AuthUpdateGuard] },
@ -59,7 +60,8 @@ const routes: Routes = [
{ path: 'security', component: SecurityComponent, canActivate: [AuthenticatedGuard] }, { path: 'security', component: SecurityComponent, canActivate: [AuthenticatedGuard] },
{ path: 'voucher', component: VoucherComponent, canActivate: [AuthenticatedGuard] }, { path: 'voucher', component: VoucherComponent, canActivate: [AuthenticatedGuard] },
{ path: 'aliases', component: AliasesComponent, canActivate: [AuthenticatedGuard] }, { path: 'aliases', component: AliasesComponent, canActivate: [AuthenticatedGuard] },
{ path: 'domains', component: DomainsComponent, canActivate: [AuthenticatedGuard] } { path: 'domains', component: DomainsComponent, canActivate: [AuthenticatedGuard] },
{ path: 'dyndns', component: DyndnsComponent, canActivate: [AuthenticatedGuard] }
] ]
}, },
{ {

View File

@ -35,6 +35,7 @@ import { VoucherDialog } from './pages/account/voucher/voucher.component';
import { InfoComponent } from './pages/account/info/info.component'; import { InfoComponent } from './pages/account/info/info.component';
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 { DyndnsComponent } from './pages/account/dyndns/dyndns.component';
import { ProfileComponent } from './pages/account/profile/profile.component'; import { ProfileComponent } from './pages/account/profile/profile.component';
import { PasswordComponent } from './pages/password/password.component'; import { PasswordComponent } from './pages/password/password.component';
import { PasswordResetComponent } from './pages/password-reset/password-reset.component'; import { PasswordResetComponent } from './pages/password-reset/password-reset.component';
@ -62,10 +63,12 @@ import { DurationpickerComponent } from './ui/durationpicker/durationpicker.comp
import { InviteCodeComponent } from './pages/invites/code/code.component'; import { InviteCodeComponent } from './pages/invites/code/code.component';
import { InviteEditComponent } from './pages/invites/edit/invite.edit'; import { InviteEditComponent } from './pages/invites/edit/invite.edit';
import { MatPaginatorIntl } from '@angular/material/paginator'; import { MatPaginatorIntl } from '@angular/material/paginator';
import { NgxMatDateAdapter, NGX_MAT_DATE_FORMATS } from '@angular-material-components/datetime-picker';
import { NgxMatMomentAdapter } from '@angular-material-components/moment-adapter';
import { JukeboxComponent } from './pages/jukebox/jukebox.compontent'; import { JukeboxComponent } from './pages/jukebox/jukebox.compontent';
import { FormLoginOidcComponent } from './pages/form-login-oidc/form-login-oidc.component'; import { FormLoginOidcComponent } from './pages/form-login-oidc/form-login-oidc.component';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MomentDateModule } from '@angular/material-moment-adapter';
import { MAT_DATE_FORMATS } from '@angular/material/core';
import { DatetimepickerComponent } from './ui/datetimepicker/datetimepicker.component';
export function init_app(i18n: I18nService) { export function init_app(i18n: I18nService) {
@ -109,6 +112,7 @@ export class XhrInterceptor implements HttpInterceptor {
InfoComponent, InfoComponent,
AliasesComponent, AliasesComponent,
DomainsComponent, DomainsComponent,
DyndnsComponent,
ProfileComponent, ProfileComponent,
PasswordComponent, PasswordComponent,
PasswordResetComponent, PasswordResetComponent,
@ -126,6 +130,7 @@ export class XhrInterceptor implements HttpInterceptor {
UrlShortenerComponent, UrlShortenerShareDialog, UrlShortenerEditDialog, UrlShortenerPasswordComponent, UrlShortenerComponent, UrlShortenerShareDialog, UrlShortenerEditDialog, UrlShortenerPasswordComponent,
BorrowComponent, BorrowItemsComponent, BorrowItemEditComponent, BorrowRequestsComponent, BorrowRequestEditComponent, BorrowProvingComponent, BorrowProvingResultDialog, BorrowComponent, BorrowItemsComponent, BorrowItemEditComponent, BorrowRequestsComponent, BorrowRequestEditComponent, BorrowProvingComponent, BorrowProvingResultDialog,
DividerComponent, DividertestComponent, DividerComponent, DividertestComponent,
DatetimepickerComponent,
DurationpickerComponent, DurationpickerComponent,
JukeboxComponent JukeboxComponent
], ],
@ -139,7 +144,8 @@ export class XhrInterceptor implements HttpInterceptor {
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
QrCodeModule, QrCodeModule,
MatDatepickerModule,
MomentDateModule
], ],
exports: [MaterialModule], exports: [MaterialModule],
providers: [{ provide: APP_INITIALIZER, useFactory: init_app, deps: [I18nService], multi: true }, { provide: HTTP_INTERCEPTORS, useClass: XhrInterceptor, multi: true }, DatePipe, providers: [{ provide: APP_INITIALIZER, useFactory: init_app, deps: [I18nService], multi: true }, { provide: HTTP_INTERCEPTORS, useClass: XhrInterceptor, multi: true }, DatePipe,
@ -149,15 +155,8 @@ export class XhrInterceptor implements HttpInterceptor {
service.injectI18n(i18n) service.injectI18n(i18n)
return service; return service;
}, deps: [I18nService] }, deps: [I18nService]
},
{
provide: NgxMatDateAdapter,
useClass: NgxMatMomentAdapter,
useFactory: (i18n: I18nService) => {
return new NgxMatMomentAdapter(i18n.getLocale(), { strict: true });
}, deps: [I18nService]
}, { }, {
provide: NGX_MAT_DATE_FORMATS, useFactory: (i18n: I18nService) => { provide: MAT_DATE_FORMATS, useFactory: (i18n: I18nService) => {
const datetimeformat = i18n.get('format.datetime', []); const datetimeformat = i18n.get('format.datetime', []);
return { return {
parse: { parse: {
@ -165,9 +164,9 @@ export class XhrInterceptor implements HttpInterceptor {
}, },
display: { display: {
dateInput: datetimeformat, dateInput: datetimeformat,
monthYearLabel: "MMM YYYY", monthYearLabel: 'MMM YYYY',
dateA11yLabel: "LL", dateA11yLabel: 'LL',
monthYearA11yLabel: "MMMM YYYY" monthYearA11yLabel: 'MMMM YYYY'
} }
}; };
}, deps: [I18nService] }, deps: [I18nService]

View File

@ -44,15 +44,6 @@ import {MatSortModule} from '@angular/material/sort';
import {MatTableModule} from '@angular/material/table'; import {MatTableModule} from '@angular/material/table';
import {MatMomentDateModule} from '@angular/material-moment-adapter'; import {MatMomentDateModule} from '@angular/material-moment-adapter';
import { NgxMatMomentModule } from '@angular-material-components/moment-adapter';
import {
NgxMatDatetimePickerModule,
NgxMatNativeDateModule
} from '@angular-material-components/datetime-picker';
import { NgxMatTimepickerModule } from 'ngx-mat-timepicker';
@NgModule({ @NgModule({
declarations: [], declarations: [],
imports: [ imports: [
@ -92,11 +83,7 @@ import { NgxMatTimepickerModule } from 'ngx-mat-timepicker';
MatPaginatorModule, MatPaginatorModule,
MatSortModule, MatSortModule,
MatTableModule, MatTableModule,
MatMomentDateModule, MatMomentDateModule
NgxMatMomentModule,
NgxMatDatetimePickerModule,
NgxMatNativeDateModule,
NgxMatTimepickerModule
], ],
exports: [ exports: [
MatAutocompleteModule, MatAutocompleteModule,
@ -133,10 +120,7 @@ import { NgxMatTimepickerModule } from 'ngx-mat-timepicker';
MatTooltipModule, MatTooltipModule,
MatPaginatorModule, MatPaginatorModule,
MatSortModule, MatSortModule,
MatTableModule, MatTableModule
NgxMatDatetimePickerModule,
NgxMatNativeDateModule,
NgxMatTimepickerModule
] ]
}) })
export class MaterialModule {} export class MaterialModule {}

View File

@ -14,6 +14,8 @@
[active]="rlaaliases.isActive">{{'user.aliases' | i18n}}</a> [active]="rlaaliases.isActive">{{'user.aliases' | i18n}}</a>
<a *ngIf="advancedView" mat-tab-link routerLink="domains" #rladomains="routerLinkActive" routerLinkActive <a *ngIf="advancedView" mat-tab-link routerLink="domains" #rladomains="routerLinkActive" routerLinkActive
[active]="rladomains.isActive">{{'user.domains' | i18n}}</a> [active]="rladomains.isActive">{{'user.domains' | i18n}}</a>
<a *ngIf="advancedView" mat-tab-link routerLink="dyndns" #rladyndns="routerLinkActive" routerLinkActive
[active]="rladyndns.isActive">{{'user.dyndns' | i18n}}</a>
<a style="align-self: center;"> <a style="align-self: center;">
<mat-slide-toggle [(ngModel)]="advancedView"> <mat-slide-toggle [(ngModel)]="advancedView">
<span *ngIf="!advancedView">{{'account.advanced' | i18n}}</span> <span *ngIf="!advancedView">{{'account.advanced' | i18n}}</span>

View File

@ -1,18 +1,24 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { AuthService } from './../../services/auth.service'; import { AuthService } from './../../services/auth.service';
import { ActivatedRoute, Router } from '@angular/router';
import { MatTabNavPanel } from '@angular/material/tabs';
@Component({ @Component({
selector: 'app-account', selector: 'app-account',
templateUrl: './account.component.html', templateUrl: './account.component.html',
styleUrls: [ './account.component.scss' ] styleUrls: ['./account.component.scss']
}) })
export class AccountComponent implements OnInit { export class AccountComponent implements OnInit {
advancedViews: string[] = ["aliases", "domains", "dyndns"]
advancedView: boolean = false; advancedView: boolean = false;
auth; auth;
constructor(private authService: AuthService) { @ViewChild('tabPanel') tabPanel: MatTabNavPanel;
constructor(private authService: AuthService, private router: Router) {
this.authService.auth.subscribe({ this.authService.auth.subscribe({
next: (data) => { next: (data) => {
this.auth = data; this.auth = data;
@ -21,6 +27,7 @@ export class AccountComponent implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.advancedView = this.advancedViews.indexOf(this.router.url.replace("/account/", "")) != -1;
} }
} }

View File

@ -0,0 +1,24 @@
<h3>{{'user.dyndns' | i18n}}</h3>
<form (ngSubmit)="create()" #formDirective="ngForm" #dyndnsForm>
<mat-card>
<mat-card-content>
<p *ngIf="dyndnsToken && !dyndnsToken.token">{{'user.dyndns.token.exists' | i18n}}</p>
<a *ngIf="dyndnsToken && dyndnsToken.token" mat-button (click)="copySecret()"
matTooltip="{{'user.dyndns.token.copy' | i18n}}" matTooltipPosition="above">{{ dyndnsToken.token}}</a>
<p *ngIf="dyndnsToken && dyndnsToken.token">{{'user.dyndns.token.store' | i18n}}</p>
</mat-card-content>
<mat-card-actions>
<button type="submit" *ngIf="!working" mat-raised-button color="primary">
{{(dyndnsToken ? 'user.dyndns.new' : 'user.dyndns.create') | i18n}}
</button>
</mat-card-actions>
<mat-card-footer>
<a href="https://wiki.bstly.de/services/webstly#dyndns" class="help-button" matTooltip="{{'help-button' | i18n}}"
matTooltipPosition="above" target="_blank" mat-fab color="accent">
<mat-icon>contact_support</mat-icon>
</a>
</mat-card-footer>
</mat-card>
</form>

View File

@ -0,0 +1,88 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { I18nService } from '../../../services/i18n.service';
import { Sort } from '@angular/material/sort';
import { UserDomainService } from '../../../services/userdomain.service';
import { FormBuilder, FormGroup, Validators, NgForm } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ConfirmDialog } from '../../../ui/confirm/confirm.component';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Clipboard } from '@angular/cdk/clipboard';
import { DyndnsTokenService } from 'src/app/services/dyndnstoken.service';
@Component({
selector: 'app-account-dyndns',
templateUrl: './dyndns.component.html',
styleUrls: ['./dyndns.component.scss']
})
export class DyndnsComponent implements OnInit {
dyndnsToken: any;
working: boolean;
domainsColumns = ["domain", "secret", "validated", "delete"];
visibilities = ["PRIVATE", "PROTECTED", "PUBLIC"];
constructor(
private dyndnsTokenService: DyndnsTokenService,
private i18n: I18nService,
private snackBar: MatSnackBar,
public dialog: MatDialog,
private clipboard: Clipboard) { }
ngOnInit(): void {
this.working = true;
this.dyndnsTokenService.get().subscribe({
next: (result: any) => {
this.dyndnsToken = result;
this.working = false;
},
error: (error) => {
this.working = false;
}
})
}
create(): void {
this.working = true;
this.dyndnsTokenService.create().subscribe({
next: (result: any) => {
this.dyndnsToken = result;
this.working = false;
},
error: (error) => {
this.working = false;
}
})
}
confirmDelete() {
const dialogRef = this.dialog.open(ConfirmDialog, {
data: {
'label': 'user.dyndns.confirmDelete',
}
})
dialogRef.afterClosed().subscribe({
next: (result) => {
if (result) {
this.dyndnsTokenService.delete().subscribe({
next: (result: any) => {
this.dyndnsToken = undefined;
}
})
}
}
});
}
copySecret() {
if (this.dyndnsToken && this.dyndnsToken.token) {
this.clipboard.copy(this.dyndnsToken.token);
this.snackBar.open(this.i18n.get("user.dyndns.token.copied", []), this.i18n.get("close", []), {
duration: 3000
});
}
}
}

View File

@ -90,9 +90,7 @@
<div [formGroup]="slotForm" class="flex wrap" fxLayoutAlign="start stretch" fxLayoutGap="24px grid"> <div [formGroup]="slotForm" class="flex wrap" fxLayoutAlign="start stretch" fxLayoutGap="24px grid">
<mat-form-field> <mat-form-field>
<mat-label>{{'borrow.items.slot.start' | i18n}}</mat-label> <mat-label>{{'borrow.items.slot.start' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="slotStartPicker" formControlName="start"> <input matInput type="datetime-local" formControlName="start">
<mat-datepicker-toggle matSuffix [for]="slotStartPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #slotStartPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'borrow.items.error.slot.start' | i18n}} {{'borrow.items.error.slot.start' | i18n}}
</mat-error> </mat-error>
@ -100,9 +98,7 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'borrow.items.slot.end' | i18n}}</mat-label> <mat-label>{{'borrow.items.slot.end' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="slotEndPicker" formControlName="end"> <input matInput type="datetime-local" formControlName="end">
<mat-datepicker-toggle matSuffix [for]="slotEndPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #slotEndPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'borrow.items.error.slot.end' | i18n}} {{'borrow.items.error.slot.end' | i18n}}
</mat-error> </mat-error>

View File

@ -24,9 +24,7 @@
<form [formGroup]="form"> <form [formGroup]="form">
<mat-form-field> <mat-form-field>
<mat-label>{{'borrow.request.start' | i18n}}</mat-label> <mat-label>{{'borrow.request.start' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="startPicker" formControlName="start"> <input matInput type="datetime-local" formControlName="start">
<mat-datepicker-toggle matSuffix [for]="startPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #startPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'borrow.request.error.start' | i18n}} {{'borrow.request.error.start' | i18n}}
</mat-error> </mat-error>
@ -34,9 +32,7 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'borrow.request.end' | i18n}}</mat-label> <mat-label>{{'borrow.request.end' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="endPicker" formControlName="end"> <input matInput type="datetime-local" formControlName="end">
<mat-datepicker-toggle matSuffix [for]="endPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #endPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'borrow.request.error.end' | i18n}} {{'borrow.request.error.end' | i18n}}
</mat-error> </mat-error>

View File

@ -27,9 +27,10 @@
{{'security.2fa.missing' | i18n}} {{'security.2fa.missing' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-slide-toggle id="keep" name="keep" [checked]="keep"> <mat-slide-toggle #toggle [checked]="keep">
{{'login.keepSession' | i18n}} {{'login.keepSession' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
<input class="hidden" type="checkbox" id="keep" name="keep" [checked]="toggle.checked">
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<a type="submit" mat-raised-button color="primary" (click)="form2FA.submit()" <a type="submit" mat-raised-button color="primary" (click)="form2FA.submit()"

View File

@ -1,3 +1,7 @@
mat-form-field { mat-form-field {
display: block; display: block;
} }
input#keep {
display: none;
}

View File

@ -21,9 +21,10 @@
{{'password.invalid.hint' | i18n}} {{'password.invalid.hint' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-slide-toggle id="keep" name="keep"> <mat-slide-toggle #toggle>
{{'login.keepSession' | i18n}} {{'login.keepSession' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
<input class="hidden" type="checkbox" id="keep" name="keep" [checked]="toggle.checked">
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<button type="submit" (click)="loginForm.submit()" mat-raised-button color="primary" <button type="submit" (click)="loginForm.submit()" mat-raised-button color="primary"

View File

@ -1,3 +1,7 @@
mat-form-field { mat-form-field {
display: block; display: block;
} }
input#keep {
display: none;
}

View File

@ -110,32 +110,23 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'jitsi.rooms.starts' | i18n}}</mat-label> <mat-label>{{'jitsi.rooms.starts' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="startsPicker" [(ngModel)]="jitsiRoom.starts" formControlName="starts" <input matInput type="datetime-local" [(ngModel)]="jitsiRoom.starts" formControlName="starts">
(dateChange)="clearModeration(jitsiRoom)">
<mat-datepicker-toggle matSuffix [for]="startsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'jitsi.rooms.error.starts' | i18n}} {{'jitsi.rooms.error.starts' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="jitsiRoom.starts"> <mat-form-field *ngIf="jitsiRoom.starts">
<input matInput [ngxMatDatetimePicker]="moderationStartsPicker" [(ngModel)]="jitsiRoom.moderationStarts"
formControlName="moderationStarts">
<mat-label>{{'jitsi.rooms.moderationStarts' | i18n}}</mat-label> <mat-label>{{'jitsi.rooms.moderationStarts' | i18n}}</mat-label>
<mat-datepicker-toggle matSuffix [for]="moderationStartsPicker"></mat-datepicker-toggle> <input matInput type="datetime-local" [(ngModel)]="jitsiRoom.moderationStarts" formControlName="moderationStarts">
<ngx-mat-datetime-picker #moderationStartsPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'jitsi.rooms.error.moderationStarts' | i18n}} {{'jitsi.rooms.error.moderationStarts' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="jitsiRoom.expires"
formControlName="expires">
<mat-label>{{'jitsi.rooms.expires' | i18n}}</mat-label> <mat-label>{{'jitsi.rooms.expires' | i18n}}</mat-label>
<mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle> <input matInput type="datetime-local" [(ngModel)]="jitsiRoom.expires" formControlName="expires">
<ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'jitsi.rooms.error.expires' | i18n}} {{'jitsi.rooms.error.expires' | i18n}}
</mat-error> </mat-error>

View File

@ -13,31 +13,23 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'jitsi.rooms.starts' | i18n}}</mat-label> <mat-label>{{'jitsi.rooms.starts' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="startsPicker" [(ngModel)]="jitsiRoom.starts" formControlName="starts" <input matInput type="datetime-local" [(ngModel)]="jitsiRoom.starts" formControlName="starts" (change)="clearModeration(jitsiRoom)">
(dateChange)="clearModeration(jitsiRoom)">
<mat-datepicker-toggle matSuffix [for]="startsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'jitsi.rooms.error.starts' | i18n}} {{'jitsi.rooms.error.starts' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field *ngIf="jitsiRoom.starts"> <mat-form-field *ngIf="jitsiRoom.starts">
<input matInput [ngxMatDatetimePicker]="moderationStartsPicker" [(ngModel)]="jitsiRoom.moderationStarts"
formControlName="moderationStarts">
<mat-label>{{'jitsi.rooms.moderationStarts' | i18n}}</mat-label> <mat-label>{{'jitsi.rooms.moderationStarts' | i18n}}</mat-label>
<mat-datepicker-toggle matSuffix [for]="moderationStartsPicker"></mat-datepicker-toggle> <input matInput type="datetime-local" [(ngModel)]="jitsiRoom.moderationStarts" formControlName="moderationStarts">
<ngx-mat-datetime-picker #moderationStartsPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'jitsi.rooms.error.moderationStarts' | i18n}} {{'jitsi.rooms.error.moderationStarts' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="jitsiRoom.expires" formControlName="expires">
<mat-label>{{'jitsi.rooms.expires' | i18n}}</mat-label> <mat-label>{{'jitsi.rooms.expires' | i18n}}</mat-label>
<mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle> <input matInput type="datetime-local" [(ngModel)]="jitsiRoom.expires" formControlName="expires">
<ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'jitsi.rooms.error.expires' | i18n}} {{'jitsi.rooms.error.expires' | i18n}}
</mat-error> </mat-error>

View File

@ -20,18 +20,14 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'partey.timeslots.starts' | i18n}}</mat-label> <mat-label>{{'partey.timeslots.starts' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="startsPicker" [(ngModel)]="timeslot.starts" formControlName="starts"> <input matInput type="datetime-local" [(ngModel)]="timeslot.starts" formControlName="starts">
<mat-datepicker-toggle matSuffix [for]="startsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #startsPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'partey.timeslots.error.starts' | i18n}} {{'partey.timeslots.error.starts' | i18n}}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'partey.timeslots.ends' | i18n}}</mat-label> <mat-label>{{'partey.timeslots.ends' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="endsPicker" [(ngModel)]="timeslot.ends" formControlName="ends"> <input matInput type="datetime-local" [(ngModel)]="timeslot.ends" formControlName="ends">
<mat-datepicker-toggle matSuffix [for]="endsPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #endsPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'partey.timeslots.error.ends' | i18n}} {{'partey.timeslots.error.ends' | i18n}}
</mat-error> </mat-error>

View File

@ -32,9 +32,7 @@
</mat-form-field> </mat-form-field>
<mat-form-field> <mat-form-field>
<mat-label>{{'partey.timeslots.filter.after' | i18n}}</mat-label> <mat-label>{{'partey.timeslots.filter.after' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="afterPicker" [formControl]="afterFormControl"> <input matInput type="datetime-local" [formControl]="afterFormControl">
<mat-datepicker-toggle matSuffix [for]="afterPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #afterPicker></ngx-mat-datetime-picker>
</mat-form-field> </mat-form-field>
</div> </div>
<table mat-table matSort [dataSource]="timeslots.content" (matSortChange)="updateSort($event)"> <table mat-table matSort [dataSource]="timeslots.content" (matSortChange)="updateSort($event)">

View File

@ -128,10 +128,7 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'urlshortener.expires' | i18n}}</mat-label> <mat-label>{{'urlshortener.expires' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="shortenedUrl.expires" <input matInput type="datetime-local" [(ngModel)]="shortenedUrl.expires" formControlName="expires">
formControlName="expires">
<mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'urlshortener.error.expires' | i18n}} {{'urlshortener.error.expires' | i18n}}
</mat-error> </mat-error>

View File

@ -50,10 +50,7 @@
<mat-form-field> <mat-form-field>
<mat-label>{{'urlshortener.expires' | i18n}}</mat-label> <mat-label>{{'urlshortener.expires' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="expiresPicker" [(ngModel)]="shortenedUrlModel.expires" <input matInput type="datetime-local" [(ngModel)]="shortenedUrlModel.expires" formControlName="expires">
formControlName="expires">
<mat-datepicker-toggle matSuffix [for]="expiresPicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #expiresPicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'urlshortener.error.expires' | i18n}} {{'urlshortener.error.expires' | i18n}}
</mat-error> </mat-error>

View File

@ -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 DyndnsTokenService {
constructor(private http: HttpClient) {
}
get() {
return this.http.get(environment.apiUrl + "/dyndns/token");
}
create() {
return this.http.post(environment.apiUrl + "/dyndns/token", undefined);
}
delete() {
return this.http.delete(environment.apiUrl + "/dyndns/token");
}
}

View File

@ -0,0 +1,23 @@
<div class="flex wrap" fxLayoutAlign="start stretch">
<div [style.flex-basis]="'33%'">
<mat-form-field>
<mat-label>{{'durationpicker.days' | i18n}}</mat-label>
<input matInput type="number" min="0" [formControl]="days">
</mat-form-field>
</div>
<div [style.flex-basis]="'33%'">
<mat-form-field>
<mat-label>{{'durationpicker.hours' | i18n}}</mat-label>
<input matInput type="number" min="0" max="23"
[formControl]="hours">
</mat-form-field>
</div>
<div [style.flex-basis]="'33%'">
<mat-form-field>
<mat-label>{{'durationpicker.minutes' | i18n}}</mat-label>
<input matInput type="number" min="0" max="59"
[formControl]="minutes">
<mat-hint align="end">{{duration}}</mat-hint>
</mat-form-field>
</div>
</div>

View File

@ -0,0 +1,4 @@
mat-form-field {
display: block;
padding-right: 8px;
}

View File

@ -0,0 +1,104 @@
import { Component, OnInit } from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import * as moment from 'moment';
@Component({
selector: 'app-datetimepicker',
templateUrl: './datetimepicker.component.html',
styleUrls: [ './datetimepicker.component.scss' ],
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: DatetimepickerComponent
}
]
})
export class DatetimepickerComponent implements OnInit, ControlValueAccessor {
days = new FormControl();
hours = new FormControl();
minutes = new FormControl();
duration: string;
model: moment.Duration;
onChange = (duration) => { };
onTouched = () => { };
isDisabled = false;
constructor() { }
ngOnInit(): void {
this.days.valueChanges.subscribe({
next: (value) => {
if (value < this.model.days()) {
this.model.subtract((this.model.days() - value), "days");
} else {
this.model.add((value - this.model.days()), "days");
}
this.checkValue();
}
})
this.hours.valueChanges.subscribe({
next: (value) => {
if (value < this.model.hours()) {
this.model.subtract((this.model.hours() - value), "hours");
} else {
this.model.add((value - this.model.hours()), "hours");
}
this.checkValue();
}
})
this.minutes.valueChanges.subscribe({
next: (value) => {
if (value < this.model.minutes()) {
this.model.subtract((this.model.minutes() - value), "minutes");
} else {
this.model.add((value - this.model.minutes()), "minutes");
}
this.checkValue();
}
})
}
checkValue() {
if (this.model.asMinutes() <= 0) {
this.days.setValue(0, { emitEvent: false });
this.hours.setValue(0, { emitEvent: false });
this.minutes.setValue(0, { emitEvent: false });
this.duration = null;
} else {
this.duration = this.model.toISOString();
}
this.onChange(this.duration);
this.onTouched();
}
writeValue(duration: string): void {
this.duration = duration;
this.model = moment.duration(this.duration);
this.days.setValue(this.model.days(), { emitEvent: false });
this.hours.setValue(this.model.hours()), { emitEvent: false };
this.minutes.setValue(this.model.minutes(), { emitEvent: false });
this.onTouched();
}
registerOnChange(onChange: any): void {
this.onChange = onChange;
}
registerOnTouched(onTouched: any): void {
this.onTouched = onTouched;
}
setDisabledState?(isDisabled: boolean): void {
this.isDisabled = isDisabled;
}
}

View File

@ -40,10 +40,7 @@
</mat-form-field> </mat-form-field>
<mat-form-field *ngSwitchCase="'DATETIME'"> <mat-form-field *ngSwitchCase="'DATETIME'">
<mat-label>{{'profileField.value' | i18n}}</mat-label> <mat-label>{{'profileField.value' | i18n}}</mat-label>
<input matInput [ngxMatDatetimePicker]="datetimePicker" [(ngModel)]="profileField.value" <input matInput type="datetime-local" [(ngModel)]="profileField.value" formControlName="value">
formControlName="value">
<mat-datepicker-toggle matSuffix [for]="datetimePicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #datetimePicker></ngx-mat-datetime-picker>
<mat-error> <mat-error>
{{'profileField.error.DATETIME' | i18n}} {{'profileField.error.DATETIME' | i18n}}
</mat-error> </mat-error>

View File

@ -8,6 +8,7 @@
// Be sure that you only ever include this mixin once! // Be sure that you only ever include this mixin once!
@include mat.core(); @include mat.core();
@import "@danielmoncada/angular-datetime-picker/assets/style/picker.min.css";
@import './variables.scss'; @import './variables.scss';