From 20e67ecd8576c4249f8b85a7b016ca6e3d24282b Mon Sep 17 00:00:00 2001 From: chatenium Date: Sat, 18 Apr 2026 19:28:51 +0200 Subject: [PATCH] Started implementing user settings -> security -> e-mail management --- public/i18n/en.json | 9 ++ .../user-settings/security/email/email.html | 97 ++++++++++++++++ .../user-settings/security/email/email.scss | 0 .../security/email/email.spec.ts | 22 ++++ .../user-settings/security/email/email.ts | 105 ++++++++++++++++++ .../security/password/password.html | 12 +- .../security/password/password.ts | 22 ++-- .../chat/user-settings/security/security.html | 36 +----- .../chat/user-settings/security/security.ts | 4 +- 9 files changed, 254 insertions(+), 53 deletions(-) create mode 100644 src/app/chat/user-settings/security/email/email.html create mode 100644 src/app/chat/user-settings/security/email/email.scss create mode 100644 src/app/chat/user-settings/security/email/email.spec.ts create mode 100644 src/app/chat/user-settings/security/email/email.ts diff --git a/public/i18n/en.json b/public/i18n/en.json index 4f107e0..3762487 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -115,6 +115,15 @@ "newPassword": "New password", "newPasswordRepeat": "Repeat new password" }, + "changeEmailDialog": { + "label": "Change e-mail address", + "labelSet": "Set e-mail address", + "labelRemove": "Remove e-mail address", + "newMail": "New e-mail address", + "currentPassword": "Current password", + "oldCode": "Code sent to the old e-mail address", + "newCode": "Code sent to the new e-mail address" + }, "label": "Keep your account safe by using as much sign in methods as possible. Also check your credentials regularly to keep them up to date.", "password": "Password", "set": "Set", diff --git a/src/app/chat/user-settings/security/email/email.html b/src/app/chat/user-settings/security/email/email.html new file mode 100644 index 0000000..42b23a5 --- /dev/null +++ b/src/app/chat/user-settings/security/email/email.html @@ -0,0 +1,97 @@ +
+ + @if (step() == 0) { +
+ @if (!changeEmailRemoveMode()) { + + + + + } + + + + + + + @if (changeEmailForm.controls['currentPassword'].dirty) { + @if (changeEmailForm.controls['currentPassword'].hasError("required")) { + + } + + @if (changeEmailForm.controls['currentPassword'].hasError("incorrect")) { + + } + } +
+ +
+ +
+ } @else { +
+ @if (!changeEmailRemoveMode()) { + + + + + } + + + + + +
+ +
+ +
+ } +
+ +
+ + {{ "chat.userSettingsDialog.security.email"|translate }} + + @if (serviceManager.currentSession()!.userData.emailSet) { +
{{ 'chat.userSettingsDialog.security.set'|translate }} +
+ } @else { +
{{ 'chat.userSettingsDialog.security.notSet'|translate }} +
+ } +
+ +
+ + + @if (serviceManager.currentSession()!.userData.emailSet) { + + } +
+
diff --git a/src/app/chat/user-settings/security/email/email.scss b/src/app/chat/user-settings/security/email/email.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/chat/user-settings/security/email/email.spec.ts b/src/app/chat/user-settings/security/email/email.spec.ts new file mode 100644 index 0000000..583023d --- /dev/null +++ b/src/app/chat/user-settings/security/email/email.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Email } from './email'; + +describe('Email', () => { + let component: Email; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Email], + }).compileComponents(); + + fixture = TestBed.createComponent(Email); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/chat/user-settings/security/email/email.ts b/src/app/chat/user-settings/security/email/email.ts new file mode 100644 index 0000000..5a54c5a --- /dev/null +++ b/src/app/chat/user-settings/security/email/email.ts @@ -0,0 +1,105 @@ +import {Component, inject, signal} from '@angular/core'; +import {TranslatePipe} from '@ngx-translate/core'; +import {TuiBadge, TuiButtonLoading, TuiInputNumber} from '@taiga-ui/kit'; +import { + TuiButton, + TuiDialog, + TuiErrorComponent, + TuiIcon, + TuiInputDirective, + TuiLabel, + TuiTextfieldComponent +} from '@taiga-ui/core'; +import {ServiceManager} from '../../../../service-manager'; +import { + AbstractControl, + FormControl, + FormGroup, + ReactiveFormsModule, + ValidationErrors, + Validators +} from '@angular/forms'; + +@Component({ + selector: 'user-settings-security-email', + imports: [ + TranslatePipe, + TuiBadge, + TuiButton, + TuiIcon, + ReactiveFormsModule, + TuiButtonLoading, + TuiDialog, + TuiErrorComponent, + TuiInputDirective, + TuiLabel, + TuiTextfieldComponent, + TuiInputNumber, + ], + templateUrl: './email.html', + styleUrl: './email.scss', +}) +export class Email { + serviceManager = inject(ServiceManager) + + step = signal(0) + changeEmailDialogOpen = signal(false) + changeEmailRemoveMode = signal(false) + changeEmailPending = signal(false) + verifyEmailPending = signal(false) + newAddress = "" + + changeEmailForm = new FormGroup({ + newAddress: new FormControl(""), + currentPassword: new FormControl(""), + }) + + verifyEmailForm = new FormGroup({ + oldCode: new FormControl(0), + newCode: new FormControl(0, {validators: [Validators.required]}) + }) + + openChangeEmailDialog(modeRemove: boolean) { + this.changeEmailDialogOpen.set(true) + this.changeEmailRemoveMode.set(modeRemove) + this.changeEmailForm.controls["currentPassword"].clearValidators() + this.changeEmailForm.controls["newAddress"].clearValidators() + if (!modeRemove) { + this.changeEmailForm.controls["newAddress"].setValidators([Validators.required]) + this.changeEmailForm.controls["newAddress"].updateValueAndValidity() + } + + this.changeEmailForm.controls["currentPassword"].setValidators([Validators.required]) + this.changeEmailForm.controls["currentPassword"].updateValueAndValidity() + } + + async changeEmail(currentPassword: string | null, newMail: string | null) { + this.changeEmailPending.set(true) + const service = this.serviceManager.currentSessionHandler + if (service) { + try { + await service.changeEmail(newMail ?? "", currentPassword ?? "") + if (!this.changeEmailRemoveMode()) { + this.newAddress = newMail ?? "" + this.verifyEmailForm.controls["oldCode"].setValidators([Validators.required]) + this.step.set(1) + } + } catch (e) { + this.changeEmailForm.controls["currentPassword"].setErrors({incorrect: true}) + } + } + } + + async verifyEmail(newCode: number | null, oldCode: number | null) { + this.verifyEmailPending.set(true) + const service = this.serviceManager.currentSessionHandler + if (service) { + try { + await service.verifyEmailChange(oldCode ?? 0, newCode ?? 0, this.newAddress) + this.changeEmailDialogOpen.set(false) + } catch (e) { + this.verifyEmailForm.controls["newCode"].setErrors({incorrect: true}) + } + } + } +} diff --git a/src/app/chat/user-settings/security/password/password.html b/src/app/chat/user-settings/security/password/password.html index dfdbc4c..98ce371 100644 --- a/src/app/chat/user-settings/security/password/password.html +++ b/src/app/chat/user-settings/security/password/password.html @@ -1,7 +1,7 @@
-
+ @if (serviceManager.currentSession()!.userData.passwordSet) { - @if (changePasswordForm.controls['currentPassword'].dirty) { - @if (changePasswordForm.controls['currentPassword'].hasError("required")) { + @if (changeEmailForm.controls['currentPassword'].dirty) { + @if (changeEmailForm.controls['currentPassword'].hasError("required")) { } - @if (changePasswordForm.controls['currentPassword'].hasError("incorrect")) { + @if (changeEmailForm.controls['currentPassword'].hasError("incorrect")) { } @@ -38,8 +38,8 @@
diff --git a/src/app/chat/user-settings/security/password/password.ts b/src/app/chat/user-settings/security/password/password.ts index 1982ccc..0c670bd 100644 --- a/src/app/chat/user-settings/security/password/password.ts +++ b/src/app/chat/user-settings/security/password/password.ts @@ -47,7 +47,7 @@ export class Password { changePasswordRemoveMode = signal(false) changePasswordPending = signal(false) - changePasswordForm = new FormGroup({ + changeEmailForm = new FormGroup({ currentPassword: new FormControl(""), newPassword: new FormControl(""), newPasswordRepeat: new FormControl("") @@ -58,18 +58,18 @@ export class Password { openChangePasswordDialog(modeRemove: boolean) { this.changePasswordDialogOpen.set(true) this.changePasswordRemoveMode.set(modeRemove) - this.changePasswordForm.controls["currentPassword"].clearValidators() - this.changePasswordForm.controls["newPassword"].clearValidators() - this.changePasswordForm.controls["newPasswordRepeat"].clearValidators() + this.changeEmailForm.controls["currentPassword"].clearValidators() + this.changeEmailForm.controls["newPassword"].clearValidators() + this.changeEmailForm.controls["newPasswordRepeat"].clearValidators() if (!modeRemove) { - this.changePasswordForm.controls["newPassword"].setValidators([Validators.required]) - this.changePasswordForm.controls["newPasswordRepeat"].setValidators([Validators.required]) - this.changePasswordForm.controls["newPassword"].updateValueAndValidity() - this.changePasswordForm.controls["newPasswordRepeat"].updateValueAndValidity() + this.changeEmailForm.controls["newPassword"].setValidators([Validators.required]) + this.changeEmailForm.controls["newPasswordRepeat"].setValidators([Validators.required]) + this.changeEmailForm.controls["newPassword"].updateValueAndValidity() + this.changeEmailForm.controls["newPasswordRepeat"].updateValueAndValidity() } - this.changePasswordForm.controls["currentPassword"].setValidators([Validators.required]) - this.changePasswordForm.controls["currentPassword"].updateValueAndValidity() + this.changeEmailForm.controls["currentPassword"].setValidators([Validators.required]) + this.changeEmailForm.controls["currentPassword"].updateValueAndValidity() } async changePassword(currentPassword: string | null, newPassword: string | null) { @@ -79,7 +79,7 @@ export class Password { try { await service.changePassword(newPassword ?? "", currentPassword ?? "") } catch (e) { - this.changePasswordForm.controls["currentPassword"].setErrors({incorrect: true}) + this.changeEmailForm.controls["currentPassword"].setErrors({incorrect: true}) } } } diff --git a/src/app/chat/user-settings/security/security.html b/src/app/chat/user-settings/security/security.html index d523a05..043b385 100644 --- a/src/app/chat/user-settings/security/security.html +++ b/src/app/chat/user-settings/security/security.html @@ -4,41 +4,7 @@

- -
-
- - {{ "chat.userSettingsDialog.security.email"|translate }} - - @if (serviceManager.currentSession()!.userData.phoneSet) { -
{{ 'chat.userSettingsDialog.security.set'|translate }} -
- } @else { -
{{ 'chat.userSettingsDialog.security.notSet'|translate }} -
- } -
- -
- - - @if (serviceManager.currentSession()!.userData.emailSet) { - - } -
-
+
diff --git a/src/app/chat/user-settings/security/security.ts b/src/app/chat/user-settings/security/security.ts index 5e56376..65619f0 100644 --- a/src/app/chat/user-settings/security/security.ts +++ b/src/app/chat/user-settings/security/security.ts @@ -13,6 +13,7 @@ import { Validators } from '@angular/forms'; import {Password} from './password/password'; +import {Email} from './email/email'; @Component({ selector: 'user-settings-security', @@ -29,7 +30,8 @@ import {Password} from './password/password'; TuiInputDirective, TuiButtonLoading, TuiErrorComponent, - Password + Password, + Email ], templateUrl: './security.html', styleUrl: './security.scss',