From a45473256b5eb5bc6f9f4bffdb5f59303daa2510 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 19 Jan 2023 19:17:44 +0800 Subject: [PATCH] feat(settings.page): ask user to backup private key before deletion --- src/app/features/home/home.page.html | 2 +- src/app/features/settings/settings.page.html | 31 +++++++++- src/app/features/settings/settings.page.scss | 28 +++++++++ src/app/features/settings/settings.page.ts | 62 +++++++++++++++----- src/assets/i18n/en-us.json | 6 +- src/assets/i18n/zh-tw.json | 6 +- 6 files changed, 115 insertions(+), 20 deletions(-) diff --git a/src/app/features/home/home.page.html b/src/app/features/home/home.page.html index 7ed66e0d0..cd3b7fbf6 100644 --- a/src/app/features/home/home.page.html +++ b/src/app/features/home/home.page.html @@ -24,7 +24,7 @@ - {{ t('settings') }} + {{ t('settings.settings') }} diff --git a/src/app/features/settings/settings.page.html b/src/app/features/settings/settings.page.html index c71022506..f58c2bf6e 100644 --- a/src/app/features/settings/settings.page.html +++ b/src/app/features/settings/settings.page.html @@ -2,7 +2,7 @@ - {{ t('settings') }} + {{ t('settings.settings') }} @@ -37,8 +37,35 @@ User Guide Preferences - + {{ 'delete' | transloco }} + + + + + +
+ {{ t('settings.pleasePressThePrivateKeyBelowToCopy') }} +
+ + + + {{ privateKeyTruncated$ | async }} + + + + + + {{ t('settings.iHaveCopiedMyPrivateKey') }} + +
+
+
+
diff --git a/src/app/features/settings/settings.page.scss b/src/app/features/settings/settings.page.scss index 3ff7b559d..7a0c19e40 100644 --- a/src/app/features/settings/settings.page.scss +++ b/src/app/features/settings/settings.page.scss @@ -7,3 +7,31 @@ mat-toolbar { ion-select { width: auto; } + +ion-modal#backup-private-key-modal { + margin: 8px; + + --width: fit-content; + --min-width: 250px; + --height: fit-content; + + ion-card { + padding: 8px; + + ion-card-content { + .backup-modal-warning-text { + font-size: 14px; + padding-bottom: 24px; + } + + ion-item { + margin-bottom: 24px; + + ion-label { + display: flex; + justify-content: center; + } + } + } + } +} diff --git a/src/app/features/settings/settings.page.ts b/src/app/features/settings/settings.page.ts index d5f4d0421..fe8825254 100644 --- a/src/app/features/settings/settings.page.ts +++ b/src/app/features/settings/settings.page.ts @@ -1,16 +1,22 @@ -import { Component } from '@angular/core'; +import { Component, ViewChild } from '@angular/core'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { Clipboard } from '@capacitor/clipboard'; +import { IonModal } from '@ionic/angular'; import { TranslocoService } from '@ngneat/transloco'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { defer, iif, Subject } from 'rxjs'; +import { defer, Subject } from 'rxjs'; import { catchError, - concatMap, concatMapTo, count, + first, + map, + switchMap, take, tap, } from 'rxjs/operators'; import { BlockingActionService } from '../../shared/blocking-action/blocking-action.service'; +import { WebCryptoApiSignatureProvider } from '../../shared/collector/signature/web-crypto-api-signature-provider/web-crypto-api-signature-provider.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'; @@ -35,6 +41,16 @@ export class SettingsPage { private readonly requiredClicks = 7; showHiddenOption = false; + private readonly privateKey$ = this.webCryptoApiSignatureProvider.privateKey$; + readonly privateKeyTruncated$ = this.privateKey$.pipe( + map(key => { + // eslint-disable-next-line @typescript-eslint/no-magic-numbers + return `${key.slice(0, 6)}******${key.slice(key.length - 6)}`; + }) + ); + + @ViewChild('modal') modal?: IonModal; + constructor( private readonly languageService: LanguageService, private readonly database: Database, @@ -44,7 +60,9 @@ export class SettingsPage { private readonly errorService: ErrorService, private readonly translocoService: TranslocoService, private readonly diaBackendAuthService: DiaBackendAuthService, - private readonly confirmAlert: ConfirmAlert + private readonly confirmAlert: ConfirmAlert, + private readonly webCryptoApiSignatureProvider: WebCryptoApiSignatureProvider, + private readonly snackBar: MatSnackBar ) {} ionViewDidEnter() { @@ -69,6 +87,28 @@ export class SettingsPage { this.hiddenOptionClicks$.next(); } + async confirmDelete() { + const confirmed = await this.confirmAlert.present({ + message: this.translocoService.translate('message.confirmDelete'), + }); + if (!confirmed) return; + this.modal?.present(); + } + + async copyPrivateKeyToClipboard() { + return this.privateKey$ + .pipe( + first(), + switchMap(privateKey => Clipboard.write({ string: privateKey })), + tap(() => { + this.snackBar.open( + this.translocoService.translate('message.copiedToClipboard') + ); + }) + ) + .subscribe(); + } + /** * // TODO: Integrate Storage Backend delete function after it's ready. * Delete user account from Storage Backend. @@ -85,17 +125,9 @@ export class SettingsPage { catchError((err: unknown) => this.errorService.toastError$(err)) ); - return defer(() => - this.confirmAlert.present({ - message: this.translocoService.translate('message.confirmDelete'), - }) - ) - .pipe( - concatMap(result => - iif(() => result, this.blockingActionService.run$(action$)) - ), - untilDestroyed(this) - ) + return this.blockingActionService + .run$(action$) + .pipe(untilDestroyed(this)) .subscribe(); } } diff --git a/src/assets/i18n/en-us.json b/src/assets/i18n/en-us.json index 42481779f..a4da409bf 100644 --- a/src/assets/i18n/en-us.json +++ b/src/assets/i18n/en-us.json @@ -1,7 +1,6 @@ { "capture": "Capture", "profile": "Profile", - "settings": "Settings", "privacy": "Privacy", "informationDetails": "Information Details", "caption": "Caption", @@ -334,6 +333,11 @@ "inAppProductsNotAvailableYetPleaseTryAgainLater": "In App Products are not available yet. Try again later." } }, + "settings": { + "settings": "Settings", + "pleasePressThePrivateKeyBelowToCopy": "Please press the private key below to copy. It is important to keep your private key in a safe place.", + "iHaveCopiedMyPrivateKey": "I have copied my private key" + }, "invitation": { "invitation": "Invitation", "shareInvitationCode": "Share invitation code", diff --git a/src/assets/i18n/zh-tw.json b/src/assets/i18n/zh-tw.json index 93349c621..817230281 100644 --- a/src/assets/i18n/zh-tw.json +++ b/src/assets/i18n/zh-tw.json @@ -1,7 +1,6 @@ { "capture": "Capture", "profile": "個人資料", - "settings": "設定", "privacy": "隱私", "informationDetails": "詳細資訊", "caption": "標題", @@ -334,6 +333,11 @@ "inAppProductsNotAvailableYetPleaseTryAgainLater": "App 內購買目前無法使用,請稍後再試." } }, + "settings": { + "settings": "設定", + "pleasePressThePrivateKeyBelowToCopy": "請點擊下方私鑰複製,並妥善保存您的私鑰,私鑰遺失您將會永久失去該錢包的存取權。", + "iHaveCopiedMyPrivateKey": "我已複製私鑰,並已了解遺失私鑰的風險" + }, "invitation": { "invitation": "邀請", "shareInvitationCode": "分享邀請碼",