Merge branch 'RED-10183' into 'master'

RED-10183: use the user preferred language + component refactoring.

See merge request redactmanager/red-ui!610
This commit is contained in:
Dan Percic 2024-10-14 13:14:42 +02:00
commit 9836c2aab5
5 changed files with 64 additions and 64 deletions

View File

@ -20,6 +20,7 @@
<label [translate]="'top-bar.navigation-items.my-account.children.language.label'"></label>
<mat-form-field>
<mat-select formControlName="language">
<mat-select-trigger>{{ languageSelectLabel() | translate }}</mat-select-trigger>
<mat-option *ngFor="let language of languages" [value]="language">
{{ translations[language] | translate }}
</mat-option>
@ -41,7 +42,7 @@
<div class="dialog-actions">
<iqser-icon-button
[disabled]="form.invalid || !(profileChanged || languageChanged || themeChanged)"
[disabled]="disabled"
[label]="'user-profile-screen.actions.save' | translate"
[submit]="true"
[type]="iconButtonTypes.primary"

View File

@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed } from '@angular/core';
import { FormGroup, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import {
BaseFormComponent,
@ -19,22 +19,49 @@ import { firstValueFrom } from 'rxjs';
import { UserProfileDialogService } from '../services/user-profile-dialog.service';
import { NgForOf, NgIf } from '@angular/common';
import { MatFormField } from '@angular/material/form-field';
import { MatOption, MatSelect } from '@angular/material/select';
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
import { MatSlideToggle } from '@angular/material/slide-toggle';
import { PdfViewer } from '../../../../pdf-viewer/services/pdf-viewer.service';
import { formControlToSignal } from '@utils/functions';
import { AsControl } from '@common-ui/utils';
interface UserProfileForm {
email: string;
firstName: string;
lastName: string;
language: string;
darkTheme: boolean;
}
@Component({
templateUrl: './user-profile-screen.component.html',
styleUrls: ['./user-profile-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ReactiveFormsModule, NgIf, MatFormField, MatSelect, MatOption, NgForOf, TranslateModule, MatSlideToggle, IconButtonComponent],
imports: [
ReactiveFormsModule,
NgIf,
MatFormField,
MatSelect,
MatOption,
NgForOf,
TranslateModule,
MatSlideToggle,
IconButtonComponent,
MatSelectTrigger,
],
})
export class UserProfileScreenComponent extends BaseFormComponent implements OnInit {
#profileModel: IProfile;
export class UserProfileScreenComponent extends BaseFormComponent {
readonly form: FormGroup<AsControl<UserProfileForm>> = this.#getForm();
initialFormValue = this.form.getRawValue();
readonly translations = languagesTranslations;
readonly devMode = this._userPreferenceService.isIqserDevMode;
readonly profileKeys = ['email', 'firstName', 'lastName'];
readonly languages = this._translateService.langs;
readonly language = formControlToSignal<UserProfileForm['language']>(this.form.controls.language);
readonly languageSelectLabel = computed(() => this.translations[this.language()]);
constructor(
private readonly _userService: UserService,
private readonly _loadingService: LoadingService,
@ -49,41 +76,26 @@ export class UserProfileScreenComponent extends BaseFormComponent implements OnI
private readonly _pdfViewer: PdfViewer,
) {
super();
this._loadingService.start();
if (!this._permissionsService.has(Roles.updateMyProfile)) {
this.form.disable();
}
this._loadingService.stop();
}
get languageChanged(): boolean {
return this.#profileModel['language'] !== this.form.get('language').value;
return this.initialFormValue['language'] !== this.form.controls.language.value;
}
get themeChanged(): boolean {
return this.#profileModel['darkTheme'] !== this.form.get('darkTheme').value;
return this.initialFormValue['darkTheme'] !== this.form.controls.darkTheme.value;
}
get emailChanged(): boolean {
return this.#profileModel['email'] !== this.form.get('email').value;
return this.initialFormValue['email'] !== this.form.controls.email.value;
}
get profileChanged(): boolean {
const keys = Object.keys(this.form.getRawValue());
keys.splice(keys.indexOf('language'), 1);
keys.splice(keys.indexOf('darkTheme'), 1);
for (const key of keys) {
if (this.#profileModel[key] !== this.form.get(key).value) {
return true;
}
}
return false;
}
get languages(): string[] {
return this._translateService.langs;
}
ngOnInit() {
this._initializeForm();
return this.profileKeys.some(key => this.initialFormValue[key] !== this.form.get(key).value);
}
async save(): Promise<void> {
@ -108,16 +120,17 @@ export class UserProfileScreenComponent extends BaseFormComponent implements OnI
}
if (this.languageChanged) {
await this._languageService.change(this.form.get('language').value);
await this._languageService.change(this.form.controls.language.value);
await this._pdfViewer.instance?.UI.setLanguage(this._languageService.currentLanguage);
}
if (this.themeChanged) {
await this._userPreferenceService.saveTheme(this.form.get('darkTheme').value ? 'dark' : 'light');
await this._userPreferenceService.saveTheme(this.form.controls.darkTheme.value ? 'dark' : 'light');
}
this._initializeForm();
this.initialFormValue = this.form.getRawValue();
this._changeRef.markForCheck();
this._loadingService.stop();
this._toaster.success(_('user-profile-screen.update.success'));
} catch (e) {
this._loadingService.stop();
@ -128,35 +141,13 @@ export class UserProfileScreenComponent extends BaseFormComponent implements OnI
await this._userService.createResetPasswordAction();
}
private _getForm(): UntypedFormGroup {
#getForm() {
return this._formBuilder.group({
email: ['', [Validators.required, Validators.email]],
firstName: [''],
lastName: [''],
language: [''],
darkTheme: [false],
email: [this._userService.currentUser.email ?? '', [Validators.required, Validators.email]],
firstName: [this._userService.currentUser.firstName ?? ''],
lastName: [this._userService.currentUser.lastName ?? ''],
language: [this._userPreferenceService.getLanguage()],
darkTheme: [this._userPreferenceService.getTheme() === 'dark'],
});
}
private _initializeForm(): void {
try {
this.form = this._getForm();
if (!this._permissionsService.has(Roles.updateMyProfile)) {
this.form.disable();
}
this.#profileModel = {
email: this._userService.currentUser.email ?? '',
firstName: this._userService.currentUser.firstName ?? '',
lastName: this._userService.currentUser.lastName ?? '',
language: this._languageService.currentLanguage ?? '',
darkTheme: this._userPreferenceService.getTheme() === 'dark',
};
this.form.patchValue(this.#profileModel, { emitEvent: false });
this.initialFormValue = this.form.getRawValue();
} catch (e) {
} finally {
this._loadingService.stop();
this._changeRef.markForCheck();
}
}
}

View File

@ -2,6 +2,8 @@ import { ITrackable } from '@iqser/common-ui';
import type { List } from '@iqser/common-ui/lib/utils';
import type { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Dayjs } from 'dayjs';
import { FormControl } from '@angular/forms';
import { toSignal } from '@angular/core/rxjs-interop';
export function hexToRgb(hex: string) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
@ -143,3 +145,7 @@ export function urlFileId() {
const fileId = splitUrl[splitUrl.length - 1];
return fileId.split('?')[0];
}
export function formControlToSignal<T>(control: FormControl<T>) {
return toSignal(control.valueChanges, { initialValue: control.value });
}

View File

@ -1,6 +1,6 @@
import { inject } from '@angular/core';
import { Router, RouterStateSnapshot } from '@angular/router';
import { AsyncGuard, IqserPermissionsService, LoadingService } from '@iqser/common-ui';
import { AsyncGuard, IqserPermissionsService, LanguageService, LoadingService } from '@iqser/common-ui';
import { TenantsService } from '@iqser/common-ui/lib/tenants';
import { ConfigService } from '@services/config.service';
import { DossiersChangesService } from '@services/dossiers/dossier-changes.service';
@ -38,6 +38,7 @@ export function mainGuard(): AsyncGuard {
const tenantsService = inject(TenantsService);
const loadingService = inject(LoadingService);
const configService = inject(ConfigService);
const languageService = inject(LanguageService);
const baseHref = inject(APP_BASE_HREF);
const generalConfig$ = inject(GeneralSettingsService).getGeneralConfigurations();
@ -51,6 +52,7 @@ export function mainGuard(): AsyncGuard {
firstValueFrom(updatedDisplayName$),
]);
await languageService.setInitialLanguage();
const lastDossierTemplate = userPreferenceService.getLastDossierTemplate();
if (lastDossierTemplate && !isUsersAdminOnly) {

@ -1 +1 @@
Subproject commit 3c89b8f7e71eb9253aa40f40c1598eac2c400c71
Subproject commit 32de7758599d887c4b574d70a11b4e0382f30f0c