From 094315d1453f531fc4ff95182029f5a6a96c24df Mon Sep 17 00:00:00 2001 From: Olga Shen Date: Fri, 25 Oct 2024 15:13:42 +0800 Subject: [PATCH 1/2] feat(features/home): update profile name - Display and update profile name instead of username. - Reduce duplicate API calls for profile. - Remove unused pages: ProfilePage and PhoneVerificationPage, and the service DiaBackendAssetRefreshingService. --- src/app/app-routing.module.ts | 8 - src/app/features/contacts/contacts.page.html | 6 +- .../capture-tab/capture-tab.component.html | 2 +- .../home/capture-tab/capture-tab.component.ts | 74 ++---- .../session/information-session.service.ts | 4 +- .../home/edit-profile/edit-profile.page.ts | 105 +++------ src/app/features/home/home.page.html | 2 +- src/app/features/home/home.page.ts | 2 +- .../sending-post-capture.page.ts | 7 +- .../phone-verification-routing.module.ts | 16 -- .../phone-verification.module.ts | 17 -- .../phone-verification.page.html | 77 ------- .../phone-verification.page.scss | 19 -- .../phone-verification.page.spec.ts | 31 --- .../phone-verification.page.ts | 214 ------------------ .../profile/profile-routing.module.ts | 23 -- src/app/features/profile/profile.module.ts | 10 - src/app/features/profile/profile.page.html | 89 -------- src/app/features/profile/profile.page.scss | 29 --- src/app/features/profile/profile.page.spec.ts | 25 -- src/app/features/profile/profile.page.ts | 115 ---------- src/app/shared/avatar/avatar.component.ts | 6 +- .../contact-selection-dialog.component.html | 8 +- .../dia-backend-asset-repository.service.ts | 2 + ...a-backend-asset-refreshing.service.spec.ts | 18 -- .../dia-backend-asset-refreshing.service.ts | 58 ----- .../auth/dia-backend-auth.service.ts | 179 ++++----------- .../dia-backend-contact-repository.service.ts | 1 + src/app/utils/validation.ts | 2 - src/assets/i18n/en-us.json | 15 +- src/assets/i18n/zh-tw.json | 15 +- 31 files changed, 111 insertions(+), 1068 deletions(-) delete mode 100644 src/app/features/profile/phone-verification/phone-verification-routing.module.ts delete mode 100644 src/app/features/profile/phone-verification/phone-verification.module.ts delete mode 100644 src/app/features/profile/phone-verification/phone-verification.page.html delete mode 100644 src/app/features/profile/phone-verification/phone-verification.page.scss delete mode 100644 src/app/features/profile/phone-verification/phone-verification.page.spec.ts delete mode 100644 src/app/features/profile/phone-verification/phone-verification.page.ts delete mode 100644 src/app/features/profile/profile-routing.module.ts delete mode 100644 src/app/features/profile/profile.module.ts delete mode 100644 src/app/features/profile/profile.page.html delete mode 100644 src/app/features/profile/profile.page.scss delete mode 100644 src/app/features/profile/profile.page.spec.ts delete mode 100644 src/app/features/profile/profile.page.ts delete mode 100644 src/app/shared/dia-backend/asset/refreshing/dia-backend-asset-refreshing.service.spec.ts delete mode 100644 src/app/shared/dia-backend/asset/refreshing/dia-backend-asset-refreshing.service.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index b40f8bd48..5d97cc800 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -24,14 +24,6 @@ const routes: Routes = [ import('./features/home/home.module').then(m => m.HomePageModule), canActivate: [AuthGuard], }, - { - path: 'profile', - loadChildren: () => - import('./features/profile/profile.module').then( - m => m.ProfilePageModule - ), - canActivate: [AuthGuard], - }, { path: 'settings', loadChildren: () => diff --git a/src/app/features/contacts/contacts.page.html b/src/app/features/contacts/contacts.page.html index b728c479f..ed5777690 100644 --- a/src/app/features/contacts/contacts.page.html +++ b/src/app/features/contacts/contacts.page.html @@ -18,7 +18,11 @@ " /> - {{ contact.contact_name || contact.contact_email }} + {{ + contact.contact_profile_display_name || + contact.contact_name || + contact.contact_email + }} - {{ username$ | ngrxPush }} + {{ displayName$ | ngrxPush }} profile.description) + ); + + readonly profileBackground$ = this.diaBackendAuthService.profile$.pipe( + map(profile => profile.profile_background_thumbnail ?? '') + ); private readonly proofs$ = this.proofRepository.all$; @@ -148,8 +144,6 @@ export class CaptureTabComponent implements OnInit { private readonly diaBackendAuthService: DiaBackendAuthService, private readonly diaBackendAssetRepository: DiaBackendAssetRepository, private readonly diaBackendTransactionRepository: DiaBackendTransactionRepository, - private readonly diaBackendAssetRefreshingService: DiaBackendAsseRefreshingService, - private readonly alertController: AlertController, private readonly networkService: NetworkService, private readonly translocoService: TranslocoService, private readonly errorService: ErrorService, @@ -160,6 +154,10 @@ export class CaptureTabComponent implements OnInit { this.uploadService.pendingTasks$ .pipe(untilDestroyed(this)) .subscribe(value => (this.pendingUploadTasks = value)); + this.diaBackendAuthService + .readProfile$() + .pipe(untilDestroyed(this)) + .subscribe(); } static async openFaq() { @@ -255,52 +253,6 @@ export class CaptureTabComponent implements OnInit { .subscribe(); } - async editUsername() { - const alert = await this.alertController.create({ - header: this.translocoService.translate('editUsername'), - inputs: [ - { - name: 'username', - type: 'text', - value: await this.diaBackendAuthService.getUsername(), - }, - ], - buttons: [ - { - text: this.translocoService.translate('cancel'), - role: 'cancel', - }, - { - text: this.translocoService.translate('ok'), - handler: value => this.updateUsername(value.username), - }, - ], - }); - return alert.present(); - } - - private updateUsername(username: string) { - const action$ = this.diaBackendAuthService - .updateUser$({ username }) - .pipe(catchError((err: unknown) => this.handleUpdateUsernameError$(err))); - return this.blockingActionService - .run$(action$) - .pipe(untilDestroyed(this)) - .subscribe(); - } - - private handleUpdateUsernameError$(err: unknown) { - if (err instanceof HttpErrorResponse) { - const errorType = err.error.error?.type; - if (errorType === 'duplicate_username') { - return this.errorService.toastError$( - this.translocoService.translate(`error.diaBackend.${errorType}`) - ); - } - } - return this.errorService.toastError$(err); - } - // eslint-disable-next-line class-methods-use-this keyDescendingOrder( a: KeyValue, diff --git a/src/app/features/home/details/information/session/information-session.service.ts b/src/app/features/home/details/information/session/information-session.service.ts index 8145fb771..456efd97c 100644 --- a/src/app/features/home/details/information/session/information-session.service.ts +++ b/src/app/features/home/details/information/session/information-session.service.ts @@ -89,8 +89,8 @@ export class DetailedCapture { readonly creator$ = defer(() => { if (this.proofOrDiaBackendAsset instanceof Proof) - return this.diaBackendAuthService.username$; - return of(this.proofOrDiaBackendAsset.creator_name); + return this.diaBackendAuthService.displayName$; + return of(this.proofOrDiaBackendAsset.creator_profile_display_name ?? ''); }); readonly geolocation$ = defer(async () => { diff --git a/src/app/features/home/edit-profile/edit-profile.page.ts b/src/app/features/home/edit-profile/edit-profile.page.ts index 32eac6ad5..6410da327 100644 --- a/src/app/features/home/edit-profile/edit-profile.page.ts +++ b/src/app/features/home/edit-profile/edit-profile.page.ts @@ -1,19 +1,11 @@ -import { HttpErrorResponse } from '@angular/common/http'; import { Component } from '@angular/core'; import { FormControl, UntypedFormGroup } from '@angular/forms'; import { NavController } from '@ionic/angular'; import { TranslocoService } from '@ngneat/transloco'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { FormlyFieldConfig } from '@ngx-formly/core'; -import { combineLatest, forkJoin } from 'rxjs'; -import { - catchError, - first, - map, - shareReplay, - switchMap, - tap, -} from 'rxjs/operators'; +import { combineLatest } from 'rxjs'; +import { catchError, first, map, tap } from 'rxjs/operators'; import { BlockingActionService } from '../../../shared/blocking-action/blocking-action.service'; import { DiaBackendAuthService } from '../../../shared/dia-backend/auth/dia-backend-auth.service'; import { ErrorService } from '../../../shared/error/error.service'; @@ -27,22 +19,21 @@ import { NetworkService } from '../../../shared/network/network.service'; }) export class EditProfilePage { readonly networkConnected$ = this.networkService.connected$; - readonly username$ = this.diaBackendAuthService.username$; - readonly avatar$ = this.diaBackendAuthService.avatar$.pipe( - shareReplay({ bufferSize: 1, refCount: true }) + readonly displayName$ = this.diaBackendAuthService.profile$.pipe( + map(profile => profile.display_name) ); - readonly profile$ = this.diaBackendAuthService - .readProfile$() - .pipe(shareReplay({ bufferSize: 1, refCount: true })); - readonly description$ = this.profile$.pipe( + readonly description$ = this.diaBackendAuthService.profile$.pipe( map(profile => profile.description) ); - readonly background$ = this.profile$.pipe( + readonly avatar$ = this.diaBackendAuthService.profile$.pipe( + map(profile => profile.profile_picture_thumbnail) + ); + readonly background$ = this.diaBackendAuthService.profile$.pipe( map(profile => profile.profile_background_thumbnail) ); readonly form = new UntypedFormGroup({}); model: EditProfileFormModel = { - username: '', + displayName: '', description: '', profilePicture: undefined, profileBackground: undefined, @@ -67,27 +58,27 @@ export class EditProfilePage { createFormFields() { combineLatest([ - this.translocoService.selectTranslate('home.editProfile.username'), + this.translocoService.selectTranslate('home.editProfile.profileName'), this.translocoService.selectTranslate('home.editProfile.description'), ]) .pipe( - tap(([usernameTranslation, descriptionTranslation]) => { + tap(([profileNameTranslation, descriptionTranslation]) => { this.fields = [ { - key: 'username', + key: 'displayName', type: 'input', templateOptions: { - label: usernameTranslation, - placeholder: usernameTranslation, + label: profileNameTranslation, + placeholder: profileNameTranslation, appearance: 'outline', }, validators: { - username: { - expression: (c: FormControl) => /^.{1,21}$/.test(c.value), + displayName: { + expression: (c: FormControl) => /^.{1,15}$/.test(c.value), message: () => this.translocoService.translate( 'home.editProfile.error.mustBeBetween', - { min: 1, max: 21 } + { min: 1, max: 15 } ), }, }, @@ -102,12 +93,12 @@ export class EditProfilePage { rows: 4, }, validators: { - username: { - expression: (c: FormControl) => /^.{0,255}$/.test(c.value), + description: { + expression: (c: FormControl) => /^.{0,125}$/.test(c.value), message: () => this.translocoService.translate( 'home.editProfile.error.mustBeBetween', - { min: 0, max: 255 } + { min: 0, max: 125 } ), }, }, @@ -130,11 +121,11 @@ export class EditProfilePage { } populateFormFields() { - combineLatest([this.username$, this.description$]) + combineLatest([this.displayName$, this.description$]) .pipe( first(), - tap(([username, description]) => { - this.model = { ...this.model, username, description }; + tap(([displayName, description]) => { + this.model = { ...this.model, displayName, description }; }) ) .subscribe(); @@ -151,19 +142,11 @@ export class EditProfilePage { } async onSubmit() { - const updateUserNameAction$ = this.blockingActionService - .run$( - this.diaBackendAuthService - .updateUser$({ username: this.model.username }) - .pipe( - catchError((err: unknown) => this.handleUpdateUsernameError$(err)) - ) - ) - .pipe(untilDestroyed(this)); - const updateProfileAction$ = this.blockingActionService + this.blockingActionService .run$( this.diaBackendAuthService .updateProfile$({ + displayName: this.model.displayName, description: this.model.description, profilePicture: this.model.profilePicture, profileBackground: this.model.profileBackground, @@ -173,39 +156,7 @@ export class EditProfilePage { ) ) .pipe( - switchMap(() => this.diaBackendAuthService.syncUser$()), - untilDestroyed(this) - ); - forkJoin([updateUserNameAction$, updateProfileAction$]) - .pipe(tap(() => this.navController.back())) - .subscribe(); - } - - private handleUpdateUsernameError$(err: unknown) { - if (err instanceof HttpErrorResponse) { - const errorType = err.error.error?.type; - if (errorType === 'duplicate_username') { - return this.errorService.toastError$( - this.translocoService.translate(`error.diaBackend.${errorType}`) - ); - } - } - return this.errorService.toastError$(err); - } - - private updateProfile() { - const updateProfileAction$ = this.diaBackendAuthService - .updateProfile$({ - description: this.model.description, - profilePicture: this.model.profilePicture, - profileBackground: this.model.profileBackground, - }) - .pipe(catchError((err: unknown) => this.handleUpdateProfileError(err))); - - this.blockingActionService - .run$(updateProfileAction$) - .pipe( - switchMap(() => this.diaBackendAuthService.syncUser$()), + tap(() => this.navController.back()), untilDestroyed(this) ) .subscribe(); @@ -217,7 +168,7 @@ export class EditProfilePage { } interface EditProfileFormModel { - username: string; + displayName: string; description: string; profilePicture: File | undefined; profileBackground: File | undefined; diff --git a/src/app/features/home/home.page.html b/src/app/features/home/home.page.html index d0a8e5414..f5085ead0 100644 --- a/src/app/features/home/home.page.html +++ b/src/app/features/home/home.page.html @@ -4,7 +4,7 @@ - {{ username$ | ngrxPush }} + {{ displayName$ | ngrxPush }} diff --git a/src/app/features/home/home.page.ts b/src/app/features/home/home.page.ts index 09a252800..2cad1a9d0 100644 --- a/src/app/features/home/home.page.ts +++ b/src/app/features/home/home.page.ts @@ -66,7 +66,7 @@ export class HomePage { private readonly collectionTabIndex = 2; selectedTabIndex = this.initialTabIndex; - readonly username$ = this.diaBackendAuthService.username$; + readonly displayName$ = this.diaBackendAuthService.displayName$; readonly hasNewInbox$ = this.diaBackendTransactionRepository.inbox$.pipe( catchError((err: unknown) => this.errorService.toastError$(err)), diff --git a/src/app/features/home/sending-post-capture/sending-post-capture.page.ts b/src/app/features/home/sending-post-capture/sending-post-capture.page.ts index e436257ce..3e29abfb5 100644 --- a/src/app/features/home/sending-post-capture/sending-post-capture.page.ts +++ b/src/app/features/home/sending-post-capture/sending-post-capture.page.ts @@ -78,6 +78,7 @@ export class SendingPostCapturePage { of({ contact_email: email, contact_name: email, + contact_profile_display_name: email, contact_profile_picture_thumbnail: '/assets/images/avatar-placeholder.png', }) @@ -112,7 +113,7 @@ export class SendingPostCapturePage { caption: this.message !== '' ? this.message : asset.caption, source_transaction: { id: '', - sender: asset.owner_name, + sender: asset.owner_profile_display_name, receiver_email: receiverEmail, created_at: '', fulfilled_at: formatDate(Date.now(), 'short', 'en-US'), @@ -127,8 +128,8 @@ export class SendingPostCapturePage { }) ); - readonly ownerAvatar$ = this.diaBackendAuthService.avatar$.pipe( - shareReplay({ bufferSize: 1, refCount: true }) + readonly ownerAvatar$ = this.diaBackendAuthService.profile$.pipe( + map(profile => profile.profile_picture_thumbnail) ); message = ''; diff --git a/src/app/features/profile/phone-verification/phone-verification-routing.module.ts b/src/app/features/profile/phone-verification/phone-verification-routing.module.ts deleted file mode 100644 index 39363a392..000000000 --- a/src/app/features/profile/phone-verification/phone-verification-routing.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { PhoneVerificationPage } from './phone-verification.page'; - -const routes: Routes = [ - { - path: '', - component: PhoneVerificationPage, - }, -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], -}) -export class PhoneVerificationPageRoutingModule {} diff --git a/src/app/features/profile/phone-verification/phone-verification.module.ts b/src/app/features/profile/phone-verification/phone-verification.module.ts deleted file mode 100644 index 27c2f730f..000000000 --- a/src/app/features/profile/phone-verification/phone-verification.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { NgModule } from '@angular/core'; -import { FormlyModule } from '@ngx-formly/core'; -import { FormlyMaterialModule } from '@ngx-formly/material'; -import { SharedModule } from '../../../shared/shared.module'; -import { PhoneVerificationPageRoutingModule } from './phone-verification-routing.module'; -import { PhoneVerificationPage } from './phone-verification.page'; - -@NgModule({ - imports: [ - SharedModule, - FormlyModule, - FormlyMaterialModule, - PhoneVerificationPageRoutingModule, - ], - declarations: [PhoneVerificationPage], -}) -export class PhoneVerificationPageModule {} diff --git a/src/app/features/profile/phone-verification/phone-verification.page.html b/src/app/features/profile/phone-verification/phone-verification.page.html deleted file mode 100644 index e82174850..000000000 --- a/src/app/features/profile/phone-verification/phone-verification.page.html +++ /dev/null @@ -1,77 +0,0 @@ - - - {{ t('verification.verification') }} - -
-

{{ t('verification.verification') }}

-

{{ t('verification.phoneVerificationInstruction') }}

-
- - - - -
-
- - - - - -
-
diff --git a/src/app/features/profile/phone-verification/phone-verification.page.scss b/src/app/features/profile/phone-verification/phone-verification.page.scss deleted file mode 100644 index 1f8e59556..000000000 --- a/src/app/features/profile/phone-verification/phone-verification.page.scss +++ /dev/null @@ -1,19 +0,0 @@ -mat-toolbar { - span { - padding-right: 40px; - } -} - -.page-content { - padding-top: 40px; - padding-left: 20px; - padding-right: 20px; - - form { - margin-top: 40px; - } - - .submit-button { - width: 100%; - } -} diff --git a/src/app/features/profile/phone-verification/phone-verification.page.spec.ts b/src/app/features/profile/phone-verification/phone-verification.page.spec.ts deleted file mode 100644 index 077a939d2..000000000 --- a/src/app/features/profile/phone-verification/phone-verification.page.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { FormlyModule } from '@ngx-formly/core'; -import { FormlyMaterialModule } from '@ngx-formly/material'; -import { SharedTestingModule } from '../../../shared/shared-testing.module'; -import { PhoneVerificationPage } from './phone-verification.page'; - -describe('PhoneVerificationPage', () => { - let component: PhoneVerificationPage; - let fixture: ComponentFixture; - - beforeEach( - waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [PhoneVerificationPage], - imports: [ - SharedTestingModule, - FormlyModule.forChild(), - FormlyMaterialModule, - ], - }).compileComponents(); - - fixture = TestBed.createComponent(PhoneVerificationPage); - component = fixture.componentInstance; - fixture.detectChanges(); - }) - ); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/features/profile/phone-verification/phone-verification.page.ts b/src/app/features/profile/phone-verification/phone-verification.page.ts deleted file mode 100644 index 5bc2039f8..000000000 --- a/src/app/features/profile/phone-verification/phone-verification.page.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { HttpErrorResponse } from '@angular/common/http'; -import { Component } from '@angular/core'; -import { UntypedFormGroup } from '@angular/forms'; -import { MatSnackBar } from '@angular/material/snack-bar'; -import { ActivatedRoute, Router } from '@angular/router'; -import { TranslocoService } from '@ngneat/transloco'; -import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { FormlyFieldConfig } from '@ngx-formly/core'; -import { combineLatest, timer } from 'rxjs'; -import { - catchError, - finalize, - first, - map, - switchMap, - take, - tap, -} from 'rxjs/operators'; -import { BlockingActionService } from '../../../shared/blocking-action/blocking-action.service'; -import { DiaBackendAuthService } from '../../../shared/dia-backend/auth/dia-backend-auth.service'; -import { ErrorService } from '../../../shared/error/error.service'; -import { - TEL_REGEXP, - VERIFICATION_CODE_REGEXP, -} from '../../../utils/validation'; - -@UntilDestroy({ checkProperties: true }) -@Component({ - selector: 'app-phone-verification', - templateUrl: './phone-verification.page.html', - styleUrls: ['./phone-verification.page.scss'], -}) -export class PhoneVerificationPage { - phoneNumberForm = new UntypedFormGroup({}); - phoneNumberModel: phoneNumberFormModel = { phoneNumber: '' }; - phoneNumberFields: FormlyFieldConfig[] = []; - - verificationCodeForm = new UntypedFormGroup({}); - verificationCodeModel: verificationCodeModel = { verificationCode: '' }; - verificationCodeFields: FormlyFieldConfig[] = []; - - hasSentPhoneVerification = false; - secondsRemained = 0; - - readonly phoneVerified$ = this.diaBackendAuthService.phoneVerified$; - - constructor( - private readonly blockingActionService: BlockingActionService, - private readonly diaBackendAuthService: DiaBackendAuthService, - private readonly errorService: ErrorService, - private readonly translocoService: TranslocoService, - private readonly snackBar: MatSnackBar, - private readonly router: Router, - private readonly route: ActivatedRoute - ) { - this.createFormFields(); - } - - private createFormFields() { - combineLatest([ - this.translocoService.selectTranslate('verification.enterPhoneNumber'), - this.translocoService.selectTranslate('verification.phonePlaceHolder'), - this.translocoService.selectTranslate( - 'verification.enterVerificationCode' - ), - this.translocoService.selectTranslate( - 'verification.verificationCodePlaceHolder' - ), - ]) - .pipe( - tap( - ([ - enterPhoneNumberTranslation, - phonePlaceHolderTranslation, - enterVerificationCodeTranslation, - verificationCodePlaceHolderTranslation, - ]) => { - this.phoneNumberFields = [ - { - fieldGroup: [ - { - key: 'phoneNumber', - type: 'input', - templateOptions: { - label: enterPhoneNumberTranslation, - type: 'tel', - placeholder: phonePlaceHolderTranslation, - required: true, - pattern: TEL_REGEXP, - }, - }, - ], - }, - ]; - this.verificationCodeFields = [ - { - fieldGroup: [ - { - key: 'verificationCode', - type: 'input', - templateOptions: { - label: enterVerificationCodeTranslation, - type: 'text', - placeholder: verificationCodePlaceHolderTranslation, - required: true, - pattern: VERIFICATION_CODE_REGEXP, - }, - }, - ], - }, - ]; - } - ), - untilDestroyed(this) - ) - .subscribe(); - } - - onPhoneNumberFormSubmit() { - const RESEND_COOLDOWN_TICKS = 60; - const TICK_INTERVAL = 1000; - const countdown$ = timer(0, TICK_INTERVAL).pipe( - take(RESEND_COOLDOWN_TICKS), - map(tick => RESEND_COOLDOWN_TICKS - tick - 1), - tap(cooldown => (this.secondsRemained = cooldown)), - finalize(() => (this.secondsRemained = 0)) - ); - const action$ = this.diaBackendAuthService - .sendPhoneVerification$(this.phoneNumberModel.phoneNumber) - .pipe(catchError((err: unknown) => this.handlePhoneSubmitError$(err))); - return this.blockingActionService - .run$(action$, { - message: this.translocoService.translate('message.pleaseWait'), - }) - .pipe( - tap(() => (this.hasSentPhoneVerification = true)), - switchMap(() => countdown$), - untilDestroyed(this) - ) - .subscribe(); - } - - onVerificationCodeFormSubmit() { - const CLOSE_DELAY = 3000; - const countdown$ = timer(CLOSE_DELAY).pipe( - first(), - finalize(() => - this.router.navigate(['..'], { - relativeTo: this.route, - }) - ) - ); - const action$ = this.diaBackendAuthService - .verifyPhoneVerification$( - this.phoneNumberModel.phoneNumber, - this.verificationCodeModel.verificationCode - ) - .pipe(catchError((err: unknown) => this.handleVerificationError$(err))); - return this.blockingActionService - .run$(action$, { - message: this.translocoService.translate('message.pleaseWait'), - }) - .pipe( - tap(() => - this.snackBar.open( - this.translocoService.translate('message.verificationSuccess') - ) - ), - switchMap(() => countdown$), - untilDestroyed(this) - ) - .subscribe(); - } - - private handlePhoneSubmitError$(err: unknown) { - if (err instanceof HttpErrorResponse) { - const errorType = err.error.error?.type; - if ( - errorType === 'validation_error' || - errorType === 'throttled' || - errorType === 'external_api_error' || - errorType === 'duplicate_phone_number' - ) { - return this.errorService.toastError$( - this.translocoService.translate(`error.diaBackend.${errorType}`) - ); - } - } - return this.errorService.toastError$(err); - } - - private handleVerificationError$(err: unknown) { - if (err instanceof HttpErrorResponse) { - const errorType = err.error.error?.type; - if ( - errorType === 'phone_verification_failed' || - errorType === 'phone_verification_code_expired' || - errorType === 'throttled' - ) - return this.errorService.toastError$( - this.translocoService.translate(`error.diaBackend.${errorType}`) - ); - } - return this.errorService.toastError$(err); - } -} - -interface phoneNumberFormModel { - phoneNumber: string; -} - -interface verificationCodeModel { - verificationCode: string; -} diff --git a/src/app/features/profile/profile-routing.module.ts b/src/app/features/profile/profile-routing.module.ts deleted file mode 100644 index 32f8c21ed..000000000 --- a/src/app/features/profile/profile-routing.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { ProfilePage } from './profile.page'; - -const routes: Routes = [ - { - path: '', - component: ProfilePage, - }, - { - path: 'phone-verification', - loadChildren: () => - import('./phone-verification/phone-verification.module').then( - m => m.PhoneVerificationPageModule - ), - }, -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], -}) -export class ProfilePageRoutingModule {} diff --git a/src/app/features/profile/profile.module.ts b/src/app/features/profile/profile.module.ts deleted file mode 100644 index c8c97c51c..000000000 --- a/src/app/features/profile/profile.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { NgModule } from '@angular/core'; -import { SharedModule } from '../../shared/shared.module'; -import { ProfilePageRoutingModule } from './profile-routing.module'; -import { ProfilePage } from './profile.page'; - -@NgModule({ - imports: [SharedModule, ProfilePageRoutingModule], - declarations: [ProfilePage], -}) -export class ProfilePageModule {} diff --git a/src/app/features/profile/profile.page.html b/src/app/features/profile/profile.page.html deleted file mode 100644 index f6ab43a07..000000000 --- a/src/app/features/profile/profile.page.html +++ /dev/null @@ -1,89 +0,0 @@ - - - {{ t('profile') }} - -
- -
-
- - - account_circle -
{{ t('username') }}
-
{{ username$ | ngrxPush }}
- -
- - contact_phone -
{{ t('verification.phoneVerification') }}
-
- {{ t('verification.verified') }} -
- -
- {{ t('verification.clickToVerify') }} -
-
- -
- - contact_mail -
{{ t('verification.emailVerification') }}
-
- {{ t('verification.verified') }} -
- -
- {{ t('verification.clickToVerify') }} -
-
- -
- - email -
{{ t('email') }}
-
{{ email$ | ngrxPush }}
-
-
- -
diff --git a/src/app/features/profile/profile.page.scss b/src/app/features/profile/profile.page.scss deleted file mode 100644 index f28e066df..000000000 --- a/src/app/features/profile/profile.page.scss +++ /dev/null @@ -1,29 +0,0 @@ -mat-toolbar { - span { - padding-right: 40px; - } -} - -app-avatar { - width: 64px; - height: 64px; - margin: 16px auto; -} - -.page-content { - button.expand { - margin-bottom: 8px; - } - - .success-icon { - color: var(--noir-success); - } - - .warn-icon { - color: var(--noir-warn); - } - - .num-balance { - margin-right: 8px; - } -} diff --git a/src/app/features/profile/profile.page.spec.ts b/src/app/features/profile/profile.page.spec.ts deleted file mode 100644 index cd8199a67..000000000 --- a/src/app/features/profile/profile.page.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { SharedTestingModule } from '../../shared/shared-testing.module'; -import { ProfilePage } from './profile.page'; - -describe('ProfilePage', () => { - let component: ProfilePage; - let fixture: ComponentFixture; - - beforeEach( - waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ProfilePage], - imports: [SharedTestingModule], - }).compileComponents(); - - fixture = TestBed.createComponent(ProfilePage); - component = fixture.componentInstance; - fixture.detectChanges(); - }) - ); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/features/profile/profile.page.ts b/src/app/features/profile/profile.page.ts deleted file mode 100644 index 72c60057f..000000000 --- a/src/app/features/profile/profile.page.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { Component } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { AlertController } from '@ionic/angular'; -import { TranslocoService } from '@ngneat/transloco'; -import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { defer, forkJoin, iif } from 'rxjs'; -import { catchError, concatMap, concatMapTo } from 'rxjs/operators'; -import { BlockingActionService } from '../../shared/blocking-action/blocking-action.service'; -import { ConfirmAlert } from '../../shared/confirm-alert/confirm-alert.service'; -import { Database } from '../../shared/database/database.service'; -import { DiaBackendAuthService } from '../../shared/dia-backend/auth/dia-backend-auth.service'; -import { ErrorService } from '../../shared/error/error.service'; -import { MediaStore } from '../../shared/media/media-store/media-store.service'; -import { PreferenceManager } from '../../shared/preference-manager/preference-manager.service'; -import { reloadApp } from '../../utils/miscellaneous'; - -@UntilDestroy({ checkProperties: true }) -@Component({ - selector: 'app-profile', - templateUrl: './profile.page.html', - styleUrls: ['./profile.page.scss'], -}) -export class ProfilePage { - readonly username$ = this.diaBackendAuthService.username$; - readonly email$ = this.diaBackendAuthService.email$; - readonly phoneVerified$ = this.diaBackendAuthService.phoneVerified$; - readonly emailVerified$ = this.diaBackendAuthService.emailVerified$; - - constructor( - private readonly database: Database, - private readonly preferenceManager: PreferenceManager, - private readonly mediaStore: MediaStore, - private readonly blockingActionService: BlockingActionService, - private readonly errorService: ErrorService, - private readonly translocoService: TranslocoService, - private readonly diaBackendAuthService: DiaBackendAuthService, - private readonly confirmAlert: ConfirmAlert, - private readonly alertController: AlertController, - private readonly router: Router, - private readonly route: ActivatedRoute - ) {} - - ionViewWillEnter() { - forkJoin([this.diaBackendAuthService.syncUser$()]) - .pipe(untilDestroyed(this)) - .subscribe(); - } - - async editUsername() { - const alert = await this.alertController.create({ - header: this.translocoService.translate('editUsername'), - inputs: [ - { - name: 'username', - type: 'text', - value: await this.diaBackendAuthService.getUsername(), - }, - ], - buttons: [ - { - text: this.translocoService.translate('cancel'), - role: 'cancel', - }, - { - text: this.translocoService.translate('ok'), - handler: value => this.updateUsername(value.username), - }, - ], - }); - return alert.present(); - } - - private updateUsername(username: string) { - const action$ = this.diaBackendAuthService - .updateUser$({ username }) - .pipe(catchError((err: unknown) => this.errorService.toastError$(err))); - return this.blockingActionService - .run$(action$) - .pipe(untilDestroyed(this)) - .subscribe(); - } - - async phoneVerification() { - return this.router.navigate(['phone-verification'], { - relativeTo: this.route, - }); - } - - async emailVerification() { - return this.router.navigate(['email-verification'], { - relativeTo: this.route, - }); - } - - logout() { - const action$ = defer(() => this.mediaStore.clear()).pipe( - concatMapTo(defer(() => this.database.clear())), - concatMapTo(defer(() => this.preferenceManager.clear())), - concatMapTo(defer(reloadApp)), - catchError((err: unknown) => this.errorService.toastError$(err)) - ); - return defer(() => - this.confirmAlert.present({ - message: this.translocoService.translate('message.confirmLogout'), - }) - ) - .pipe( - concatMap(result => - iif(() => result, this.blockingActionService.run$(action$)) - ), - untilDestroyed(this) - ) - .subscribe(); - } -} diff --git a/src/app/shared/avatar/avatar.component.ts b/src/app/shared/avatar/avatar.component.ts index a0f6edd0e..ae2547066 100644 --- a/src/app/shared/avatar/avatar.component.ts +++ b/src/app/shared/avatar/avatar.component.ts @@ -1,7 +1,7 @@ import { Component, ElementRef, Input, ViewChild } from '@angular/core'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { of, ReplaySubject } from 'rxjs'; -import { catchError, concatMap, first, shareReplay, tap } from 'rxjs/operators'; +import { catchError, concatMap, first, map, tap } from 'rxjs/operators'; import { isNonNullable } from '../../utils/rx-operators/rx-operators'; import { DiaBackendAuthService } from '../dia-backend/auth/dia-backend-auth.service'; import { ErrorService } from '../error/error.service'; @@ -23,8 +23,8 @@ export class AvatarComponent { if (value) this.avatarInput$.next(value.nativeElement); } - readonly avatar$ = this.diaBackendAuthService.avatar$.pipe( - shareReplay({ bufferSize: 1, refCount: true }) + readonly avatar$ = this.diaBackendAuthService.profile$.pipe( + map(profile => profile.profile_picture_thumbnail) ); @Input() diff --git a/src/app/shared/contact-selection-dialog/contact-selection-dialog.component.html b/src/app/shared/contact-selection-dialog/contact-selection-dialog.component.html index 3d4dc671b..4b6de6687 100644 --- a/src/app/shared/contact-selection-dialog/contact-selection-dialog.component.html +++ b/src/app/shared/contact-selection-dialog/contact-selection-dialog.component.html @@ -19,7 +19,13 @@

{{ t('selectContact') }}

'/assets/images/avatar-placeholder.png' " /> -
{{ contact.contact_name || contact.contact_email }}
+
+ {{ + contact.contact_profile_display_name || + contact.contact_name || + contact.contact_email + }} +
share diff --git a/src/app/shared/dia-backend/asset/dia-backend-asset-repository.service.ts b/src/app/shared/dia-backend/asset/dia-backend-asset-repository.service.ts index 8c7624578..fdc9f09b9 100644 --- a/src/app/shared/dia-backend/asset/dia-backend-asset-repository.service.ts +++ b/src/app/shared/dia-backend/asset/dia-backend-asset-repository.service.ts @@ -332,6 +332,7 @@ export interface DiaBackendAsset extends Tuple { readonly is_original_owner: boolean; readonly owner: string; readonly owner_name: string; + readonly owner_profile_display_name: string; readonly owner_addresses: OwnerAddresses; readonly asset_file: string; readonly asset_file_thumbnail: string; @@ -343,6 +344,7 @@ export interface DiaBackendAsset extends Tuple { readonly source_transaction: DiaBackendAssetTransaction | null; readonly parsed_meta: DiaBackendAssetParsedMeta; readonly creator_name: string; + readonly creator_profile_display_name: string | null; readonly supporting_file: string | null; readonly source_type: 'original' | 'post_capture' | 'store'; readonly cai_file: string; diff --git a/src/app/shared/dia-backend/asset/refreshing/dia-backend-asset-refreshing.service.spec.ts b/src/app/shared/dia-backend/asset/refreshing/dia-backend-asset-refreshing.service.spec.ts deleted file mode 100644 index d4390fbfe..000000000 --- a/src/app/shared/dia-backend/asset/refreshing/dia-backend-asset-refreshing.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { SharedTestingModule } from '../../../shared-testing.module'; -import { DiaBackendAsseRefreshingService } from './dia-backend-asset-refreshing.service'; - -describe('DiaBackendAssetUploadingService', () => { - let service: DiaBackendAsseRefreshingService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [SharedTestingModule], - }); - service = TestBed.inject(DiaBackendAsseRefreshingService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/src/app/shared/dia-backend/asset/refreshing/dia-backend-asset-refreshing.service.ts b/src/app/shared/dia-backend/asset/refreshing/dia-backend-asset-refreshing.service.ts deleted file mode 100644 index 2b9690c6a..000000000 --- a/src/app/shared/dia-backend/asset/refreshing/dia-backend-asset-refreshing.service.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { HttpErrorResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { EMPTY, forkJoin, of } from 'rxjs'; -import { catchError, concatMap, first } from 'rxjs/operators'; -import { HttpErrorCode } from '../../../error/error.service'; -import { ProofRepository } from '../../../repositories/proof/proof-repository.service'; -import { DiaBackendAssetRepository } from '../dia-backend-asset-repository.service'; -import { DiaBackendAssetPrefetchingService } from '../prefetching/dia-backend-asset-prefetching.service'; -import { DiaBackendAssetUploadingService } from '../uploading/dia-backend-asset-uploading.service'; - -@UntilDestroy() -@Injectable({ - providedIn: 'root', -}) -export class DiaBackendAsseRefreshingService { - private pendingUploadTasks = 0; - - constructor( - private readonly assetRepository: DiaBackendAssetRepository, - private readonly proofRepository: ProofRepository, - private readonly diaBackendAssetPrefetchingService: DiaBackendAssetPrefetchingService, - private readonly uploadService: DiaBackendAssetUploadingService - ) { - this.uploadService.pendingTasks$ - .pipe(untilDestroyed(this)) - .subscribe(value => (this.pendingUploadTasks = value)); - } - - refresh() { - // Don't refresh if there are still captures being uploaded. - if (this.pendingUploadTasks > 0) { - return EMPTY; - } - return this.proofRepository.all$.pipe( - first(), - concatMap(proofs => { - if (proofs.length === 0) return of([]); - return forkJoin( - proofs.map(proof => - this.assetRepository.fetchByProof$(proof).pipe( - catchError((err: unknown) => { - if ( - err instanceof HttpErrorResponse && - err.status === HttpErrorCode.NOT_FOUND - ) { - this.proofRepository.remove(proof); - } - return EMPTY; - }) - ) - ) - ); - }), - concatMap(() => this.diaBackendAssetPrefetchingService.prefetch()) - ); - } -} diff --git a/src/app/shared/dia-backend/auth/dia-backend-auth.service.ts b/src/app/shared/dia-backend/auth/dia-backend-auth.service.ts index d43970e3f..34e25417c 100644 --- a/src/app/shared/dia-backend/auth/dia-backend-auth.service.ts +++ b/src/app/shared/dia-backend/auth/dia-backend-auth.service.ts @@ -3,20 +3,24 @@ import { Injectable } from '@angular/core'; import { Device } from '@capacitor/device'; import { Storage } from '@capacitor/storage'; import { isEqual, reject } from 'lodash-es'; -import { Observable, Subject, combineLatest, defer, forkJoin, of } from 'rxjs'; +import { + Observable, + ReplaySubject, + combineLatest, + defer, + forkJoin, + of, +} from 'rxjs'; import { concatMap, concatMapTo, distinctUntilChanged, filter, map, - pluck, - repeatWhen, switchMap, tap, } from 'rxjs/operators'; import { secondSince } from '../../../utils/date'; -import { isNonNullable } from '../../../utils/rx-operators/rx-operators'; import { LanguageService } from '../../language/service/language.service'; import { PreferenceManager } from '../../preference-manager/preference-manager.service'; import { PushNotificationService } from '../../push-notification/push-notification.service'; @@ -40,6 +44,8 @@ export class DiaBackendAuthService { readonly username$ = this.preferences.getString$(PrefKeys.USERNAME); + readonly displayName$ = this.preferences.getString$(PrefKeys.DISPLAY_NAME); + readonly email$ = this.preferences.getString$(PrefKeys.EMAIL); readonly token$ = this.preferences @@ -50,47 +56,7 @@ export class DiaBackendAuthService { map(token => ({ authorization: `token ${token}` })) ); - private readonly refreshAvatar$ = new Subject(); - - readonly avatar$ = defer(() => this.getAuthHeaders()).pipe( - concatMap(headers => - this.httpClient.get( - `${BASE_URL}/auth/users/profile/`, - { headers } - ) - ), - pluck('profile_picture_thumbnail'), - isNonNullable(), - repeatWhen(() => this.refreshAvatar$) - ); - - readonly profileBackground$ = defer(() => this.getAuthHeaders()).pipe( - concatMap(headers => - this.httpClient.get( - `${BASE_URL}/auth/users/profile/`, - { headers } - ) - ), - map(response => response.profile_background_thumbnail), - isNonNullable(), - repeatWhen(() => this.refreshAvatar$) // TODO: refreshProfile$ - ); - - readonly profileDescription$ = defer(() => this.getAuthHeaders()).pipe( - concatMap(headers => - this.httpClient.get( - `${BASE_URL}/auth/users/profile/`, - { headers } - ) - ), - map(response => response.description), - isNonNullable(), - repeatWhen(() => this.refreshAvatar$) // TODO: refreshProfile$ - ); - - readonly phoneVerified$ = this.preferences.getBoolean$( - PrefKeys.PHONE_VERIFIED - ); + readonly profile$ = new ReplaySubject(1); readonly emailVerified$ = this.preferences.getBoolean$( PrefKeys.EMAIL_VERIFIED @@ -213,7 +179,11 @@ export class DiaBackendAuthService { headers, } ) - ) + ), + tap(response => { + this.profile$.next(response); + this.setDisplayName(response.display_name); + }) ); } @@ -299,36 +269,19 @@ export class DiaBackendAuthService { ); } - updateUser$({ username }: { username: string }) { - return defer(() => this.getAuthHeaders()).pipe( - concatMap(headers => - this.httpClient.patch( - `${BASE_URL}/auth/users/me/`, - { username }, - { headers } - ) - ), - concatMapTo(this.readUser$()), - concatMap(response => - forkJoin([ - this.setUsername(response.username), - this.setEmail(response.email), - this.setRerferralCode(response.referral_code), - ]) - ) - ); - } - updateProfile$({ + displayName, description, profilePicture, profileBackground, }: { + displayName: string; description: string; profilePicture?: File; profileBackground?: File; }) { const formData = new FormData(); + formData.append('display_name', displayName); formData.append('description', description); if (profilePicture) { formData.append('profile_picture', profilePicture); @@ -344,31 +297,10 @@ export class DiaBackendAuthService { { headers } ) ), - map(response => response.profile_picture_thumbnail), - tap(url => this.refreshAvatar$.next(url)) - ); - } - - deleteUser$(email: string) { - return defer(() => this.getAuthHeaders()).pipe( - concatMap(headers => - this.httpClient.post( - `https://node.numbersprotocol.io/api/1.1/wf/delete_user`, - { - account: email, - token: headers.authorization, - }, - { headers } - ) - ), - concatMapTo(this.readUser$()), - concatMap(response => - forkJoin([ - this.setUsername(response.username), - this.setEmail(response.email), - this.setRerferralCode(response.referral_code), - ]) - ) + tap(response => { + this.profile$.next(response); + this.setDisplayName(response.display_name); + }) ); } @@ -394,7 +326,7 @@ export class DiaBackendAuthService { formData.append('profile_picture', picture); return defer(() => this.getAuthHeaders()).pipe( concatMap(headers => - this.httpClient.patch( + this.httpClient.patch( `${BASE_URL}/auth/users/profile/`, formData, { @@ -402,8 +334,7 @@ export class DiaBackendAuthService { } ) ), - pluck('profile_picture_thumbnail'), - tap(url => this.refreshAvatar$.next(url)) + tap(profile => this.profile$.next(profile)) ); } @@ -419,22 +350,12 @@ export class DiaBackendAuthService { ); } - verifyPhoneVerification$(phoneNumber: string, verificationCode: string) { - return defer(() => this.getAuthHeaders()).pipe( - concatMap(headers => - this.httpClient.post( - `${BASE_URL}/auth/users/verify-phone-verification/`, - { phone_number: phoneNumber, verification_code: verificationCode }, - { headers } - ) - ), - concatMapTo(this.readUser$()), - concatMap(response => this.setPhoneVerfied(response.phone_verified)) - ); - } - syncUser$() { return this.readUser$().pipe( + tap(response => { + this.profile$.next(response.profile); + this.setDisplayName(response.profile.display_name); + }), concatMap(response => { return forkJoin([ this.setUsername(response.username), @@ -454,14 +375,14 @@ export class DiaBackendAuthService { return !!token; } - async getUsername() { - return this.preferences.getString(PrefKeys.USERNAME); - } - private async setUsername(value: string) { return this.preferences.setString(PrefKeys.USERNAME, value); } + private async setDisplayName(value: string) { + return this.preferences.setString(PrefKeys.DISPLAY_NAME, value); + } + async getEmail() { return this.preferences.getString(PrefKeys.EMAIL); } @@ -529,26 +450,14 @@ export class DiaBackendAuthService { return this.preferences.setBoolean(PrefKeys.PHONE_VERIFIED, value); } - async getPhoneVerified() { - return this.preferences.getBoolean(PrefKeys.PHONE_VERIFIED); - } - private async setEmailVerfied(value: boolean) { return this.preferences.setBoolean(PrefKeys.EMAIL_VERIFIED, value); } - async getEmailVerified() { - return this.preferences.getBoolean(PrefKeys.EMAIL_VERIFIED); - } - private async setPoints(value: number) { return this.preferences.setNumber(PrefKeys.POINTS, value); } - async getPoints() { - return this.preferences.getNumber(PrefKeys.POINTS); - } - private async setRerferralCode(value: string) { return this.preferences.setString(PrefKeys.REFERRAL_CODE, value); } @@ -561,6 +470,7 @@ export class DiaBackendAuthService { const enum PrefKeys { TOKEN = 'TOKEN', USERNAME = 'USERNAME', + DISPLAY_NAME = 'DISPLAY_NAME', EMAIL = 'EMAIL', EMAIL_VERIFIED = 'EMAIL_VERIFIED', PHONE_VERIFIED = 'PHONE_VERIFIED', @@ -592,6 +502,7 @@ export interface ReadUserResponse { readonly email: string; readonly phone_verified: boolean; readonly email_verified: boolean; + readonly profile: ReadProfileResponse; readonly user_wallet: { asset_wallet: string; asset_wallet_num: string | null; @@ -607,9 +518,6 @@ export interface ReadUserResponse { // eslint-disable-next-line @typescript-eslint/no-empty-interface interface CreateUserResponse {} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface UpdateUserResponse {} - // eslint-disable-next-line @typescript-eslint/no-empty-interface interface ResendActivationEmailResponse {} @@ -619,21 +527,12 @@ interface ResetPasswordResponse {} // eslint-disable-next-line @typescript-eslint/no-empty-interface interface SendPhoneVerificationResponse {} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface VerifyPhoneVerificationResponse {} - -type GetAvatarResponse = UploadAvatarResponse; - -interface UploadAvatarResponse { - readonly profile_picture_thumbnail?: string; -} - export interface ReadProfileResponse { display_name: string; - profile_background: string; - profile_background_thumbnail: string; - profile_picture: string; - profile_picture_thumbnail: string; + profile_background: string | null; + profile_background_thumbnail: string | null; + profile_picture: string | null; + profile_picture_thumbnail: string | null; description: string; phone_number: string; } diff --git a/src/app/shared/dia-backend/contact/dia-backend-contact-repository.service.ts b/src/app/shared/dia-backend/contact/dia-backend-contact-repository.service.ts index 05acb2525..82cdbae2e 100644 --- a/src/app/shared/dia-backend/contact/dia-backend-contact-repository.service.ts +++ b/src/app/shared/dia-backend/contact/dia-backend-contact-repository.service.ts @@ -114,6 +114,7 @@ export class DiaBackendContactRepository { export interface DiaBackendContact extends Tuple { readonly contact_email: string; readonly contact_name: string; + readonly contact_profile_display_name: string | null; readonly contact_profile_picture_thumbnail: string | null; } diff --git a/src/app/utils/validation.ts b/src/app/utils/validation.ts index b4ae26760..d5d6253e8 100644 --- a/src/app/utils/validation.ts +++ b/src/app/utils/validation.ts @@ -2,5 +2,3 @@ // https://github.com/angular/angular/blob/e96b3793852ebfd79a54a708363691b11818b4a0/packages/forms/src/validators.ts#L98 export const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; -export const TEL_REGEXP = /^\+[1-9]\d{1,14}$/; -export const VERIFICATION_CODE_REGEXP = /^[0-9]{6}$/; diff --git a/src/assets/i18n/en-us.json b/src/assets/i18n/en-us.json index 6ef440620..400cd0c0b 100644 --- a/src/assets/i18n/en-us.json +++ b/src/assets/i18n/en-us.json @@ -90,7 +90,6 @@ "confirmSyncAndRestore": "Confirm", "cancelSyncAndRestore": "Do it later", "contactUs": "Contact us", - "editUsername": "Edit Username", "forgotPassword": "Forgot Password", "resetPassword": "Reset Password", "friends": "Friends", @@ -119,17 +118,8 @@ "insufficientNum": "Insufficient NUM", "verification": { "verification": "Verification", - "clickToVerify": "Click me to verify", - "phoneVerification": "Phone Verification", - "phoneVerificationInstruction": "Please check you mobile phone and get the 6-digit verification code", - "enterPhoneNumber": "Enter your phone number", - "phonePlaceHolder": "+886 Please enter international phone number", - "enterVerificationCode": "Enter SMS verification code", - "verificationCodePlaceHolder": "Please enter 6-digit verification code", "emailVerification": "Email Verification", - "emailVerificationInstruction": "Please follow the instruction from the received email to verify your email.", - "verify": "Verfiy", - "verified": "Verified" + "emailVerificationInstruction": "Please follow the instruction from the received email to verify your email." }, "tutorial": { "switchBetween0": "Switch between", @@ -187,7 +177,6 @@ "viewSupportingVideoOnIpfs": "View supporting video on IPFS", "videoLimitation": "Capture App DOES NOT support video recording longer than 10 secs. Recording longer than 10 secs will not be saved.", "yesIUnderstand": "Yes, I understand", - "verificationSuccess": "Success", "assetBecomePublicAfterSharing": "This asset will become public after sharing", "assetBecomePublicFor24Hours": "Anyone with the link will be able to access the asset in the next 24 hours", "mintNft": "Mint NFT", @@ -274,7 +263,7 @@ }, "editProfile": { "editProfile": "Edit profile", - "username": "Username", + "profileName": "Profile Name", "description": "Description", "profilePicture": "Profile Picture", "profileBackground": "Profile Background", diff --git a/src/assets/i18n/zh-tw.json b/src/assets/i18n/zh-tw.json index 905b6c50d..7ad3172da 100644 --- a/src/assets/i18n/zh-tw.json +++ b/src/assets/i18n/zh-tw.json @@ -90,7 +90,6 @@ "confirmSyncAndRestore": "確認", "cancelSyncAndRestore": "稍後再說", "contactUs": "聯絡我們", - "editUsername": "修改使用者名稱", "forgotPassword": "忘記密碼", "resetPassword": "重設密碼", "friends": "好友", @@ -119,17 +118,8 @@ "noResultUrlAvailable": "沒有可以顯示的結果連結", "verification": { "verification": "驗證", - "clickToVerify": "點擊驗證", - "phoneVerification": "手機驗證", - "phoneVerificationInstruction": "請檢查手機收到的六位數字驗證碼", - "enterPhoneNumber": "輸入手機號碼", - "phonePlaceHolder": "+886 請輸入國際碼", - "enterVerificationCode": "輸入簡訊驗證碼", - "verificationCodePlaceHolder": "請輸入六位數字驗證碼", "emailVerification": "電子郵件驗證", - "emailVerificationInstruction": "請遵從收到電子郵件中的指示,完成驗證電子郵件。", - "verify": "驗證", - "verified": "已驗證" + "emailVerificationInstruction": "請遵從收到電子郵件中的指示,完成驗證電子郵件。" }, "tutorial": { "switchBetween0": "切換影像與收藏模式", @@ -187,7 +177,6 @@ "viewSupportingVideoOnIpfs": "於 IPFS 檢視附件影片", "videoLimitation": "錄製的影片長度需小於 10 秒鐘。", "yesIUnderstand": "我了解", - "verificationSuccess": "驗證成功", "assetBecomePublicAfterSharing": "該資產將在分享後公開", "assetBecomePublicFor24Hours": "其他用戶在接下來的 24 小時將可透過此連結存取影像", "mintNft": "鑄造 NFT", @@ -274,7 +263,7 @@ }, "editProfile": { "editProfile": "編輯個人頁面", - "username": "用戶名稱", + "profileName": "個人頁面名稱", "description": "描述", "profilePicture": "個人頁面頭像", "profileBackground": "個人頁面背景", From 483b94c38b39feb9f24f493bdf1bfef9d9a32bfe Mon Sep 17 00:00:00 2001 From: Olga Shen Date: Tue, 29 Oct 2024 14:02:29 +0800 Subject: [PATCH 2/2] refactor(features/home): unify profile name --- .../capture-tab/capture-tab.component.html | 2 +- .../home/capture-tab/capture-tab.component.ts | 2 +- .../session/information-session.service.ts | 2 +- .../home/edit-profile/edit-profile.page.ts | 18 ++++++++--------- src/app/features/home/home.page.html | 2 +- src/app/features/home/home.page.ts | 2 +- .../auth/dia-backend-auth.service.ts | 20 +++++++++---------- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/app/features/home/capture-tab/capture-tab.component.html b/src/app/features/home/capture-tab/capture-tab.component.html index 6f56eb9a3..b4473fcae 100644 --- a/src/app/features/home/capture-tab/capture-tab.component.html +++ b/src/app/features/home/capture-tab/capture-tab.component.html @@ -39,7 +39,7 @@ - {{ displayName$ | ngrxPush }} + {{ profileName$ | ngrxPush }} profile.description) diff --git a/src/app/features/home/details/information/session/information-session.service.ts b/src/app/features/home/details/information/session/information-session.service.ts index 456efd97c..5f0786ad5 100644 --- a/src/app/features/home/details/information/session/information-session.service.ts +++ b/src/app/features/home/details/information/session/information-session.service.ts @@ -89,7 +89,7 @@ export class DetailedCapture { readonly creator$ = defer(() => { if (this.proofOrDiaBackendAsset instanceof Proof) - return this.diaBackendAuthService.displayName$; + return this.diaBackendAuthService.profileName$; return of(this.proofOrDiaBackendAsset.creator_profile_display_name ?? ''); }); diff --git a/src/app/features/home/edit-profile/edit-profile.page.ts b/src/app/features/home/edit-profile/edit-profile.page.ts index 6410da327..6c388f4d4 100644 --- a/src/app/features/home/edit-profile/edit-profile.page.ts +++ b/src/app/features/home/edit-profile/edit-profile.page.ts @@ -19,7 +19,7 @@ import { NetworkService } from '../../../shared/network/network.service'; }) export class EditProfilePage { readonly networkConnected$ = this.networkService.connected$; - readonly displayName$ = this.diaBackendAuthService.profile$.pipe( + readonly profileName$ = this.diaBackendAuthService.profile$.pipe( map(profile => profile.display_name) ); readonly description$ = this.diaBackendAuthService.profile$.pipe( @@ -33,7 +33,7 @@ export class EditProfilePage { ); readonly form = new UntypedFormGroup({}); model: EditProfileFormModel = { - displayName: '', + profileName: '', description: '', profilePicture: undefined, profileBackground: undefined, @@ -65,7 +65,7 @@ export class EditProfilePage { tap(([profileNameTranslation, descriptionTranslation]) => { this.fields = [ { - key: 'displayName', + key: 'profileName', type: 'input', templateOptions: { label: profileNameTranslation, @@ -73,7 +73,7 @@ export class EditProfilePage { appearance: 'outline', }, validators: { - displayName: { + profileName: { expression: (c: FormControl) => /^.{1,15}$/.test(c.value), message: () => this.translocoService.translate( @@ -121,11 +121,11 @@ export class EditProfilePage { } populateFormFields() { - combineLatest([this.displayName$, this.description$]) + combineLatest([this.profileName$, this.description$]) .pipe( first(), - tap(([displayName, description]) => { - this.model = { ...this.model, displayName, description }; + tap(([profileName, description]) => { + this.model = { ...this.model, profileName, description }; }) ) .subscribe(); @@ -146,7 +146,7 @@ export class EditProfilePage { .run$( this.diaBackendAuthService .updateProfile$({ - displayName: this.model.displayName, + profileName: this.model.profileName, description: this.model.description, profilePicture: this.model.profilePicture, profileBackground: this.model.profileBackground, @@ -168,7 +168,7 @@ export class EditProfilePage { } interface EditProfileFormModel { - displayName: string; + profileName: string; description: string; profilePicture: File | undefined; profileBackground: File | undefined; diff --git a/src/app/features/home/home.page.html b/src/app/features/home/home.page.html index f5085ead0..bd514ce85 100644 --- a/src/app/features/home/home.page.html +++ b/src/app/features/home/home.page.html @@ -4,7 +4,7 @@ - {{ displayName$ | ngrxPush }} + {{ profileName$ | ngrxPush }} diff --git a/src/app/features/home/home.page.ts b/src/app/features/home/home.page.ts index b4586d894..23d5bd254 100644 --- a/src/app/features/home/home.page.ts +++ b/src/app/features/home/home.page.ts @@ -67,7 +67,7 @@ export class HomePage { private shouldReloadWallet = false; selectedTabIndex = this.initialTabIndex; - readonly displayName$ = this.diaBackendAuthService.displayName$; + readonly profileName$ = this.diaBackendAuthService.profileName$; readonly hasNewInbox$ = this.diaBackendTransactionRepository.inbox$.pipe( catchError((err: unknown) => this.errorService.toastError$(err)), diff --git a/src/app/shared/dia-backend/auth/dia-backend-auth.service.ts b/src/app/shared/dia-backend/auth/dia-backend-auth.service.ts index 34e25417c..2d6cf85b1 100644 --- a/src/app/shared/dia-backend/auth/dia-backend-auth.service.ts +++ b/src/app/shared/dia-backend/auth/dia-backend-auth.service.ts @@ -44,7 +44,7 @@ export class DiaBackendAuthService { readonly username$ = this.preferences.getString$(PrefKeys.USERNAME); - readonly displayName$ = this.preferences.getString$(PrefKeys.DISPLAY_NAME); + readonly profileName$ = this.preferences.getString$(PrefKeys.PROFILE_NAME); readonly email$ = this.preferences.getString$(PrefKeys.EMAIL); @@ -182,7 +182,7 @@ export class DiaBackendAuthService { ), tap(response => { this.profile$.next(response); - this.setDisplayName(response.display_name); + this.setProfileName(response.display_name); }) ); } @@ -270,18 +270,18 @@ export class DiaBackendAuthService { } updateProfile$({ - displayName, + profileName, description, profilePicture, profileBackground, }: { - displayName: string; + profileName: string; description: string; profilePicture?: File; profileBackground?: File; }) { const formData = new FormData(); - formData.append('display_name', displayName); + formData.append('display_name', profileName); formData.append('description', description); if (profilePicture) { formData.append('profile_picture', profilePicture); @@ -299,7 +299,7 @@ export class DiaBackendAuthService { ), tap(response => { this.profile$.next(response); - this.setDisplayName(response.display_name); + this.setProfileName(response.display_name); }) ); } @@ -354,7 +354,7 @@ export class DiaBackendAuthService { return this.readUser$().pipe( tap(response => { this.profile$.next(response.profile); - this.setDisplayName(response.profile.display_name); + this.setProfileName(response.profile.display_name); }), concatMap(response => { return forkJoin([ @@ -379,8 +379,8 @@ export class DiaBackendAuthService { return this.preferences.setString(PrefKeys.USERNAME, value); } - private async setDisplayName(value: string) { - return this.preferences.setString(PrefKeys.DISPLAY_NAME, value); + private async setProfileName(value: string) { + return this.preferences.setString(PrefKeys.PROFILE_NAME, value); } async getEmail() { @@ -470,7 +470,7 @@ export class DiaBackendAuthService { const enum PrefKeys { TOKEN = 'TOKEN', USERNAME = 'USERNAME', - DISPLAY_NAME = 'DISPLAY_NAME', + PROFILE_NAME = 'PROFILE_NAME', EMAIL = 'EMAIL', EMAIL_VERIFIED = 'EMAIL_VERIFIED', PHONE_VERIFIED = 'PHONE_VERIFIED',