diff --git a/android/app/build.gradle b/android/app/build.gradle index 3bd79376b3a8226768e3bdac47b7d2bbe5691b70..d9824703ba1db8464cbdadeec19a7655d93752fa 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -13,8 +13,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 2000049 - versionName "2.0.0-alpha49" + versionCode 2000051 + versionName "2.0.0-alpha51" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. @@ -23,22 +23,22 @@ android { } } - signingConfigs { - release { - keyAlias keystoreProperties['keyAlias'] - keyPassword keystoreProperties['keyPassword'] - storeFile file(keystoreProperties['storeFile']) - storePassword keystoreProperties['storePassword'] - } - } + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile file(keystoreProperties['storeFile']) + storePassword keystoreProperties['storePassword'] + } + } - buildTypes { - release { - signingConfig signingConfigs.release - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } + buildTypes { + release { + signingConfig signingConfigs.release + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } } repositories { diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index 059245d1b5695049346fe6755b9602be6f10b0ca..534dd28d7741d10c40ca582c2daa913e573e0f53 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -9,6 +9,7 @@ android { apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { + implementation project(':aparajita-capacitor-biometric-auth') implementation project(':capacitor-community-barcode-scanner') implementation project(':capacitor-app') implementation project(':capacitor-browser') diff --git a/android/capacitor.settings.gradle b/android/capacitor.settings.gradle index a370f4ec0b9d11d9fda8f62a03798ae67c23393e..9289fe50a9f41a3d600fab5264ea98cccdc65fb6 100644 --- a/android/capacitor.settings.gradle +++ b/android/capacitor.settings.gradle @@ -2,6 +2,9 @@ include ':capacitor-android' project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') +include ':aparajita-capacitor-biometric-auth' +project(':aparajita-capacitor-biometric-auth').projectDir = new File('../node_modules/@aparajita/capacitor-biometric-auth/android') + include ':capacitor-community-barcode-scanner' project(':capacitor-community-barcode-scanner').projectDir = new File('../node_modules/@capacitor-community/barcode-scanner/android') diff --git a/electron/package.json b/electron/package.json index c1350f1bda562834ed29b9ab0573582676838bc7..2aa6ecb35f37396d51227850f49853dca039c9ac 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,6 +1,6 @@ { "name": "cesium2s", - "version": "2.0.0-alpha49", + "version": "2.0.0-alpha51", "description": "Cesium², running on Duniter v2s (Substrate).", "author": { "name": "Benoit Lavenier", diff --git a/install.sh b/install.sh index e38c8d2bc1879a378451e4eae798e6d078d94841..9075626bdbf6b8162ba40cfa03d3578bc39e2678 100755 --- a/install.sh +++ b/install.sh @@ -14,7 +14,7 @@ INSTALL_DIR=${1:-$(pwd)/${PROJECT_NAME}} INSTALL_ENV=testing latest_version() { - echo "2.0.0-alpha49" #lastest + echo "2.0.0-alpha51" #lastest } api_release_url() { diff --git a/package-lock.json b/package-lock.json index 57beb6ff47621b0f08f947488564c4f2f9cdf928..7db0a5722a58249fafbe8f57db0f47c8de4eb2d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cesium", - "version": "2.0.0-alpha46", + "version": "2.0.0-alpha50", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cesium", - "version": "2.0.0-alpha46", + "version": "2.0.0-alpha50", "license": "AGPL-3.0", "dependencies": { "@angular/animations": "^18.2.13", @@ -16,6 +16,7 @@ "@angular/platform-browser": "^18.2.13", "@angular/platform-browser-dynamic": "^18.2.13", "@angular/router": "^18.2.13", + "@aparajita/capacitor-biometric-auth": "^7.2.0", "@apollo/client": "~3.12.11", "@capacitor-community/barcode-scanner": "~4.0.1", "@capacitor/android": "^5.0.0", @@ -817,6 +818,28 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/@aparajita/capacitor-biometric-auth": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@aparajita/capacitor-biometric-auth/-/capacitor-biometric-auth-7.2.0.tgz", + "integrity": "sha512-piU8bSB/tJPVRPCAhreC5cioM0CM5Hv6xADXBp6BcUz0n0YLI2TXykUHS+j1WMnhxxdsi4W1flCdQz0zz/K9Gg==", + "dependencies": { + "@capacitor/android": "^5.7.4", + "@capacitor/app": "^5.0.7", + "@capacitor/core": "^5.7.4", + "@capacitor/ios": "^5.7.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@aparajita/capacitor-biometric-auth/node_modules/@capacitor/ios": { + "version": "5.7.8", + "resolved": "https://registry.npmjs.org/@capacitor/ios/-/ios-5.7.8.tgz", + "integrity": "sha512-XhGrziBnlRmCJ97LdPXOJquHPpYTwSJZIxYSXuPl7SDDuAEve8vs2wY76gLdaaFH2Z6ctdugUX+jR6VNu+ds+w==", + "peerDependencies": { + "@capacitor/core": "^5.7.0" + } + }, "node_modules/@apollo/client": { "version": "3.12.11", "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.12.11.tgz", diff --git a/package.json b/package.json index 36514065d6a1eb2576a1e83b2d49b88ef22e08dd..9ae86976ae02687d5ea0fefae09e38e82538ebbf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium", - "version": "2.0.0-alpha49", + "version": "2.0.0-alpha51", "description": "Manage G1 wallet", "author": "Benoit Lavenier <benoit.lavenier@e-is.pro>", "homepage": "https://cesium.app", @@ -98,6 +98,7 @@ "@angular/platform-browser": "^18.2.13", "@angular/platform-browser-dynamic": "^18.2.13", "@angular/router": "^18.2.13", + "@aparajita/capacitor-biometric-auth": "^7.2.0", "@apollo/client": "~3.12.11", "@capacitor-community/barcode-scanner": "~4.0.1", "@capacitor/android": "^5.0.0", diff --git a/resources/webext/manifest.json b/resources/webext/manifest.json index 5297fd33ea7abc00cb196f445c8c3bf60e192ad2..ce71bb3e70dd156d2ec43d5633b93119773cb817 100644 --- a/resources/webext/manifest.json +++ b/resources/webext/manifest.json @@ -1,8 +1,8 @@ { "manifest_version": 2, "name": "cesium2s", - "version": "2.0.0.49", - "version_name": "2.0.0-alpha49", + "version": "2.0.0.51", + "version_name": "2.0.0-alpha51", "short_name": "Cesium²", "description": "Manage G1 wallet", "author": "Benoit Lavenier <benoit.lavenier@e-is.pro>", diff --git a/src/app/account/accounts.service.ts b/src/app/account/accounts.service.ts index c6f0aa8d82d435e3a0132b84124ff756c3b72cc8..6f7f71bca7ffbd61fee257a135c77bd3fde836e5 100644 --- a/src/app/account/accounts.service.ts +++ b/src/app/account/accounts.service.ts @@ -15,6 +15,7 @@ import { isNotEmptyArray, isNotNil, isNotNilOrBlank, + isNotNilOrNaN, sleep, } from '@app/shared/functions'; import { APP_STORAGE, IStorage } from '@app/shared/services/storage/storage.utils'; @@ -39,6 +40,9 @@ import { TranslateService } from '@ngx-translate/core'; import { WotService } from '@app/wot/wot.service'; import { CertificationSearchFilter } from '@app/certification/history/cert-history.model'; import { ISubmittableResult } from '@polkadot/types/types'; +import { BiometricAuth } from '@aparajita/capacitor-biometric-auth'; +import { AlertController, ModalController } from '@ionic/angular'; +import { ValidationModal } from '@app/transfer/send/transfer-validation/validation.modal'; // kind of certify action enum CertType { @@ -94,6 +98,8 @@ export class AccountsService extends RxStartableService<AccountsState> { protected pod: PodService, protected settings: SettingsService, protected translate: TranslateService, + protected modalCtrl: ModalController, + protected alertCtrl: AlertController, @Inject(APP_STORAGE) protected storage: IStorage, @Inject(APP_AUTH_CONTROLLER) protected authController: IAuthController ) { @@ -191,10 +197,10 @@ export class AccountsService extends RxStartableService<AccountsState> { }); // Add Dev account - if (!environment.production) { - const devAccount = await this.addDevAccount(accounts); - if (devAccount) accounts.push(devAccount); - } + // if (!environment.production) { + // const devAccount = await this.addDevAccount(accounts); + // if (devAccount) accounts.push(devAccount); + // } // Load account's data try { @@ -844,6 +850,99 @@ export class AccountsService extends RxStartableService<AccountsState> { } } + async getBiometricAuth(): Promise<boolean> { + try { + await BiometricAuth.authenticate({ + reason: '', + cancelTitle: this.translate.instant('COMMON.BTN_CANCEL'), + allowDeviceCredential: true, + iosFallbackTitle: '', + androidTitle: this.translate.instant('ACCOUNT.SECURITY.BIOMETRIC.TITLE'), + androidSubtitle: this.translate.instant('ACCOUNT.SECURITY.BIOMETRIC.LABEL'), + androidConfirmationRequired: false, + }); + return true; + } catch (error) { + throw new Error(error); + } + } + + async canBiometric(): Promise<boolean> { + const info = await BiometricAuth.checkBiometry(); + return info.isAvailable; + } + + async resetBiometricAuth() { + this.settings.patchValue({ biometricAuth: false }); + } + + async isBiometricEnabled(): Promise<boolean> { + return this.settings.get('biometricAuth') && isNotNilOrBlank(this.storage.get('validationCode')); + } + + async getActionConfirmation(mobile: boolean, from: Account, message?: string, retry?: number): Promise<boolean> { + let validation = false; + let retryUsed = retry ?? 0; + const isBiometricEnabled = await this.isBiometricEnabled(); + if (mobile && isBiometricEnabled) { + validation = await this.getBiometricAuth(); + } else { + const modal = await this.modalCtrl.create({ + component: ValidationModal, + componentProps: { + message, + }, + }); + await modal.present(); + + const { data } = await modal.onWillDismiss(); + const codeValidation = data.codeValidation; + + if (isNotNilOrNaN(codeValidation)) { + const code = await this.storage.get('validationCode'); + validation = codeValidation === code; + } + } + + if (validation === false && retryUsed < 4) { + retryUsed++; + const { role } = await this.openRetryAlert(retryUsed); + if (role === 'cancel') return false; + validation = await this.getActionConfirmation(mobile, from, message, retryUsed); + } + + if (!validation && isBiometricEnabled) { + await this.resetBiometricAuth(); + } + return validation; + } + + private async openRetryAlert(retry: number) { + const alert = await this.alertCtrl.create({ + header: this.translate.instant('ACCOUNT.SECURITY.WAITING_CONFIRMATION.TITLE'), + message: this.translate.instant('ACCOUNT.SECURITY.WAITING_CONFIRMATION.ERROR', { retry: retry }), + buttons: [ + { + text: this.translate.instant('COMMON.BTN_NO'), + role: 'cancel', + handler: () => { + return false; + }, + }, + { + text: this.translate.instant('COMMON.BTN_YES'), + + role: 'confirm', + handler: () => { + return true; + }, + }, + ], + }); + await alert.present(); + return await alert.onDidDismiss(); + } + /** * Load account data (balance, tx history, etc.). * This load can be skipped, when data already loaded (See options) diff --git a/src/app/account/register/password/password.modal.html b/src/app/account/register/password/password.modal.html index 9c03215996feaec5a28bcd884088926e1cfa43fa..9ce3b85f69336939169e0910f2f45b846e0a5ed4 100644 --- a/src/app/account/register/password/password.modal.html +++ b/src/app/account/register/password/password.modal.html @@ -21,7 +21,7 @@ </ion-header> <ion-content style="height: 100%"> - <app-unlock-form #form (validate)="doSubmit($event)" (cancel)="cancel()"></app-unlock-form> + <app-transfer-validation-form #form (validate)="doSubmit($event)"></app-transfer-validation-form> <ion-toolbar *ngIf="!mobile"> <ion-row class="ion-no-padding" nowrap> diff --git a/src/app/account/register/password/password.module.ts b/src/app/account/register/password/password.module.ts index 3f869e6c4c3c9fd44a0a2f00ad45c988c8d4879e..c56fb660a65f2ac8334cd3336807ad6198ee4e78 100644 --- a/src/app/account/register/password/password.module.ts +++ b/src/app/account/register/password/password.module.ts @@ -9,9 +9,19 @@ import { PasswordForm } from '@app/account/register/password/password.form'; import { PasswordModal } from '@app/account/register/password/password.modal'; import { CodeInputModule } from 'angular-code-input'; import { AppUnlockModule } from '@app/account/unlock/unlock.module'; +import { AppTransferValidationFormModule } from '@app/transfer/send/transfer-validation/transfer-validation.form/transfer-validation.form.module'; @NgModule({ - imports: [CommonModule, FormsModule, IonicModule, TranslateModule, AppSharedModule, CodeInputModule, AppUnlockModule], + imports: [ + CommonModule, + FormsModule, + IonicModule, + TranslateModule, + AppSharedModule, + CodeInputModule, + AppUnlockModule, + AppTransferValidationFormModule, + ], declarations: [PasswordForm, PasswordModal], exports: [PasswordForm, PasswordModal], }) diff --git a/src/app/account/register/register.form.html b/src/app/account/register/register.form.html index 373f0358767498dd5f70f8f8e3f2b9f61f47c803..fd9893fd5833ad76d2330e2019e25de3dc0047e5 100644 --- a/src/app/account/register/register.form.html +++ b/src/app/account/register/register.form.html @@ -110,56 +110,82 @@ <!-- Explain code #1 --> <swiper-slide class="ion-padding" cdkTrapFocus> <ion-text> - <p [innerHTML]="'ACCOUNT.NEW.STEP_CODE_1_HELP' | translate"></p> + @if (mobile) { + <p [innerHTML]="'ACCOUNT.NEW.CODE_PIN.STEP_1_HELP' | translate"></p> + } @else { + <p [innerHTML]="'ACCOUNT.NEW.PASSWORD.STEP_1_HELP' | translate"></p> + } </ion-text> </swiper-slide> <!-- Explain code #2 --> <swiper-slide class="ion-padding" cdkTrapFocus> <ion-text> - <p [innerHTML]="'ACCOUNT.NEW.STEP_CODE_2_HELP' | translate"></p> + @if (mobile) { + <p [innerHTML]="'ACCOUNT.NEW.CODE_PIN.STEP_2_HELP' | translate"></p> + } @else { + <p [innerHTML]="'ACCOUNT.NEW.PASSWORD.STEP_2_HELP' | translate"></p> + } </ion-text> </swiper-slide> <!-- Secret code --> - <swiper-slide cdkTrapFocus> - <ion-grid> - <ion-row> - <ion-col size="12"> - <p [innerHTML]="'ACCOUNT.NEW.STEP_CODE_3_HELP' | translate"></p> - </ion-col> - <ion-col size="12"> - <ion-label color="medium" translate>ACCOUNT.NEW.YOUR_SECRET_CODE</ion-label> - </ion-col> - <ion-col size="12" class="ion-padding-top"> - <ion-item> - <ion-text class="ion-text-center" style="width: 100%"> - <h2>{{ form | formGetValue: 'code' }}</h2> - </ion-text> - </ion-item> - </ion-col> - </ion-row> - + <swiper-slide [class.ion-padding]="!mobile" cdkTrapFocus> + @if (mobile) { + <app-unlock-form [control]="form | formGetControl: 'code'" helpMessage="ACCOUNT.NEW.CODE_PIN.STEP_3_HELP"></app-unlock-form> <ion-row> <ion-col class="ion-text-center"> <!-- generate new --> <ion-button color="light" (click)="generateCode(true)" translate>ACCOUNT.NEW.BTN_GENERATE_NEW_CODE</ion-button> </ion-col> </ion-row> - </ion-grid> + } @else { + <ng-container + *ngTemplateOutlet="registerByPassword; context: { text: 'ACCOUNT.NEW.PASSWORD.STEP_3_HELP', formControlName: 'code' }" + ></ng-container> + } </swiper-slide> <!-- check code --> - <swiper-slide cdkTrapFocus> - <app-unlock-form - [control]="form | formGetControl: 'codeConfirmation'" - helpMessage="ACCOUNT.NEW.STEP_CHECK_CODE_HELP" - (codeChange)="checkCodeConfirmation()" - ></app-unlock-form> + <swiper-slide [class.ion-padding]="!mobile" cdkTrapFocus> + @if (mobile) { + <app-unlock-form + [control]="form | formGetControl: 'codeConfirmation'" + helpMessage="ACCOUNT.NEW.CODE_PIN.STEP_CHECK_HELP" + (codeChange)="checkCodeConfirmation()" + ></app-unlock-form> + } @else { + <ion-text [innerHTML]="'ACCOUNT.NEW.PASSWORD.STEP_CHECK_HELP' | translate"></ion-text> + <!-- Password --> + <ion-item> + @if (showPwd) { + <ion-input + [formControl]="form | formGetControl: 'codeConfirmation'" + [label]="'LOGIN.PASSWORD' | translate" + (ionChange)="checkCodeConfirmation()" + labelPlacement="floating" + autocomplete="off" + ></ion-input> + } @else { + <ion-input + [formControl]="form | formGetControl: 'codeConfirmation'" + type="password" + [label]="'LOGIN.PASSWORD' | translate" + (ionChange)="checkCodeConfirmation()" + labelPlacement="floating" + autocomplete="off" + ></ion-input> + } + <!-- show/hide button --> + <ion-button slot="end" (click)="toggleShowPwd($event)" fill="clear" color="medium" [tabindex]="-1"> + <ion-icon slot="icon-only" [name]="showPwd ? 'eye-off' : 'eye'"></ion-icon> + </ion-button> + </ion-item> + } </swiper-slide> <!-- congratulation ! --> - <swiper-slide cdkTrapFocus> + <swiper-slide class="ion-padding" cdkTrapFocus> <ion-row> <ion-col size="12"> <p [innerHTML]="'ACCOUNT.NEW.STEP_CONGRATULATION_1_HELP' | translate"></p> @@ -220,3 +246,30 @@ </ion-text> </ion-col> </ng-template> + +<ng-template #registerByPassword let-text="text" let-formControlName="formControlName"> + <ion-text [innerHTML]="text | translate"></ion-text> + <!-- Password --> + <ion-item> + @if (showPwd) { + <ion-input + [formControl]="form | formGetControl: formControlName" + [label]="'LOGIN.PASSWORD' | translate" + labelPlacement="floating" + autocomplete="off" + ></ion-input> + } @else { + <ion-input + [formControl]="form | formGetControl: formControlName" + type="password" + [label]="'LOGIN.PASSWORD' | translate" + labelPlacement="floating" + autocomplete="off" + ></ion-input> + } + <!-- show/hide button --> + <ion-button slot="end" (click)="toggleShowPwd($event)" fill="clear" color="medium" [tabindex]="-1"> + <ion-icon slot="icon-only" [name]="showPwd ? 'eye-off' : 'eye'"></ion-icon> + </ion-button> + </ion-item> +</ng-template> diff --git a/src/app/account/register/register.form.ts b/src/app/account/register/register.form.ts index 9a974976b9b326f63471a279082b79e060729262..7828d7290a6f1dc52aba80b6c5cbddc280ca7f6b 100644 --- a/src/app/account/register/register.form.ts +++ b/src/app/account/register/register.form.ts @@ -8,12 +8,13 @@ import { NetworkService } from '@app/network/network.service'; import { Currency } from '@app/currency/currency.model'; import { AccountMeta } from '@app/account/account.model'; import { Swiper, SwiperOptions } from 'swiper/types'; -import { IonicSlides } from '@ionic/angular'; +import { AlertController, IonicSlides } from '@ionic/angular'; import { SwiperDirective } from '@app/shared/swiper/app-swiper.directive'; import { isNilOrBlank } from '@app/shared/functions'; import { formatAddress } from '@app/shared/currencies'; import { setTimeout } from '@rx-angular/cdk/zone-less/browser'; import { AuthData } from '@app/account/auth/auth.model'; +import { StorageService } from '@app/shared/services/storage/storage.service'; export const REGISTER_FORM_SLIDES = { MNEMONIC: 5, @@ -30,7 +31,8 @@ export const REGISTER_FORM_SLIDES = { }) export class RegisterForm extends AppForm<AuthData> implements OnInit { protected swiperModules = [IonicSlides]; - + isBiometricAllowed = false; + showPwd = false; @Input() swiperOptions: SwiperOptions = { initialSlide: 0, speed: 400, @@ -58,6 +60,8 @@ export class RegisterForm extends AppForm<AuthData> implements OnInit { constructor( private accountService: AccountsService, private networkService: NetworkService, + private alertCtrl: AlertController, + private storage: StorageService, public formBuilder: FormBuilder, protected settings?: SettingsService ) { @@ -160,6 +164,7 @@ export class RegisterForm extends AppForm<AuthData> implements OnInit { this.slideState.index = this.swiper.activeIndex; this.slideState.isBeginning = this.slideState.index === 0 || this.swiper.isBeginning; this.slideState.isEnd = this.swiper.isEnd; + this.markForCheck(); console.debug('[register-form] Slide #' + this.slideState.index); @@ -176,7 +181,9 @@ export class RegisterForm extends AppForm<AuthData> implements OnInit { this.generateWordNumber(); break; case REGISTER_FORM_SLIDES.CODE: - this.generateCode(); + if (this.mobile) { + this.generateCode(); + } break; case REGISTER_FORM_SLIDES.CODE_CONFIRMATION: this.checkCodeConfirmation(); @@ -276,8 +283,66 @@ export class RegisterForm extends AppForm<AuthData> implements OnInit { this.form.get('name').setValue(name); } + this.isBiometricAllowed = await this.accountService.canBiometric(); + if (this.mobile && this.isBiometricAllowed) { + const { role } = await this.showBiometricAlert(); + if (role === 'confirm') { + const biometricValid = await this.accountService.getBiometricAuth(); + if (biometricValid) { + try { + await this.storage.set('validationCode', data.password); + } catch (error) { + console.error('Erreur lors du stockage dans le cache :', error); + } + } + } + } + this.slideState.canNext = true; this.markAsLoaded(); }, 250); } + + async showBiometricAlert() { + const alert = await this.alertCtrl.create({ + header: this.translate.instant('ACCOUNT.SECURITY.BIOMETRIC.TITLE'), + message: this.translate.instant('ACCOUNT.SECURITY.BIOMETRIC.QUESTION'), + buttons: [ + { + text: this.translate.instant('COMMON.BTN_NO'), + role: 'cancel', + handler: () => { + return false; + }, + }, + { + text: this.translate.instant('COMMON.BTN_YES'), + + role: 'confirm', + handler: () => { + return true; + }, + }, + ], + }); + + await alert.present(); + return await alert.onDidDismiss(); + } + + protected toggleShowPwd(event?: Event) { + event?.preventDefault(); + this.showPwd = !this.showPwd; + this.markForCheck(); + + // Auto hide + if (this.showPwd) { + setTimeout(() => { + if (this.showPwd) { + this.showPwd = false; + this.markForCheck(); + } + }, 2000); + } + } } diff --git a/src/app/account/unlock/unlock.form.ts b/src/app/account/unlock/unlock.form.ts index b9fa44717a9536c0607a919d57b0f3cb07d7ad69..99d1a0093539acfe63e74d5de9ccb32949f7e2de 100644 --- a/src/app/account/unlock/unlock.form.ts +++ b/src/app/account/unlock/unlock.form.ts @@ -5,7 +5,6 @@ import { environment } from '@environments/environment'; import { AppForm } from '@app/shared/form.class'; import { isNotNilOrBlank } from '@app/shared/functions'; import { distinctUntilChanged, map, Subject } from 'rxjs'; -import { MaskitoElementPredicateAsync, MaskitoOptions } from '@maskito/core'; @Component({ selector: 'app-unlock-form', @@ -28,12 +27,6 @@ export class UnlockForm extends AppForm<string> implements OnInit { $valid = new Subject<boolean>(); - readonly codeMask: MaskitoOptions = { - mask: [/[A-Z]/, /[A-Z]/, /[A-Z]/, /[A-Z]/, /[A-Z]/, /[A-Z]/], - }; - - readonly maskPredicate: MaskitoElementPredicateAsync = async (el) => (el as HTMLIonInputElement).getInputElement(); - constructor( public formBuilder: FormBuilder, protected settings?: SettingsService, @@ -92,7 +85,8 @@ export class UnlockForm extends AppForm<string> implements OnInit { } onInput(event: Event) { - console.log(event); + if (this.debug) console.log(event); + let value = event.target['value'] || ''; // Removes non alphanumeric characters diff --git a/src/app/account/unlock/unlock.modal.ts b/src/app/account/unlock/unlock.modal.ts index 6b83cbb46705c1be4ced9cd056ee7d508fa08412..ea644598b8d2f44d28f6862632531bfb05598aef 100644 --- a/src/app/account/unlock/unlock.modal.ts +++ b/src/app/account/unlock/unlock.modal.ts @@ -4,6 +4,7 @@ import { AccountsService } from '@app/account/accounts.service'; import { firstNotNilPromise } from '@app/shared/observables'; import { UnlockForm } from '@app/account/unlock/unlock.form'; import { UnlockOptions } from '@app/account/account.model'; +import { StorageService } from '@app/shared/services/storage/storage.service'; @Component({ selector: 'app-unlock-modal', @@ -29,6 +30,7 @@ export class UnlockModal implements OnInit, UnlockOptions { constructor( private accountService: AccountsService, + private storage: StorageService, private viewCtrl: ModalController, private cd: ChangeDetectorRef ) {} @@ -54,7 +56,7 @@ export class UnlockModal implements OnInit, UnlockOptions { try { data = data || this.form.value; - + await this.storage.set('validationCode', data); // Disable the form this.form.disable(); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 348a9591eecb9ce13bd8c8892beb093ef3bbdc79..0860569efd8d9658eda60e8f16283dbc9c93702a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -29,9 +29,12 @@ import { AppWotModule } from './wot/wot.module'; import { APP_FORM_ERROR_I18N_KEYS } from '@app/shared/form/form-error-translator.service'; import { IdentityConfirmValidators } from '@app/account/confirm/identity-confirm.validator'; import { CodeInputModule } from 'angular-code-input'; -import { HelpModule } from '@app/home/help/help.module'; +import { AppHelpModule } from '@app/home/help/app-help.module'; import { AboutModule } from '@app/home/about/about.module'; import { VerificationModule } from '@app/certification/verification/verification.module'; +import { HomeModule } from '@app/home/home.module'; +import { AppValidationModule } from '@app/transfer/send/transfer-validation/validation.module'; +import { AppUnlockModule } from '@app/account/unlock/unlock.module'; export function createTranslateLoader(http: HttpClient) { if (environment.production) { @@ -62,7 +65,8 @@ export function createTranslateLoader(http: HttpClient) { deps: [HttpClient], }, }), - HelpModule, + HomeModule, + AppHelpModule, VerificationModule, AboutModule, CodeInputModule.forRoot({ @@ -70,7 +74,8 @@ export function createTranslateLoader(http: HttpClient) { isCharsCode: true, code: 'abcdef', }), - HelpModule, + AppValidationModule, + AppUnlockModule, AppRoutingModule, AppSharedModule, AppWotModule.forRoot(), diff --git a/src/app/home/help/help.module.ts b/src/app/home/help/app-help.module.ts similarity index 92% rename from src/app/home/help/help.module.ts rename to src/app/home/help/app-help.module.ts index aaba39986d6800d42f1a92c7b02cb50dc44f8778..365b595a24da1fc4662ea56bc9cb7ba46d7ccc81 100644 --- a/src/app/home/help/help.module.ts +++ b/src/app/home/help/app-help.module.ts @@ -9,4 +9,4 @@ import { HelpPage } from '@app/home/help/help.page'; imports: [AppSharedModule, TranslateModule, HomePageRoutingModule], declarations: [HelpPage], }) -export class HelpModule {} +export class AppHelpModule {} diff --git a/src/app/settings/settings.model.ts b/src/app/settings/settings.model.ts index 52dd0d692f83c8d4a93106bcf4724f3f306bc301..063932b63334ed756eefcfb734cb165a418c22cf 100644 --- a/src/app/settings/settings.model.ts +++ b/src/app/settings/settings.model.ts @@ -39,4 +39,5 @@ export interface Settings { selectedRedeemPeriod?: DurationConstructor; lastRedeemDate: Moment; expertMode: boolean; + biometricAuth: boolean; } diff --git a/src/app/settings/settings.page.html b/src/app/settings/settings.page.html index 9a9f8ffcde9bbca8448530dac86f7d487ce7820e..c380572a7e6dc7b2ef574d758c83222dcaef12ad 100644 --- a/src/app/settings/settings.page.html +++ b/src/app/settings/settings.page.html @@ -143,6 +143,16 @@ </ion-select-option> </ion-select> </ion-item> + + <!-- @if (canBiometric) {--> + <ion-item> + <ion-icon slot="start" name="finger-print"></ion-icon> + <ion-label color="dark" translate>SETTINGS.BIOMETRIC</ion-label> + <ion-toggle [(ngModel)]="biometricAuth" (click)="toggleBiometricAuth()"> + <ion-label color="medium" translate></ion-label> + </ion-toggle> + </ion-item> + <!-- }--> </ion-list> <!-- buttons --> diff --git a/src/app/settings/settings.page.ts b/src/app/settings/settings.page.ts index bd0bfe3d75f338de78f0383d73cea341665192f8..ff031086954b2133f5ff8d608d66fc0d463aaf58 100644 --- a/src/app/settings/settings.page.ts +++ b/src/app/settings/settings.page.ts @@ -4,10 +4,13 @@ import { AppPage, AppPageState } from '@app/shared/pages/base-page.class'; import { RxState } from '@rx-angular/state'; import { RxStateProperty, RxStateSelect } from '@app/shared/decorator/state.decorator'; import { Observable, skip } from 'rxjs'; -import { IonModal } from '@ionic/angular'; +import { IonModal, ModalController } from '@ionic/angular'; import { NetworkService } from '@app/network/network.service'; import { map } from 'rxjs/operators'; import moment from 'moment'; +import { AccountsService } from '@app/account/accounts.service'; +import { isNotNil } from '@app/shared/functions'; +import { Account } from '@app/account/account.model'; import DurationConstructor = moment.unitOfTime.DurationConstructor; export interface SettingsPageState extends Settings, AppPageState { @@ -81,6 +84,7 @@ export class SettingsPage extends AppPage<SettingsPageState> implements OnInit { @RxStateSelect() dirty$: Observable<boolean>; @RxStateSelect() newPeer$: Observable<string>; @RxStateSelect() durationRedeemUd$: Observable<number>; + @RxStateSelect() biometricAuth$: Observable<boolean>; @RxStateProperty() darkMode: boolean; @RxStateProperty() locale: string; @@ -92,6 +96,8 @@ export class SettingsPage extends AppPage<SettingsPageState> implements OnInit { @RxStateProperty() unAuthDelayMs: number; @RxStateProperty() dirty: boolean; @RxStateProperty() expertMode: boolean; + @RxStateProperty() biometricAuth: boolean; + @RxStateProperty() displayUnit: 'base' | 'du'; @Input() @RxStateProperty() customPeer: string; @Input() @RxStateProperty() selectedRedeemPeriod: DurationConstructor = 'day'; @@ -100,8 +106,12 @@ export class SettingsPage extends AppPage<SettingsPageState> implements OnInit { @ViewChild('selectPodModal') selectPodModal: IonModal; @ViewChild('selectIpfsGatewayModal') selectIpfsGatewayModal: IonModal; + canBiometric = false; + account: Account; constructor( protected networkService: NetworkService, + protected accountService: AccountsService, + protected modalCtrl: ModalController, @Inject(APP_LOCALES) protected locales: LocaleConfig[] ) { super({ name: 'settings' }); @@ -113,7 +123,10 @@ export class SettingsPage extends AppPage<SettingsPageState> implements OnInit { // Detect changes this._state.hold( this._state - .select(['locale', 'peer', 'indexer', 'pod', 'ipfsGateway', 'unAuthDelayMs', 'displayUnit', 'selectedRedeemPeriod', 'expertMode'], (s) => s) + .select( + ['locale', 'peer', 'indexer', 'pod', 'ipfsGateway', 'unAuthDelayMs', 'displayUnit', 'selectedRedeemPeriod', 'expertMode', 'biometricAuth'], + (s) => s + ) .pipe(skip(1)), () => { if (this.mobile) { @@ -127,6 +140,8 @@ export class SettingsPage extends AppPage<SettingsPageState> implements OnInit { protected async ngOnLoad() { await this.settings.ready(); + await this.accountService.ready(); + this.canBiometric = (await this.accountService.canBiometric()) && this.accountService.isLogin; return { ...this.settings.clone(), dirty: false, @@ -160,4 +175,24 @@ export class SettingsPage extends AppPage<SettingsPageState> implements OnInit { this.darkMode = !this.darkMode; this.save(); } + + async toggleBiometricAuth() { + this.account = await this.accountService.getDefault(); + if (this.biometricAuth) { + const confirmation = await this.accountService.getActionConfirmation(this.mobile, this.account); + if (confirmation) { + await this.accountService.resetBiometricAuth(); + } + return; + } + + let validation = await this.accountService.getActionConfirmation(this.mobile, this.account); + + if (validation) { + validation = await this.accountService.getBiometricAuth(); + } + this.settings.patchValue({ biometricAuth: validation }); + } + + protected readonly isNotNil = isNotNil; } diff --git a/src/app/shared/currencies.ts b/src/app/shared/currencies.ts index d37df3dacb4312ea2d1419cf6315c74f82ccb780..958e7bec7d7b6406d59be7de9c5c20ca61e93d5e 100644 --- a/src/app/shared/currencies.ts +++ b/src/app/shared/currencies.ts @@ -39,7 +39,7 @@ export function formatAddress(value: string): string { export function formatPubkey(pubkey: string, withChecksum?: boolean): string { if (!pubkey) return ''; if (pubkey.length < 43) return '?'; - let shortPubkey = pubkey.substring(0, 4) + '\u2026' + pubkey.substring(pubkey.length - 4); + let shortPubkey = pubkey.substring(0, 5) + '\u2026' + pubkey.substring(pubkey.length - 4); if (withChecksum) { shortPubkey += ':' + pubkeyV1Checksum(pubkey); diff --git a/src/app/shared/services/storage/storage.service.ts b/src/app/shared/services/storage/storage.service.ts index 52f9d9d28f4cf6b4905de5b4a4b2308e722f3725..74cffb16afdd3ea9ed16ad6ec1349c688abac38e 100644 --- a/src/app/shared/services/storage/storage.service.ts +++ b/src/app/shared/services/storage/storage.service.ts @@ -23,6 +23,8 @@ export class StorageService<T = any> extends StartableService<Storage> implement protected async ngOnStart() { await this.platform.ready(); + // TODO : defineSecureStorage https://github.com/martinkasa/capacitor-secure-storage-plugin + // this.storage.defineDriver({}); const storage = await this.storage.create(); console.info(`[storage-service] Started using driver=${this.driver}`); return storage; diff --git a/src/app/transfer/send/transfer-validation/transfer-validation.form/transfer-validation.form.module.ts b/src/app/transfer/send/transfer-validation/transfer-validation.form/transfer-validation.form.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..bfe0c59d2fcb32ac276847889bb0ea60d0544a48 --- /dev/null +++ b/src/app/transfer/send/transfer-validation/transfer-validation.form/transfer-validation.form.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { AppSharedModule } from '@app/shared/shared.module'; +import { TranslateModule } from '@ngx-translate/core'; +import { AppUnlockModule } from '@app/account/unlock/unlock.module'; +import { ValidationForm } from '@app/transfer/send/transfer-validation/transfer-validation.form/validation.form'; + +@NgModule({ + imports: [AppSharedModule, TranslateModule.forChild(), AppUnlockModule], + exports: [ValidationForm], + declarations: [ValidationForm], +}) +export class AppTransferValidationFormModule {} diff --git a/src/app/transfer/send/transfer-validation/transfer-validation.form/validation.form.html b/src/app/transfer/send/transfer-validation/transfer-validation.form/validation.form.html new file mode 100644 index 0000000000000000000000000000000000000000..7ca46975875867edec00499a4fec2aa868932cde --- /dev/null +++ b/src/app/transfer/send/transfer-validation/transfer-validation.form/validation.form.html @@ -0,0 +1,33 @@ +<form [formGroup]="form"> + @if (mobile) { + <app-unlock-form + [control]="form | formGetControl: 'codeValidation'" + helpMessage="ACCOUNT.SECURITY.WAITING_CONFIRMATION.HELP_CODE_PIN" + ></app-unlock-form> + } @else { + <ion-text [innerHTML]="'ACCOUNT.SECURITY.WAITING_CONFIRMATION.HELP_PASSWORD' | translate"></ion-text> + <!-- Password --> + <ion-item> + @if (showPwd) { + <ion-input + [formControlName]="'codeValidation'" + [label]="'LOGIN.PASSWORD' | translate" + labelPlacement="floating" + autocomplete="off" + ></ion-input> + } @else { + <ion-input + [formControlName]="'codeValidation'" + type="password" + [label]="'LOGIN.PASSWORD' | translate" + labelPlacement="floating" + autocomplete="off" + ></ion-input> + } + <!-- show/hide button --> + <ion-button slot="end" (click)="toggleShowPwd($event)" fill="clear" color="medium" [tabindex]="-1"> + <ion-icon slot="icon-only" [name]="showPwd ? 'eye-off' : 'eye'"></ion-icon> + </ion-button> + </ion-item> + } +</form> diff --git a/src/app/transfer/send/transfer-validation/transfer-validation.form/validation.form.scss b/src/app/transfer/send/transfer-validation/transfer-validation.form/validation.form.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/transfer/send/transfer-validation/transfer-validation.form/validation.form.spec.ts b/src/app/transfer/send/transfer-validation/transfer-validation.form/validation.form.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..19d2bdc035d795d59add9019f1215360c7d1bc39 --- /dev/null +++ b/src/app/transfer/send/transfer-validation/transfer-validation.form/validation.form.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { ValidationForm } from './validation.form'; + +describe('TransferValidationFormComponent', () => { + let component: ValidationForm; + let fixture: ComponentFixture<ValidationForm>; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ValidationForm], + imports: [IonicModule.forRoot()], + }).compileComponents(); + + fixture = TestBed.createComponent(ValidationForm); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/transfer/send/transfer-validation/transfer-validation.form/validation.form.ts b/src/app/transfer/send/transfer-validation/transfer-validation.form/validation.form.ts new file mode 100644 index 0000000000000000000000000000000000000000..fbcbed983e080b16e83c811048ee382bd7518c77 --- /dev/null +++ b/src/app/transfer/send/transfer-validation/transfer-validation.form/validation.form.ts @@ -0,0 +1,63 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { AppForm } from '@app/shared/form.class'; +import { FormBuilder, Validators } from '@angular/forms'; +import { environment } from '@environments/environment'; +import { setTimeout } from '@rx-angular/cdk/zone-less/browser'; + +export interface ValidationData { + codeValidation?: string; +} +@Component({ + selector: 'app-transfer-validation-form', + templateUrl: './validation.form.html', + styleUrls: ['./validation.form.scss'], +}) +export class ValidationForm extends AppForm<ValidationData> implements OnInit { + @Input() message: string; + + showPwd = false; + + constructor(formBuilder: FormBuilder) { + super( + formBuilder.group({ + codeValidation: [null, Validators.required], + }) + ); + this._enable = true; + this.debug = !environment.production; + } + + ngOnInit() { + super.ngOnInit(); + } + + protected toggleShowPwd(event?: Event) { + event?.preventDefault(); + this.showPwd = !this.showPwd; + this.markForCheck(); + + // Auto hide + if (this.showPwd) { + setTimeout(() => { + if (this.showPwd) { + this.showPwd = false; + this.markForCheck(); + } + }, 2000); + } + } + + get value(): ValidationData { + const json = this.form.value; + return { + codeValidation: json.codeValidation, + }; + } + + get invalid() { + return this.form?.invalid; + } + get valid() { + return this.form?.valid; + } +} diff --git a/src/app/transfer/send/transfer-validation/validation.modal.html b/src/app/transfer/send/transfer-validation/validation.modal.html new file mode 100644 index 0000000000000000000000000000000000000000..6c6f18653f059c3b95b9ab81f58b6b4fb15dd58b --- /dev/null +++ b/src/app/transfer/send/transfer-validation/validation.modal.html @@ -0,0 +1,28 @@ +<ion-header> + <ion-toolbar color="danger"> + <ion-buttons slot="start"> + <ion-button (click)="cancel()" *ngIf="mobile"> + <ion-icon slot="icon-only" name="arrow-back"></ion-icon> + </ion-button> + </ion-buttons> + + <ion-title [innerHTML]="'AUTH.TITLE' | translate"></ion-title> + + <ion-buttons slot="end"> + @if (mobile) { + <ion-button (click)="doSubmit()"> + <ion-icon slot="icon-only" name="checkmark"></ion-icon> + </ion-button> + } + </ion-buttons> + </ion-toolbar> +</ion-header> + +<ion-content class="ion-padding" style="height: 100%"> + <app-transfer-validation-form [message]="message" #form></app-transfer-validation-form> + <ion-row> + <ion-col class="ion-text-center"> + <ion-button color="light" (click)="doSubmit()" translate>COMMON.BTN_CONFIRM</ion-button> + </ion-col> + </ion-row> +</ion-content> diff --git a/src/app/transfer/send/transfer-validation/validation.modal.scss b/src/app/transfer/send/transfer-validation/validation.modal.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/app/transfer/send/transfer-validation/validation.modal.spec.ts b/src/app/transfer/send/transfer-validation/validation.modal.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a3aa2efa98fbd683f9d22d65c5a188dee58a3154 --- /dev/null +++ b/src/app/transfer/send/transfer-validation/validation.modal.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { ValidationModal } from './validation.modal'; + +describe('TransferValidationModalComponent', () => { + let component: ValidationModal; + let fixture: ComponentFixture<ValidationModal>; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ValidationModal], + imports: [IonicModule.forRoot()], + }).compileComponents(); + + fixture = TestBed.createComponent(ValidationModal); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/transfer/send/transfer-validation/validation.modal.ts b/src/app/transfer/send/transfer-validation/validation.modal.ts new file mode 100644 index 0000000000000000000000000000000000000000..29c838e85219a5d63db667fd37e14f1399add0e4 --- /dev/null +++ b/src/app/transfer/send/transfer-validation/validation.modal.ts @@ -0,0 +1,34 @@ +import { ChangeDetectionStrategy, Component, Input, ViewChild } from '@angular/core'; +import { ModalController } from '@ionic/angular'; +import { SettingsService } from '@app/settings/settings.service'; +import { ValidationForm } from '@app/transfer/send/transfer-validation/transfer-validation.form/validation.form'; + +@Component({ + selector: 'app-validation', + templateUrl: './validation.modal.html', + styleUrls: ['./validation.modal.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ValidationModal { + @ViewChild('form') form: ValidationForm; + + @Input() message: string; + + protected mobile = this.settingsService.mobile; + + constructor( + private viewCtrl: ModalController, + private settingsService: SettingsService + ) {} + + cancel() { + this.viewCtrl.dismiss(); + } + + doSubmit() { + const data = this.form.value; + this.viewCtrl.dismiss({ + codeValidation: data.codeValidation, + }); + } +} diff --git a/src/app/transfer/send/transfer-validation/validation.module.ts b/src/app/transfer/send/transfer-validation/validation.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..3e823b6a3915a44099723efb218ae24f762c2532 --- /dev/null +++ b/src/app/transfer/send/transfer-validation/validation.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { AppSharedModule } from '@app/shared/shared.module'; +import { TranslateModule } from '@ngx-translate/core'; +import { AppTransferValidationFormModule } from '@app/transfer/send/transfer-validation/transfer-validation.form/transfer-validation.form.module'; +import { ValidationModal } from '@app/transfer/send/transfer-validation/validation.modal'; +import { AppAuthModule } from '@app/account/auth/auth.module'; + +@NgModule({ + imports: [AppSharedModule, TranslateModule.forChild(), AppTransferValidationFormModule, AppAuthModule], + declarations: [ValidationModal], +}) +export class AppValidationModule {} diff --git a/src/app/transfer/send/transfer.page.ts b/src/app/transfer/send/transfer.page.ts index 00aa9d1db720f6f968a64dfa6fcf9cfc0ad63268..c5daacb536e9f5c619112a781e6450cd71bcd441 100644 --- a/src/app/transfer/send/transfer.page.ts +++ b/src/app/transfer/send/transfer.page.ts @@ -238,13 +238,17 @@ export class TransferPage extends AppPage<TransferPageState> implements Transfer this.resetError(); try { + const validation = await this.accountService.getActionConfirmation(this.mobile, this.account); + if (!validation) { + await this.close(); + } const hash = await this.accountService.transfer(this.account, this.recipient, this.amount, this.comment?.trim()); if (this.showToastOnSubmit) { await this.showToast({ message: 'INFO.TRANSFER_SENT' }); } - this.close(hash); + await this.close(hash); } catch (err) { this.setError(err); this.markAsLoaded(); diff --git a/src/app/wot/wot-lookup.page.ts b/src/app/wot/wot-lookup.page.ts index 7c8925c709c28a531752bc5d4187aa5a7d717e03..10c5f81a31d5147e8937adf24b520c7ec6774d77 100644 --- a/src/app/wot/wot-lookup.page.ts +++ b/src/app/wot/wot-lookup.page.ts @@ -188,7 +188,7 @@ export class WotLookupPage extends AppPage<WotLookupState> implements OnInit, Wo event.preventDefault(); event.stopPropagation(); - this.searchText = value; + this.searchText = value?.trim(); } async clearSearch(event: UIEvent) { @@ -225,6 +225,8 @@ export class WotLookupPage extends AppPage<WotLookupState> implements OnInit, Wo } applyFilter(filter: Partial<WotSearchFilter>) { + this.searchText = ''; + this._state.set( (s) => <WotLookupState>{ diff --git a/src/assets/i18n/ca.json b/src/assets/i18n/ca.json index 776f43fff7700e976408628206b5888b2a7a5b4d..ae04fc3287273420fe0f31fec69365a2b9e2ee87 100644 --- a/src/assets/i18n/ca.json +++ b/src/assets/i18n/ca.json @@ -202,7 +202,10 @@ "USE_SSL": "Connexió segura", "USE_SSL_HELP": "(Xifrat SSL)", "BTN_SHOW_LIST": "Llista de nodes" - } + }, + "BIOMETRIC": "Autenticació biomètrica del dispositiu?", + "DURATION_BETWEEN_UD_CLAIM": "Període entre cada recuperació del dividend universal" + }, "BLOCKCHAIN": { "HASH": "Hash: {{hash}}", @@ -551,6 +554,26 @@ "INTRO_WARNING_SECURITY": "Asegúrese de que el dispositivo actual (ordenador, tablet, teléfono) <b>es seguro i de confiança</b>.", "INTRO_WARNING_SECURITY_HELP": "Antivirus, cortafuegos, sessió protegida per contraseña o número PIN, etc.", "INTRO_HELP": "Haga clic en <b>{{'COMMON.BTN_START'|translate}}</b> para iniciar la creación de la compte. Se le guiará paso a paso.", + "STEP_1_HELP": "Cesium generarà una <b>frase de recuperació</b>. És com un plànol que permet crear les claus d'accés al teu compte.", + "STEP_2_HELP": "Guarda aquesta frase amb cura, ja que sense ella Cesium no podrà reconstruir les claus d'accés al teu compte. <br/>La necessitaràs cada vegada que hagis d'instal·lar Cesium en un nou telèfon o ordinador.", + "STEP_3_HELP": "En una blockchain, no hi ha cap procediment de recuperació per correu electrònic o SMS. Només la teva frase de recuperació et permetrà recuperar els teus {{currency}} en qualsevol moment.", + "STEP_4_HELP": "És hora d'agafar <b>paper i llapis</b> per anotar la teva frase de recuperació.", + "STEP_MNEMONIC_HELP": "Aquí tens la teva frase de recuperació!<br/>Procura mantenir-la en secret, ja que qualsevol que la trobi podria buidar el teu compte.", + "STEP_CHECK_WORD_HELP": "Has anotat correctament la teva frase de recuperació?<br/>Per comprovar-ho, escriu la paraula #{{number}} de la frase al camp següent:", + "CODE_PIN": { + "STEP_1_HELP": "<b>Un codi secret</b> es generarà ara.<br/>És necessari per accedir al vostre compte en aquest dispositiu, sense haver d'utilitzar la frase de restauració.", + "STEP_2_HELP": "Aquest codi secret també té com a objectiu <b>protegir el vostre compte</b>. És per això que <b>només vós l'heu de tenir</b>.", + "STEP_3_HELP": "Aquí teniu el vostre codi secret!<br/>Memoritzeu-lo perfectament, o anoteu-lo, ja que se us demanarà <b>cada vegada</b> que vulgueu fer un pagament en aquest dispositiu. Una mica com el codi PIN per a un pagament amb targeta bancària.", + "STEP_CHECK_HELP": "Per verificar que heu memoritzat o anotat correctament el vostre codi secret, introduïu-lo a continuació:" + }, + "PASSWORD": { + "STEP_1_HELP": "Haureu d'introduir <b>una contrasenya</b>.<br/>És necessària per accedir al vostre compte en aquest dispositiu, sense haver d'utilitzar la frase de restauració.", + "STEP_2_HELP": "Aquesta contrasenya també té com a objectiu <b>protegir el vostre compte</b>. És per això que <b>només vós l'heu de tenir</b>.", + "STEP_3_HELP": "Heu d'introduir la vostra contrasenya<br/>Memoritzeu-la perfectament, o anoteu-la, ja que se us demanarà <b>cada vegada</b> que vulgueu fer un pagament en aquest dispositiu. Una mica com el codi PIN per a un pagament amb targeta bancària.", + "STEP_CHECK_HELP": "Per verificar que heu memoritzat o anotat correctament la vostra contrasenya, confirmeu-la a continuació:" + }, + "STEP_CONGRATULATION_1_HELP": "Felicitats! Tot està a punt per crear el teu compte.<br/><br/>Tindrà el següent <b>número de compte</b>:", + "STEP_CONGRATULATION_2_HELP": "Cesium també pot mostrar aquest número de compte en <b>un format més compacte</b>:", "REGISTRATION_NODE": "Su registro será grabado a través del nodo Duniter <b>{{server}}</b>, que luego se transmitirá al resto del sistema de la moneda.", "REGISTRATION_NODE_HELP": "Si no confía en este nodo, <a ng-click=\"doQuickFix('settings')\">cambie los ajustes</a> de Cesium.", "SELECT_ACCOUNT_TYPE": "Elegir el tipo de compte a crear:", @@ -679,7 +702,18 @@ "TITLE": "Cuenta i seguridad", "RECOVER_ID_HELP": "Si dispone de un <b>arxiu de recuperación de sus credenciales</b>, las puede reobtenir respondiendo correctamente a las preguntas personales elegidas en su momento.", "REVOCATION_WALLET_HELP": "Pedir la revocación de vuestra identitat comporta la <b>salida de la xarxa de confiança</b> (definitiva para el pseudònim i la clau pública asociada). La compte no producirá ya más el Dividend Universal.<br/>Podrá seguir usándola como moneder simple.", - "SAVE_ID_HELP": "Creación de un arxiu de recuperación, para <b>reobtenir su contraseña</b> (y frase secreta) en caso de olvido. El arxiu se <b>cifra</b> con ayuda de les preguntas personales elegidas." + "SAVE_ID_HELP": "Creación de un arxiu de recuperación, para <b>reobtenir su contraseña</b> (y frase secreta) en caso de olvido. El arxiu se <b>cifra</b> con ayuda de les preguntas personales elegidas.", + "BIOMETRIC": { + "TITLE": "Biometria", + "LABEL": "Utilitzar la biometria per validar", + "QUESTION": "Voleu utilitzar la biometria del vostre dispositiu per a l'aplicació?" + }, + "WAITING_CONFIRMATION": { + "TITLE": "Seguretat", + "HELP_PASSWORD": "Si us plau, introduïu la vostra contrasenya per validar la transacció.", + "HELP_CODE_PIN": "Si us plau, introduïu el vostre codi secret per validar la transacció.", + "ERROR": "Heu introduït un codi secret o una contrasenya no vàlida. Voleu tornar-ho a provar?<br/> Us queden {{retry}} intent(s)." + } }, "FILE_NAME": "{{currency}}_HistorialDeCuenta_{{pubkey|formatPubkey}}_{{currentTime|formatDateForFile}}.csv", "HEADERS": { diff --git a/src/assets/i18n/en-GB.json b/src/assets/i18n/en-GB.json index c7bd7855d44158077a1e0b04ec4adcd9914fc0e8..3b7bd4d49450d6ea135f98ea9b938806546763a6 100644 --- a/src/assets/i18n/en-GB.json +++ b/src/assets/i18n/en-GB.json @@ -199,7 +199,9 @@ "USE_SSL": "Secured?", "USE_SSL_HELP": "(SSL Encryption)", "BTN_SHOW_LIST": "Peer's list" - } + }, + "BIOMETRIC": "Device biometric authentication?", + "DURATION_BETWEEN_UD_CLAIM": "Period betwixt each universal dividend claim" }, "BLOCKCHAIN": { "HASH": "Hash: {{hash}}", @@ -537,10 +539,30 @@ }, "NEW": { "TITLE": "Registration", - "INTRO_WARNING_TIME": "Creating an account on {{name|capitalize}} is very simple. Please take sufficient time to do this correctly (not to forget the usernames, passwords, etc.).", - "INTRO_WARNING_SECURITY": "Check that the hardware you are currently using (computer, tablet, phone) <b>is secure and trustworthy </b>.", - "INTRO_WARNING_SECURITY_HELP": "Up-to-date anti-virus, firewall enabled, session protected by password or pin code...", - "INTRO_HELP": "Click <b> {{'COMMON.BTN_START'|translate}}</b> to begin creating an account. You will be guided step by step.", + "INTRO_WARNING_TIME": "Creating an account on {{name}} is very simple. However, please take sufficient time to properly complete this formality.", + "INTRO_WARNING_SECURITY": "Ensure that the device you are currently using (computer, tablet, telephone) <b>is secure and trustworthy</b>.", + "INTRO_WARNING_SECURITY_HELP": "Up-to-date anti-virus, activated firewall, session protected by password or PIN code, etc.", + "INTRO_HELP": "Click on <b>Start</b> to begin the account creation. You will be guided step by step.", + "STEP_1_HELP": "Cesium will generate a <b>recovery phrase</b>. It is rather like a blueprint that enables the creation of access keys to your account.", + "STEP_2_HELP": "Keep this phrase safe, as without it, Cesium will not be able to rebuild the access keys to your account. <br/>You will need it whenever you wish to install Cesium on a new telephone or computer.", + "STEP_3_HELP": "In a blockchain, there is no recovery procedure via email or SMS. Only your recovery phrase can enable you to retrieve your {{currency}} at any time.", + "STEP_4_HELP": "It is time to obtain <b>a piece of paper and a pencil</b> to write down your recovery phrase.", + "STEP_MNEMONIC_HELP": "Here is your recovery phrase!<br/>Endeavour to keep it absolutely secret, as anyone who finds it could empty your account.", + "STEP_CHECK_WORD_HELP": "Have you properly written down your recovery phrase?<br/>To ensure this, please type word #{{number}} of the phrase in the field below:", + "CODE_PIN": { + "STEP_1_HELP": "<b>A secret code</b> will now be generated.<br/>It is required to access your account on this device, without having to use the recovery phrase.", + "STEP_2_HELP": "This secret code also aims to <b>protect your account</b>. This is why <b>only you should possess it</b>.", + "STEP_3_HELP": "Here is your secret code!<br/>Memorise it perfectly, or write it down, as it will be required <b>every time</b> you wish to make a payment on this device. Similar to a PIN code for a bank card payment.", + "STEP_CHECK_HELP": "To verify that you have properly memorised or written down your secret code, please enter it below:" + }, + "PASSWORD": { + "STEP_1_HELP": "You will need to enter <b>a password</b>.<br/>It is required to access your account on this device, without having to use the recovery phrase.", + "STEP_2_HELP": "This password also aims to <b>protect your account</b>. This is why <b>only you should possess it</b>.", + "STEP_3_HELP": "You must enter your password<br/>Memorise it perfectly, or write it down, as it will be required <b>every time</b> you wish to make a payment on this device. Similar to a PIN code for a bank card payment.", + "STEP_CHECK_HELP": "To verify that you have properly memorised or written down your password, please confirm it below:" + }, + "STEP_CONGRATULATION_1_HELP": "Well done! Everything is ready to create your account.<br/><br/>It will have the following <b>account number</b>:", + "STEP_CONGRATULATION_2_HELP": "Cesium can also display this account number in <b>a more compact form</b>:", "REGISTRATION_NODE": "Your registration will be registered via the Duniter peer <b>{{server}}</b> node, which will then be distributed to the rest of the currency network.", "REGISTRATION_NODE_HELP": "If you do not trust this peer, please change <a ng-click=\"doQuickFix('settings')\">in the settings</a> of Cesium.", "SELECT_ACCOUNT_TYPE": "Choose the type of account to create:", @@ -669,7 +691,19 @@ "BAD_PASSWORD": "Bad passphrase", "BAD_CHECKSUM": "Bad checksum" } + }, + "BIOMETRIC": { + "TITLE": "Biometric", + "LABEL": "Use biometric authentication to validate", + "QUESTION": "Would you like to use your device's biometric authentication for the application?" + }, + "WAITING_CONFIRMATION": { + "TITLE": "Security", + "HELP_PASSWORD": "Please enter your password to validate the transaction.", + "HELP_CODE_PIN": "Please enter your PIN code to validate the transaction.", + "ERROR": "You have entered an invalid PIN code or password. Would you like to try again?<br/> You have {{retry}} attempt(s) remaining." } + }, "FILE_NAME": "{{currency}} - Account statement {{pubkey|formatPubkey}} to {{currentTime|formatDateForFile}}.csv", "HEADERS": { diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index ee9e32e4cd7920914995e4bc85517879d2a7735b..c5dd2bf9a35fb9f8489700a3f8eed39282168fd6 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -200,7 +200,9 @@ "USE_SSL": "Secured?", "USE_SSL_HELP": "(SSL Encryption)", "BTN_SHOW_LIST": "Peer's list" - } + }, + "BIOMETRIC": "Device biometric authentication?", + "DURATION_BETWEEN_UD_CLAIM": "Period between each universal dividend claim" }, "BLOCKCHAIN": { "HASH": "Hash: {{hash}}", @@ -551,10 +553,29 @@ }, "NEW": { "TITLE": "Registration", - "INTRO_WARNING_TIME": "Creating an account on {{name|capitalize}} is very simple. Please take sufficient time to do this correctly (not to forget the usernames, passwords, etc.).", - "INTRO_WARNING_SECURITY": "Check that the hardware you are currently using (computer, tablet, phone) <b>is secure and trustworthy </b>.", - "INTRO_WARNING_SECURITY_HELP": "Up-to-date anti-virus, firewall enabled, session protected by password or pin code...", - "INTRO_HELP": "Click <b> {{'COMMON.BTN_START'|translate}}</b> to begin creating an account. You will be guided step by step.", + "INTRO_WARNING_TIME": "Creating an account on {{name}} is very simple. However, please take enough time to properly complete this process.", + "INTRO_WARNING_SECURITY": "Make sure that the device you are currently using (computer, tablet, phone) <b>is secure and trustworthy</b>.", + "INTRO_WARNING_SECURITY_HELP": "Up-to-date antivirus, activated firewall, session protected by password or PIN code, etc.", + "STEP_1_HELP": "Cesium will generate a <b>recovery phrase</b>. It's like a blueprint that allows the creation of access keys to your account.", + "STEP_2_HELP": "Keep this phrase safe, because without it, Cesium won't be able to rebuild the access keys to your account. <br/>You will need it whenever you install Cesium on a new phone or computer.", + "STEP_3_HELP": "In a blockchain, there's no recovery procedure via email or SMS. Only your recovery phrase can allow you to retrieve your {{currency}} at any time.", + "STEP_4_HELP": "It's time to get <b>a piece of paper and a pencil</b> to write down your recovery phrase.", + "STEP_MNEMONIC_HELP": "Here is your recovery phrase!<br/>Try to keep it very secret, because anyone who finds it could empty your account.", + "STEP_CHECK_WORD_HELP": "Did you properly write down your recovery phrase?<br/>To make sure, please type word #{{number}} of the phrase in the field below:", + "CODE_PIN": { + "STEP_1_HELP": "<b>A secret code</b> will now be generated.<br/>It is needed to access your account on this device, without having to use the recovery phrase.", + "STEP_2_HELP": "This secret code also aims to <b>protect your account</b>. This is why <b>only you should possess it</b>.", + "STEP_3_HELP": "Here is your secret code!<br/>Memorize it perfectly, or write it down, as it will be required <b>every time</b> you want to make a payment on this device. Similar to a PIN code for a bank card payment.", + "STEP_CHECK_HELP": "To verify that you have properly memorized or written down your secret code, please enter it below:" + }, + "PASSWORD": { + "STEP_1_HELP": "You will need to enter <b>a password</b>.<br/>It is needed to access your account on this device, without having to use the recovery phrase.", + "STEP_2_HELP": "This password also aims to <b>protect your account</b>. This is why <b>only you should possess it</b>.", + "STEP_3_HELP": "You must enter your password<br/>Memorize it perfectly, or write it down, as it will be required <b>every time</b> you want to make a payment on this device. Similar to a PIN code for a bank card payment.", + "STEP_CHECK_HELP": "To verify that you have properly memorized or written down your password, please confirm it below:" + }, + "STEP_CONGRATULATION_1_HELP": "Congratulations! Everything is ready to create your account.<br/><br/>It will have the following <b>account number</b>:", + "STEP_CONGRATULATION_2_HELP": "Cesium can also display this account number in <b>a more compact form</b>:", "REGISTRATION_NODE": "Your registration will be registered via the Duniter peer <b>{{server}}</b> node, which will then be distributed to the rest of the currency network.", "REGISTRATION_NODE_HELP": "If you do not trust this peer, please change <a ng-click=\"doQuickFix('settings')\">in the settings</a> of Cesium.", "SELECT_ACCOUNT_TYPE": "Choose the type of account to create:", @@ -684,6 +705,17 @@ "BAD_PASSWORD": "Bad passphrase", "BAD_CHECKSUM": "Bad checksum" } + }, + "BIOMETRIC": { + "TITLE": "Biometric", + "LABEL": "Use biometric authentication to validate", + "QUESTION": "Would you like to use your device's biometric authentication for the application?" + }, + "WAITING_CONFIRMATION": { + "TITLE": "Security", + "HELP_PASSWORD": "Please enter your password to validate the transaction.", + "HELP_CODE_PIN": "Please enter your PIN code to validate the transaction.", + "ERROR": "You entered an invalid PIN code or password. Would you like to try again?<br/> You have {{retry}} attempt(s) remaining." } }, "FILE_NAME": "{{currency}} - Account statement {{pubkey|formatPubkey}} to {{currentTime|formatDateForFile}}.csv", diff --git a/src/assets/i18n/eo-EO.json b/src/assets/i18n/eo-EO.json index 3bbc0c5013744a5465614f2803a9749309228e90..78681ac12a3258163c653811cc28908d4c4c0ed5 100644 --- a/src/assets/i18n/eo-EO.json +++ b/src/assets/i18n/eo-EO.json @@ -198,7 +198,9 @@ "USE_SSL": "Sekurigita?", "USE_SSL_HELP": "(SSL-ĉifrado)", "BTN_SHOW_LIST": "Listo de la nodoj" - } + }, + "BIOMETRIC": "Biometria aŭtentigo de la aparato?", + "DURATION_BETWEEN_UD_CLAIM": "Periodo inter ĉiu universala dividenda postulo" }, "BLOCKCHAIN": { "HASH": "Haketo: {{hash}}", @@ -539,7 +541,27 @@ "INTRO_WARNING_TIME": "La kreado de konto ĉe {{name|capitalize}} estas tre simpla. Bonvolu tamen dediĉi sufiĉe da tempo por ĝuste efektivigi tiun proceduron (por ne forgesi la identigilojn, pasvortojn, ktp.).", "INTRO_WARNING_SECURITY": "Kontrolu ke la aparatoj, kiujn vi nun uzas (komputilo, tabuleto, telefono), <b>estas sekurigitaj kaj fidindaj</b>.", "INTRO_WARNING_SECURITY_HELP": "Senvirusigilo ĝisdata, fajroŝirmilo aktivigita, seanco protektita per pasvorto aŭ PIN-kodo, ktp.", - "INTRO_HELP": "Alklaku <b>{{'COMMON.BTN_START'|translate}}</b> por ekigi la kreadon de konto. Vi estos gvidata paŝon post paŝo.", + "INTRO_HELP": "Alklaku <b>Komenci</b> por lanĉi la konto-kreadon. Vi estos gvidata paŝon post paŝo.", + "STEP_1_HELP": "Cesium generos <b>reakiran frazon</b>. Ĝi estas kiel projekto kiu ebligas krei la alir-ŝlosilojn al via konto.", + "STEP_2_HELP": "Konservu ĉi tiun frazon zorge, ĉar sen ĝi Cesium ne povos rekrei la alir-ŝlosilojn al via konto. <br/>Vi bezonos ĝin ĉiufoje kiam vi devos instali Cesium en novan telefonon aŭ komputilon.", + "STEP_3_HELP": "En blokĉeno, ne ekzistas reakira proceduro per retpoŝto aŭ SMS. Nur via reakira frazo ebligos al vi reakiri viajn {{currency}} iam ajn.", + "STEP_4_HELP": "Estas tempo preni <b>paperon kaj krajonon</b> por noti vian reakiran frazon.", + "STEP_MNEMONIC_HELP": "Jen via reakira frazo!<br/>Zorgu teni ĝin absolute sekreta, ĉar iu ajn, kiu trovus ĝin, povus malplenigigi vian konton.", + "STEP_CHECK_WORD_HELP": "Ĉu vi ĝuste notis vian reakiran frazon?<br/>Por kontroli tion, tajpu la vorton #{{number}} de la frazo en la suban kampon:", + "CODE_PIN": { + "STEP_1_HELP": "<b>Sekreta kodo</b> nun estos generita.<br/>Ĝi estas necesa por aliri vian konton en ĉi tiu aparato, sen devi uzi la restaŭran frazon.", + "STEP_2_HELP": "Ĉi tiu sekreta kodo ankaŭ celas <b>protekti vian konton</b>. Tial <b>nur vi devas posedi ĝin</b>.", + "STEP_3_HELP": "Jen via sekreta kodo!<br/>Memoru ĝin perfekte, aŭ notu ĝin, ĉar ĝi estos postulata <b>ĉiufoje</b> kiam vi volas fari pagon per ĉi tiu aparato. Simile al PIN-kodo por bankokarta pago.", + "STEP_CHECK_HELP": "Por kontroli, ke vi bone memoris aŭ notis vian sekretan kodon, bonvolu entajpi ĝin sube:" + }, + "PASSWORD": { + "STEP_1_HELP": "Vi devos entajpi <b>pasvorton</b>.<br/>Ĝi estas necesa por aliri vian konton en ĉi tiu aparato, sen devi uzi la restaŭran frazon.", + "STEP_2_HELP": "Ĉi tiu pasvorto ankaŭ celas <b>protekti vian konton</b>. Tial <b>nur vi devas posedi ĝin</b>.", + "STEP_3_HELP": "Vi devas entajpi vian pasvorton<br/>Memoru ĝin perfekte, aŭ notu ĝin, ĉar ĝi estos postulata <b>ĉiufoje</b> kiam vi volas fari pagon per ĉi tiu aparato. Simile al PIN-kodo por bankokarta pago.", + "STEP_CHECK_HELP": "Por kontroli, ke vi bone memoris aŭ notis vian pasvorton, bonvolu konfirmi ĝin sube:" + }, + "STEP_CONGRATULATION_1_HELP": "Gratulojn! Ĉio estas preta por krei vian konton.<br/><br/>Ĝi havos la sekvan <b>konto-numeron</b>:", + "STEP_CONGRATULATION_2_HELP": "Cesium povas ankaŭ montri ĉi tiun konto-numeron en <b>pli kompakta formo</b>:", "REGISTRATION_NODE": "Via aliĝo estos registrita tra la nodo Duniter <b>{{server}}</b>, kiu dissendos ĝin poste al la cetero de la mon-reto.", "REGISTRATION_NODE_HELP": "Se vi ne fidas tiun nodon, bonvolu ŝanĝi ĝin <a ng-click=\"doQuickFix('settings')\">en la parametroj</a> de Cesium.", "SELECT_ACCOUNT_TYPE": "Elektu la tipon de konto kreota:", @@ -668,7 +690,19 @@ "BAD_PASSWORD": "Sekreta frazo malĝusta", "BAD_CHECKSUM": "Kontrol-sumo malĝusta" } + }, + "BIOMETRIC": { + "TITLE": "Biometrio", + "LABEL": "Uzi biometrion por validigi", + "QUESTION": "Ĉu vi volas uzi la biometrion de via aparato por la aplikaĵo?" + }, + "WAITING_CONFIRMATION": { + "TITLE": "Sekureco", + "HELP_PASSWORD": "Bonvolu enigi vian pasvorton por validigi la transakcion.", + "HELP_CODE_PIN": "Bonvolu enigi vian PIN-kodon por validigi la transakcion.", + "ERROR": "Vi enigis nevalidan PIN-kodon aŭ pasvorton. Ĉu vi volas provi denove?<br/> Vi havas {{retry}} provo(j)n restas." } + }, "FILE_NAME": "{{currency}} - Konto-tabelo {{pubkey|formatPubkey}} je {{currentTime|formatDateForFile}}.csv", "HEADERS": { diff --git a/src/assets/i18n/es-ES.json b/src/assets/i18n/es-ES.json index a8f3d5296c15b157dab18ecc6ffd10555ace8b01..a678ed11b1322df5896b5caea77b764ae313dc15 100644 --- a/src/assets/i18n/es-ES.json +++ b/src/assets/i18n/es-ES.json @@ -193,7 +193,9 @@ "USE_SSL": "Conexión segura", "USE_SSL_HELP": "(Cifrado SSL)", "BTN_SHOW_LIST": "Lista de nodos" - } + }, + "BIOMETRIC": "¿Autenticación biométrica del dispositivo?", + "DURATION_BETWEEN_UD_CLAIM": "Período entre cada reclamo del dividendo universal" }, "BLOCKCHAIN": { "HASH": "Hash: {{hash}}", @@ -627,7 +629,27 @@ "INTRO_WARNING_TIME": "Crear una cuenta en {{name|capitalize}} es muy simple. Sin embargo, por favor tome el tiempo suficiente para hacerlo correctamente (generar y memorizar bien las credenciales, etc.)", "INTRO_WARNING_SECURITY": "Asegúrese de que el dispositivo actual (ordenador, tablet, teléfono) <b>es seguro y de confianza</b>.", "INTRO_WARNING_SECURITY_HELP": "Antivirus, cortafuegos, sesión protegida por contraseña o número PIN, etc.", - "INTRO_HELP": "Haga clic en <b>{{'COMMON.BTN_START'|translate}}</b> para iniciar la creación de la cuenta. Se le guiará paso a paso.", + "INTRO_HELP": "Haz clic en <b>Comenzar</b> para iniciar la creación de la cuenta. Se te guiará paso a paso.", + "STEP_1_HELP": "Cesium generará una <b>frase de recuperación</b>. Es como un plano que permite crear las claves de acceso a tu cuenta.", + "STEP_2_HELP": "Guarda esta frase con cuidado, ya que sin ella Cesium no podrá recrear las claves de acceso a tu cuenta. <br/>La necesitarás cada vez que tengas que instalar Cesium en un nuevo teléfono u ordenador.", + "STEP_3_HELP": "En una blockchain, no existe ningún procedimiento de recuperación por correo electrónico o SMS. Solo tu frase de recuperación te permitirá recuperar tus {{currency}} en cualquier momento.", + "STEP_4_HELP": "Es hora de tomar <b>papel y lápiz</b> para anotar tu frase de recuperación.", + "STEP_MNEMONIC_HELP": "¡Aquí está tu frase de recuperación!<br/>Asegúrate de mantenerla absolutamente secreta, ya que cualquiera que la encuentre podría vaciar tu cuenta.", + "STEP_CHECK_WORD_HELP": "¿Has anotado correctamente tu frase de recuperación?<br/>Para comprobarlo, escribe la palabra #{{number}} de la frase en el campo siguiente:", + "CODE_PIN": { + "STEP_1_HELP": "<b>Un código secreto</b> será generado ahora.<br/>Es necesario para acceder a su cuenta en este dispositivo, sin tener que usar la frase de recuperación.", + "STEP_2_HELP": "Este código secreto también tiene como objetivo <b>proteger su cuenta</b>. Por eso <b>solo usted debe poseerlo</b>.", + "STEP_3_HELP": "¡Aquí está su código secreto!<br/>Memorícelo perfectamente, o anótelo, ya que se le pedirá <b>cada vez</b> que quiera realizar un pago en este dispositivo. Similar a un código PIN para un pago con tarjeta bancaria.", + "STEP_CHECK_HELP": "Para verificar que ha memorizado o anotado correctamente su código secreto, por favor introdúzcalo a continuación:" + }, + "PASSWORD": { + "STEP_1_HELP": "Deberá introducir <b>una contraseña</b>.<br/>Es necesaria para acceder a su cuenta en este dispositivo, sin tener que usar la frase de recuperación.", + "STEP_2_HELP": "Esta contraseña también tiene como objetivo <b>proteger su cuenta</b>. Por eso <b>solo usted debe poseerla</b>.", + "STEP_3_HELP": "Debe introducir su contraseña<br/>Memorícela perfectamente, o anótela, ya que se le pedirá <b>cada vez</b> que quiera realizar un pago en este dispositivo. Similar a un código PIN para un pago con tarjeta bancaria.", + "STEP_CHECK_HELP": "Para verificar que ha memorizado o anotado correctamente su contraseña, por favor confírmela a continuación:" + }, + "STEP_CONGRATULATION_1_HELP": "¡Felicitaciones! Todo está listo para crear tu cuenta.<br/><br/>Tendrá el siguiente <b>número de cuenta</b>:", + "STEP_CONGRATULATION_2_HELP": "Cesium también puede mostrar este número de cuenta en <b>una forma más compacta</b>:", "REGISTRATION_NODE": "Su registro será grabado a través del nodo Duniter <b>{{server}}</b>, que luego se transmitirá al resto del sistema de la moneda.", "REGISTRATION_NODE_HELP": "Si no confía en este nodo, <a ng-click=\"doQuickFix('settings')\">cambie los ajustes</a> de Cesium.", "SELECT_ACCOUNT_TYPE": "Elegir el tipo de cuenta a crear:", @@ -756,7 +778,18 @@ "TITLE": "Cuenta y seguridad", "RECOVER_ID_HELP": "Si dispone de un <b>archivo de recuperación de sus credenciales</b>, las puede reobtener respondiendo correctamente a las preguntas personales elegidas en su momento.", "REVOCATION_WALLET_HELP": "Pedir la revocación de vuestra identidad comporta la <b>salida de la red de confianza</b> (definitiva para el seudónimo y la llave pública asociada). La cuenta no producirá ya más el Dividendo Universal.<br/>Podrá seguir usándola como monedero simple.", - "SAVE_ID_HELP": "Creación de un archivo de recuperación, para <b>reobtener su contraseña</b> (y frase secreta) en caso de olvido. El archivo se <b>cifra</b> con ayuda de las preguntas personales elegidas." + "SAVE_ID_HELP": "Creación de un archivo de recuperación, para <b>reobtener su contraseña</b> (y frase secreta) en caso de olvido. El archivo se <b>cifra</b> con ayuda de las preguntas personales elegidas.", + "BIOMETRIC": { + "TITLE": "Biometría", + "LABEL": "Usar biometría para validar", + "QUESTION": "¿Desea utilizar la biometría de su dispositivo para la aplicación?" + }, + "WAITING_CONFIRMATION": { + "TITLE": "Seguridad", + "HELP_PASSWORD": "Por favor, introduzca su contraseña para validar la transacción.", + "HELP_CODE_PIN": "Por favor, introduzca su código PIN para validar la transacción.", + "ERROR": "Ha introducido un código PIN o contraseña no válidos. ¿Desea intentarlo de nuevo?<br/> Le quedan {{retry}} intento(s)." + } }, "FILE_NAME": "{{currency}}_HistorialDeCuenta_{{pubkey|formatPubkey}}_{{currentTime|formatDateForFile}}.csv", "HEADERS": { diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 226f07be2bdace53efe72518484f9db062b150b3..130315790b7943c4ad4f1bffcf871ed9a40bf84a 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -124,7 +124,7 @@ "MESSAGE": "Recevez et envoyez de la monnaie libre {{currency}}", "MESSAGE_READONLY": "Suivez l'état de la monnaie libre {{currency}} en temps réel.", "BTN_CURRENCY": "Explorer la monnaie {{currency}}", - "BTN_ABOUT": "à propos", + "BTN_ABOUT": "À propos", "BTN_HELP": "Aide en ligne", "BTN_NETWORK": "État du réseau", "FREE_SOFTWARE": "Logiciel libre", @@ -206,6 +206,7 @@ "USE_SSL_HELP": "(Chiffrement SSL)", "BTN_SHOW_LIST": "Liste des noeuds" }, + "BIOMETRIC": "Authentification biométrique de l'appareil ?", "DURATION_BETWEEN_UD_CLAIM": "Période entre chaque récupération du dividende universel" }, "BLOCKCHAIN": { @@ -573,10 +574,18 @@ "STEP_4_HELP": "Il est temps de vous munir d'<b>un papier et d'un crayon</b> afin de pouvoir noter votre phrase de restauration.", "STEP_MNEMONIC_HELP": "Voici votre phrase de restauration !<br/>Tâcher de la garder bien secrète, car quiconque la trouve pourra vider votre compte.", "STEP_CHECK_WORD_HELP": "Avez-vous bien noté votre phrase de restauration ?<br/>Pour en être sûr, veuillez taper dans le champs ci-dessous le mot n°{{number}} de la phrase :", - "STEP_CODE_1_HELP": "<b>Un code secret</b> va maintenant être généré.<br/>Il est nécessaire pour accéder à votre compte sur cet appareil, sans avoir à utiliser la phrase de restauration.", - "STEP_CODE_2_HELP": "Ce code secret a aussi pour but de <b>protèger aussi votre compte</b>. C'est pourquoi <b>vous seul devez le posséder</b>.", - "STEP_CODE_3_HELP": "Voici votre code secret !<br/>Mémorisez-le parfaitement, ou notez-le, car il vous sera demandé <b>à chaque fois</b> que voudrez effectuer un paiement sur cet appareil. Un peu comme le code pin pour un paiement par carte bancaire.", - "STEP_CHECK_CODE_HELP": "Pour vérifier que vous avez bien mémorisé ou noté votre code secret, veuillez le taper ci-dessous :", + "CODE_PIN": { + "STEP_1_HELP": "<b>Un code secret</b> va maintenant être généré.<br/>Il est nécessaire pour accéder à votre compte sur cet appareil, sans avoir à utiliser la phrase de restauration.", + "STEP_2_HELP": "Ce code secret a aussi pour but de <b>protèger aussi votre compte</b>. C'est pourquoi <b>vous seul devez le posséder</b>.", + "STEP_3_HELP": "Voici votre code secret !<br/>Mémorisez-le parfaitement, ou notez-le, car il vous sera demandé <b>à chaque fois</b> que voudrez effectuer un paiement sur cet appareil. Un peu comme le code pin pour un paiement par carte bancaire.", + "STEP_CHECK_HELP": "Pour vérifier que vous avez bien mémorisé ou noté votre code secret, veuillez le taper ci-dessous :" + }, + "PASSWORD": { + "STEP_1_HELP": "Vous allez devoir saisir <b>un mot de passe</b>.<br/>Il est nécessaire pour accéder à votre compte sur cet appareil, sans avoir à utiliser la phrase de restauration.", + "STEP_2_HELP": "Ce mot de passe a aussi pour but de <b>protèger aussi votre compte</b>. C'est pourquoi <b>vous seul devez le posséder</b>.", + "STEP_3_HELP": "Vous devez saisir votre mot de passe<br/>Mémorisez-le parfaitement, ou notez-le, car il vous sera demandé <b>à chaque fois</b> que voudrez effectuer un paiement sur cet appareil. Un peu comme le code pin pour un paiement par carte bancaire.", + "STEP_CHECK_HELP": "Pour vérifier que vous avez bien mémorisé ou noté votre mot de passe, veuillez le confirmer ci-dessous :" + }, "STEP_CONGRATULATION_1_HELP": "Bravo ! Tout est prêt pour créer votre compte.<br/><br/>Celui-ci aura le <b>numéro de compte</b> suivant :", "STEP_CONGRATULATION_2_HELP": "Cesium pourra aussi afficher ce numéro de compte <b>sous une forme plus compacte</b> :", "PSEUDO": "Pseudonyme", @@ -701,6 +710,17 @@ "BAD_PASSWORD": "Phrase secrète incorrecte", "BAD_CHECKSUM": "Somme de contrôle incorrecte" } + }, + "BIOMETRIC": { + "TITLE": "Biométrie", + "LABEL": "Utiliser la biométrie pour valider", + "QUESTION": "Voulez-vous utiliser la biométrie de votre appareil pour l'application ?" + }, + "WAITING_CONFIRMATION": { + "TITLE": "Sécurité", + "HELP_PASSWORD": "Veuillez saisir votre mot de passe pour valider la transaction.", + "HELP_CODE_PIN": "Veuillez saisir votre code secret pour valider la transaction.", + "ERROR": "Vous avez saisi un code secret ou un mot de passe invalide. Voulez-vous réessayer ?<br/> Il vous reste {{retry}} tentative(s)." } }, "FILE_NAME": "{{currency}} - Relevé du compte {{pubkey|formatPubkey}} au {{currentTime|formatDateForFile}}.csv", @@ -874,7 +894,8 @@ "UNKNOWN_WALLET_ID": "Portefeuille secondaire inconnu.", "RESTORE_WALLET_LIST_FAILED": "Échec de la restauration des portefeuilles secondaires.", "INVALID_FILE_FORMAT": "Format de fichier invalide.", - "SAME_TX_RECIPIENT": "Le destinataire doit être différent de l'émetteur." + "SAME_TX_RECIPIENT": "Le destinataire doit être différent de l'émetteur.", + "VALIDATION_TRANSFER_FAILED": "Le validation avant le transfer a échoué." }, "INFO": { "POPUP_TITLE": "Information", diff --git a/src/assets/i18n/it-IT.json b/src/assets/i18n/it-IT.json index 128731f36777a8297d96c836e495d50c6f0720c7..2603dc2524d00d575200ea622c83c7443cf04f50 100644 --- a/src/assets/i18n/it-IT.json +++ b/src/assets/i18n/it-IT.json @@ -179,7 +179,9 @@ "USE_SSL": "Cifrato?", "USE_SSL_HELP": "(Cifratura SSL)", "BTN_SHOW_LIST": "Lista dei nodi" - } + }, + "BIOMETRIC": "Autenticazione biometrica del dispositivo?", + "DURATION_BETWEEN_UD_CLAIM": "Periodo tra ogni richiesta di dividendo universale" }, "BLOCKCHAIN": { "HASH": "Hash: {{hash}}", @@ -518,6 +520,26 @@ "INTRO_WARNING_SECURITY": "Occorre verificare che l'hardware che stai utilizzando (computer, tablet, cellulare) <b>è sicuro e affidabile</b>.", "INTRO_WARNING_SECURITY_HELP": "Anti-virus aggiornato, firewall abilitato, sessione protteta da una password o codice PIN...", "INTRO_HELP": "Cliccare <b> {{'COMMON.BTN_START'|translate}}</b> per avviare la creazione del conto. Ti accompagniamo passo a passo.", + "STEP_1_HELP": "Cesium genererà una <b>frase di recupero</b>. È come un progetto che permette di creare le chiavi di accesso al tuo account.", + "STEP_2_HELP": "Conserva questa frase con cura, perché senza di essa Cesium non potrà ricreare le chiavi di accesso al tuo account. <br/>Ne avrai bisogno ogni volta che dovrai installare Cesium su un nuovo telefono o computer.", + "STEP_3_HELP": "In una blockchain, non esiste una procedura di recupero via email o SMS. Solo la tua frase di recupero ti permetterà di recuperare i tuoi {{currency}} in qualsiasi momento.", + "STEP_4_HELP": "È il momento di prendere <b>carta e penna</b> per annotare la tua frase di recupero.", + "STEP_MNEMONIC_HELP": "Ecco la tua frase di recupero!<br/>Cerca di mantenerla assolutamente segreta, perché chiunque la trovi potrebbe svuotare il tuo account.", + "STEP_CHECK_WORD_HELP": "Hai annotato correttamente la tua frase di recupero?<br/>Per verificarlo, inserisci la parola #{{number}} della frase nel campo sottostante:", + "CODE_PIN": { + "STEP_1_HELP": "<b>Un codice segreto</b> verrà ora generato.<br/>È necessario per accedere al tuo account su questo dispositivo, senza dover utilizzare la frase di recupero.", + "STEP_2_HELP": "Questo codice segreto serve anche a <b>proteggere il tuo account</b>. Per questo motivo <b>solo tu devi possederlo</b>.", + "STEP_3_HELP": "Ecco il tuo codice segreto!<br/>Memorizzalo perfettamente, o annotalo, poiché ti verrà richiesto <b>ogni volta</b> che vorrai effettuare un pagamento su questo dispositivo. Simile a un codice PIN per un pagamento con carta bancaria.", + "STEP_CHECK_HELP": "Per verificare che tu abbia memorizzato o annotato correttamente il tuo codice segreto, inseriscilo qui sotto:" + }, + "PASSWORD": { + "STEP_1_HELP": "Dovrai inserire <b>una password</b>.<br/>È necessaria per accedere al tuo account su questo dispositivo, senza dover utilizzare la frase di recupero.", + "STEP_2_HELP": "Questa password serve anche a <b>proteggere il tuo account</b>. Per questo motivo <b>solo tu devi possederla</b>.", + "STEP_3_HELP": "Devi inserire la tua password<br/>Memorizzala perfettamente, o annotala, poiché ti verrà richiesta <b>ogni volta</b> che vorrai effettuare un pagamento su questo dispositivo. Simile a un codice PIN per un pagamento con carta bancaria.", + "STEP_CHECK_HELP": "Per verificare che tu abbia memorizzato o annotato correttamente la tua password, confermala qui sotto:" + }, + "STEP_CONGRATULATION_1_HELP": "Complimenti! Tutto è pronto per creare il tuo account.<br/><br/>Avrà il seguente <b>numero di conto</b>:", + "STEP_CONGRATULATION_2_HELP": "Cesium può anche visualizzare questo numero di conto in <b>forma più compatta</b>:", "REGISTRATION_NODE": "La tua iscrizione verrà salvata dal nodo Duniter <b>{{server}}</b>, è verrà poi condivisa nella rete della moneta.", "REGISTRATION_NODE_HELP": "Se non ti fidi di questo nodo, per favore cambialo <a ng-click=\"doQuickFix('settings')\">nelle impostazioni/a> di Cesium.", "SELECT_ACCOUNT_TYPE": "Scegliere un tipo di conto:", @@ -611,7 +633,18 @@ "SAVE_ID": "Salvare le mie credenziali...", "SAVE_ID_HELP": "Creare un file di backup, per <b>recuperare la vostra password</b> (e l'identificativo segreto) <b> in caso di smarrimento</b>. Il file è <b>sicuro</ b> (cryptato) utilizzando le domande personalizzate.", "STRONG_LEVEL": "Alto <span class=\"hidden-xs \">(minimo di 6 domande)</span>", - "TITLE": "Conto e sicurezza" + "TITLE": "Conto e sicurezza", + "BIOMETRIC": { + "TITLE": "Biometria", + "LABEL": "Usa autenticazione biometrica per la validazione", + "QUESTION": "Vuoi utilizzare l'autenticazione biometrica del tuo dispositivo per l'applicazione?" + }, + "WAITING_CONFIRMATION": { + "TITLE": "Sicurezza", + "HELP_PASSWORD": "Inserisci la password per validare la transazione.", + "HELP_CODE_PIN": "Inserisci il codice PIN per validare la transazione.", + "ERROR": "Hai inserito un codice PIN o una password non validi. Vuoi riprovare?<br/> Hai ancora {{retry}} tentativo/i rimanente/i." + } }, "FILE_NAME": "{{currency}} - Account statement {{pubkey|formatPubkey}} to {{currentTime|formatDateForFile}}.csv", "HEADERS": { diff --git a/src/assets/i18n/nl-NL.json b/src/assets/i18n/nl-NL.json index b3a5605f91f0bd94cd7603add7b32a351e2775e7..a9e5bc5664033a18653ef3d3452f9481e9387854 100644 --- a/src/assets/i18n/nl-NL.json +++ b/src/assets/i18n/nl-NL.json @@ -154,7 +154,9 @@ "DAILY": "Elke dag", "WEEKLY": "Elke week", "MONTHLY": "Elke maand" - } + }, + "BIOMETRIC": "Biometrische apparaatauthenticatie?", + "DURATION_BETWEEN_UD_CLAIM": "Periode tussen elke universele dividend claim" }, "BLOCKCHAIN": { "HASH": "Hachee : {{hash}}", @@ -398,6 +400,30 @@ "MEMBER_CONVERT_FAILED": "Verzoek mislukt, u voldoet niet aan alle regels van het vertrouwensnetwerk.", "NEW": { "TITLE": "Registratie", + "INTRO_WARNING_TIME": "Een account aanmaken op {{name}} is heel eenvoudig. Neem echter voldoende tijd om deze procedure correct uit te voeren.", + "INTRO_WARNING_SECURITY": "Controleer of het apparaat dat u momenteel gebruikt (computer, tablet, telefoon) <b>beveiligd en betrouwbaar is</b>.", + "INTRO_WARNING_SECURITY_HELP": "Bijgewerkte antivirussoftware, actieve firewall, sessie beschermd met wachtwoord of pincode, etc.", + "INTRO_HELP": "Klik op <b>Starten</b> om te beginnen met het aanmaken van uw account. U wordt stap voor stap begeleid.", + "STEP_1_HELP": "Cesium genereert een <b>herstelzin</b>. Deze is als een blauwdruk waarmee de toegangssleutels tot uw account kunnen worden gemaakt.", + "STEP_2_HELP": "Bewaar deze zin zorgvuldig, want zonder deze kan Cesium de toegangssleutels tot uw account niet opnieuw maken. <br/>U heeft deze nodig wanneer u Cesium op een nieuwe telefoon of computer installeert.", + "STEP_3_HELP": "In een blockchain is er geen herstelprocedure via e-mail of sms. Alleen uw herstelzin stelt u in staat om uw {{currency}} op elk moment terug te krijgen.", + "STEP_4_HELP": "Het is tijd om <b>pen en papier</b> te pakken om uw herstelzin op te schrijven.", + "STEP_MNEMONIC_HELP": "Hier is uw herstelzin!<br/>Zorg ervoor dat u deze geheim houdt, want iedereen die deze vindt, kan uw account leegmaken.", + "STEP_CHECK_WORD_HELP": "Heeft u uw herstelzin goed opgeschreven?<br/>Om dit te controleren, typt u hieronder woord #{{number}} van de zin:", + "CODE_PIN": { + "STEP_1_HELP": "<b>Een geheime code</b> wordt nu gegenereerd.<br/>Deze is nodig om toegang te krijgen tot uw account op dit apparaat, zonder dat u de herstelzin hoeft te gebruiken.", + "STEP_2_HELP": "Deze geheime code dient ook om <b>uw account te beschermen</b>. Daarom mag <b>alleen u deze bezitten</b>.", + "STEP_3_HELP": "Hier is uw geheime code!<br/>Onthoud deze perfect, of schrijf hem op, want deze wordt <b>elke keer</b> gevraagd wanneer u een betaling wilt doen op dit apparaat. Vergelijkbaar met een pincode voor een betaling met bankpas.", + "STEP_CHECK_HELP": "Om te controleren of u uw geheime code goed heeft onthouden of opgeschreven, voer deze hieronder in:" + }, + "PASSWORD": { + "STEP_1_HELP": "U moet <b>een wachtwoord</b> invoeren.<br/>Dit is nodig om toegang te krijgen tot uw account op dit apparaat, zonder dat u de herstelzin hoeft te gebruiken.", + "STEP_2_HELP": "Dit wachtwoord dient ook om <b>uw account te beschermen</b>. Daarom mag <b>alleen u dit bezitten</b>.", + "STEP_3_HELP": "U moet uw wachtwoord invoeren<br/>Onthoud dit perfect, of schrijf het op, want dit wordt <b>elke keer</b> gevraagd wanneer u een betaling wilt doen op dit apparaat. Vergelijkbaar met een pincode voor een betaling met bankpas.", + "STEP_CHECK_HELP": "Om te controleren of u uw wachtwoord goed heeft onthouden of opgeschreven, bevestig het hieronder:" + }, + "STEP_CONGRATULATION_1_HELP": "Gefeliciteerd! Alles is klaar om uw account aan te maken.<br/><br/>Deze zal het volgende <b>rekeningnummer</b> hebben:", + "STEP_CONGRATULATION_2_HELP": "Cesium kan dit rekeningnummer ook weergeven in <b>een compactere vorm</b>:", "SLIDE_1_TITLE": "Selecteer een valuta:", "SLIDE_2_TITLE": "Soort rekening:", "MEMBER_ACCOUNT": "Persoonlijke rekening (lidmaatschap)", @@ -451,8 +477,60 @@ "PUBLIC_KEY_DIFFERENT": "De <b>openbare sleutel</b> moet <b>identiek</b> zijn aan die welke deze persoon je heeft meegedeeld. Zijn de sleutels <b>verschillend</b>?" }, "SHORT_LICENSE_REMINDER": "Je kunt de persoon eraan herinneren verschillende certificeringsparameters te certificeren:<br/><br/><ul><li>Elk lid kan maximaal 100 andere identiteiten certificeren.</li><li>De certificeringen worden met een interval van 5 dagen opgeslagen.</li><li>Een nieuwe identiteit moet in minder dan 2 maanden minstens 5 certificeringen verzamelen.</li><li>Een lid moet minstens eenmaal per jaar zijn lidmaatschap vernieuwen.</li><li>Certificeringen hebben een levensduur van twee jaar.</li></ul>" - } - }, + }, + "SECURITY": { + "ADD_QUESTION": "Persoonlijke vraag toevoegen", + "BTN_CLEAN": "Wissen", + "BTN_RESET": "Herstellen", + "DOWNLOAD_REVOKE": "Herroepingsbestand opslaan", + "DOWNLOAD_REVOKE_HELP": "Een herroepingsbestand is nodig als u uw inloggegevens verliest. Hiermee kunt u <b>uw account uit het Vertrouwensnetwerk verwijderen</b> en het terugbrengen naar een eenvoudige portemonnee.", + "HELP_LEVEL": "Kies <strong>ten minste {{nb}} vragen</strong>:", + "LEVEL": "Beveiligingsniveau", + "LOW_LEVEL": "Laag <span class=\"hidden-xs\">(minimaal 2 vragen)</span>", + "MEDIUM_LEVEL": "Gemiddeld <span class=\"hidden-xs\">(minimaal 4 vragen)</span>", + "QUESTION_1": "Wat was de naam van je beste vriend tijdens je tienerjaren?", + "QUESTION_2": "Wat was de naam van je eerste huisdier?", + "QUESTION_3": "Wat was het eerste gerecht dat je leerde koken?", + "QUESTION_4": "Wat was de eerste film die je in de bioscoop zag?", + "QUESTION_5": "Wat was de bestemming van je eerste vlucht?", + "QUESTION_6": "Wat was de naam van je favoriete leraar op school?", + "QUESTION_7": "Wat zou je droombaan zijn?", + "QUESTION_8": "Wat is je favoriete kinderboek?", + "QUESTION_9": "Wat was het merk van je eerste auto?", + "QUESTION_10": "Wat was je bijnaam als kind?", + "QUESTION_11": "Wie was je favoriete filmkarakter of acteur toen je student was?", + "QUESTION_12": "Wie was je favoriete muzikant/zanger/band toen je student was?", + "QUESTION_13": "In welke stad hebben je ouders elkaar ontmoet?", + "QUESTION_14": "Wat was de naam van je eerste baas?", + "QUESTION_15": "Wat is de naam van de straat waar je bent opgegroeid?", + "QUESTION_16": "Wat is de naam van je favoriete strand?", + "QUESTION_17": "Wat was het eerste album dat je kocht?", + "QUESTION_18": "Wat is de naam van je favoriete sportteam?", + "QUESTION_19": "Wat deed je grootvader voor werk?", + "RECOVER_ID": "Mijn wachtwoord herstellen...", + "RECOVER_ID_HELP": "Als u een <b>back-upbestand van uw inloggegevens</b> heeft, kunt u deze terugvinden door uw persoonlijke vragen correct te beantwoorden.", + "REVOCATION_WITH_FILE": "Mijn lidmaatschapsaccount herroepen...", + "REVOCATION_WITH_FILE_DESCRIPTION": "Als u denkt dat u uw lidmaatschapsgegevens <b>definitief bent kwijtgeraakt</b> (of dat de beveiliging van uw account is aangetast), kunt u het <b>herroepingsbestand</b> gebruiken om <b>uw account permanent uit het Vertrouwensnetwerk te verwijderen</b>.", + "REVOCATION_WITH_FILE_HELP": "Als u uw inloggegevens <b>definitief bent kwijtgeraakt</b> (of als de beveiliging van uw account is aangetast), kunt u het <b>herroepingsbestand</b> gebruiken om <b>het Vertrouwensnetwerk te verlaten</b>.", + "REVOCATION_WALLET": "Dit account nu herroepen", + "REVOCATION_WALLET_HELP": "Het verzoek om uw identiteit te verwijderen zal <b>uw lidmaatschap van het Vertrouwensnetwerk herroepen</b> (definitief voor de gebruikersnaam en bijbehorende publieke sleutel). Het account kan niet langer het Universeel Dividend genereren.<br/>U kunt het account wel nog steeds als gewone portemonnee gebruiken.", + "REVOCATION_FILENAME": "revocation-{{uid}}-{{pubkey|formatPubkey}}-{{currency}}.txt", + "SAVE_ID": "Mijn inloggegevens opslaan...", + "SAVE_ID_HELP": "Maak een back-upbestand aan om <b>uw wachtwoord</b> (en geheime ID) te kunnen <b>herstellen als u deze verliest</b>. Het bestand is <b>veilig</b> (versleuteld) met behulp van uw persoonlijke vragen.", + "STRONG_LEVEL": "Hoog <span class=\"hidden-xs\">(minimaal 6 vragen)</span>", + "TITLE": "Account en beveiliging", + "BIOMETRIC": { + "TITLE": "Biometrie", + "LABEL": "Gebruik biometrische authenticatie voor validatie", + "QUESTION": "Wilt u de biometrische authenticatie van uw apparaat gebruiken voor de applicatie?" + }, + "WAITING_CONFIRMATION": { + "TITLE": "Beveiliging", + "HELP_PASSWORD": "Voer uw wachtwoord in om de transactie te valideren.", + "HELP_CODE_PIN": "Voer uw pincode in om de transactie te valideren.", + "ERROR": "U heeft een ongeldige pincode of wachtwoord ingevoerd. Wilt u het opnieuw proberen?<br/> U heeft nog {{retry}} poging(en) over." + } + }, "TRANSFER": { "TITLE": "Overboeken", "SUB_TITLE": "Geld overboeken", diff --git a/src/manifest.json b/src/manifest.json index 3520786764d037fbc80d3b72ce62eda5b5dab981..3454a153be6bd873cd3673aaf10e2df17b3460b0 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -2,7 +2,7 @@ "short_name": "Cesium", "name": "Cesium2", "manifest_version": 2, - "version": "2.0.0-alpha49", + "version": "2.0.0-alpha51", "default_locale": "fr", "description": "Cesium Wallet for Ğ1 libre currency", "icons": [