diff --git a/android/app/build.gradle b/android/app/build.gradle index 55679eb9bead8a9808f3b2109ad4144b2a484a26..9278f8b078a89eab8598205d43c4ba801f358d9f 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 2000041 - versionName "2.0.0-alpha41" + versionCode 2000042 + versionName "2.0.0-alpha42" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/electron/package.json b/electron/package.json index 0c00bc51b4842aa869bc215e80645799c7c1237d..e4adb40323dd1e8ad2ecc4de308a08c708fb4040 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,6 +1,6 @@ { "name": "cesium2s", - "version": "2.0.0-alpha41", + "version": "2.0.0-alpha42", "description": "Cesium², running on Duniter v2s (Substrate).", "author": { "name": "Benoit Lavenier", diff --git a/graphql.config.yml b/graphql.config.yml index 5d96ef556aabd8e78d69fb49fda33cf6e7d74799..68a6ad952c55eb6779092514ca7dd90f2bc607db 100644 --- a/graphql.config.yml +++ b/graphql.config.yml @@ -1,7 +1,7 @@ projects: indexer: schema: src/app/network/indexer/indexer-schema.graphql - documents: "src/app/network/indexer/indexer-*!(.generated).{ts,gql}" + documents: "src/app/network/indexer/*!(.generated).{ts,gql}" extensions: endpoints: Gdev Indexer GraphQL Endpoint: @@ -26,14 +26,13 @@ projects: namedClient: 'indexer' src/app/network/indexer/indexer-helpers.generated.ts: plugins: - - "add" - "typescript-apollo-client-helpers" config: content: "// Auto-generated via `npx graphql-codegen`, do not edit\n/* eslint-disable */" pod: schema: src/app/network/pod/pod-schema.graphql - documents: "src/app/network/pod/pod-*!(.generated).{ts,gql}" + documents: "src/app/network/pod/*!(.generated).{ts,gql}" extensions: endpoints: Gdev Pod GraphQL Endpoint: diff --git a/install.sh b/install.sh index 065a2833d6c3c1284de1674825275580a560e4ee..1abcb385d25f0581dff45ee11f4c2fb9be9593cf 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-alpha41" #lastest + echo "2.0.0-alpha42" #lastest } api_release_url() { diff --git a/package-lock.json b/package-lock.json index 55b3a68194c4ec9f4c9c61c950aed63be814ac9a..40371375023a94f16b24b8f28821b182b5b4faf8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,9 +45,9 @@ "@polkadot/ui-settings": "^2.12.1", "@polkadot/util": "^10.4.2", "@polkadot/util-crypto": "^10.4.2", - "@rx-angular/cdk": "^17.0.1", - "@rx-angular/state": "^17.0.1", - "@rx-angular/template": "^17.1.0", + "@rx-angular/cdk": "^17.1.0", + "@rx-angular/state": "^17.2.0", + "@rx-angular/template": "^17.3.1", "apollo-angular": "~6.0.0", "apollo-link-logger": "~2.0.1", "apollo-link-queue": "~3.1.0", @@ -125,7 +125,7 @@ "shelljs": "~0.8.5", "stdio": "^2.1.3", "ts-node": "^8.10.2", - "typescript": "~5.2.2" + "typescript": "~5.4.5" }, "engines": { "node": ">= 18.18.2", @@ -9389,9 +9389,9 @@ ] }, "node_modules/@rx-angular/cdk": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@rx-angular/cdk/-/cdk-17.0.1.tgz", - "integrity": "sha512-l4iJA8hqlUWXsH+QFj6El66eIOmOH3zf6itnGMcxO3B44jjOlwyT/tj2jsL4uDJDUQyM22iY5kLSjinkW5OU+g==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@rx-angular/cdk/-/cdk-17.1.0.tgz", + "integrity": "sha512-N24LvggsDPjk7VJb5h9ICHxFW7EQbF2Qw6nnLCvfGR/yrP7VPGxbzl51pEOXZmN1lotBfzS28tLfCXqA3gMgyA==", "dependencies": { "ng-morph": "^4.0.3", "tslib": "^2.4.1" @@ -9416,9 +9416,9 @@ } }, "node_modules/@rx-angular/state": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@rx-angular/state/-/state-17.0.1.tgz", - "integrity": "sha512-S8ASWMNFh7hIyDV/EwRHpK5PCiOx26Ey0hiaQvQNuiQlGcOLjHVe5jCuTftc5AdPLpM+gc8v4ZIz0yOCdu8Ebg==", + "version": "17.2.0", + "resolved": "https://registry.npmjs.org/@rx-angular/state/-/state-17.2.0.tgz", + "integrity": "sha512-1wN7TOO2W/R5i6I/mLjOPzO+Bizeu2mrfGPA/dcKxUHNnMTvqjPah/cfK0QtxFxOTGMf5TtmPhlKC1bsoJroIg==", "dependencies": { "ng-morph": "^4.0.3", "tslib": "^2.4.1" @@ -9429,9 +9429,9 @@ } }, "node_modules/@rx-angular/template": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@rx-angular/template/-/template-17.1.0.tgz", - "integrity": "sha512-+dO4FJZk1GMJxKfIDMfngM0+XaEbIan2yAlngb46v2PXhFd3q21hihLhqL48hVjfu0UEApwKii5GyjlSjEs7nA==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/@rx-angular/template/-/template-17.3.1.tgz", + "integrity": "sha512-C9xdKNQ9qqkuHXosmqCrLRNkpqhZhqLJyhI29HCsk09BfDyaNrLVahppP0bBivGPWPsDeQ6ZU98K/BpT0N7JlQ==", "dependencies": { "ng-morph": "^4.0.3", "tslib": "^2.4.1" @@ -26944,9 +26944,9 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index c85c93a858ca74d72e5d875372c26a9666670979..1b96bec659174e8d7e343180b1d39af7e175958e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cesium", - "version": "2.0.0-alpha41", + "version": "2.0.0-alpha42", "description": "Manage G1 wallet", "author": "Benoit Lavenier <benoit.lavenier@e-is.pro>", "homepage": "https://cesium.app", @@ -116,9 +116,9 @@ "@polkadot/ui-settings": "^2.12.1", "@polkadot/util": "^10.4.2", "@polkadot/util-crypto": "^10.4.2", - "@rx-angular/cdk": "^17.0.1", - "@rx-angular/state": "^17.0.1", - "@rx-angular/template": "^17.1.0", + "@rx-angular/cdk": "^17.1.0", + "@rx-angular/state": "^17.2.0", + "@rx-angular/template": "^17.3.1", "apollo-angular": "~6.0.0", "apollo-link-logger": "~2.0.1", "apollo-link-queue": "~3.1.0", diff --git a/resources/webext/manifest.json b/resources/webext/manifest.json index ffa142a0474b58b73eb243c127f0f8541b4f59aa..b7711a488881ac19d7ea79db937cebea31dbeda7 100644 --- a/resources/webext/manifest.json +++ b/resources/webext/manifest.json @@ -1,8 +1,8 @@ { "manifest_version": 2, "name": "cesium2s", - "version": "2.0.0.41", - "version_name": "2.0.0-alpha41", + "version": "2.0.0.42", + "version_name": "2.0.0-alpha42", "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 441412fee193bf43b9ad8e68750c0996e10e55a2..d7b740368629a0cf33dcf673aa586e668598cd06 100644 --- a/src/app/account/accounts.service.ts +++ b/src/app/account/accounts.service.ts @@ -580,7 +580,6 @@ export class AccountsService extends RxStartableService<AccountsState> { */ async cert(from: Partial<Account>, to: Partial<Account>, opts = { allowCreation: true, confirmBeforeCreation: true }): Promise<string> { if (!from || !to) throw new Error("Missing argument 'from' or 'to' !"); - if (isNil(to.meta)) throw new Error("Missing 'to.meta' argument"); // Check currency const currency = this.network.currency; @@ -672,6 +671,37 @@ export class AccountsService extends RxStartableService<AccountsState> { } } + /// WIP should wot action be part of account.service or wot.service? + async confirm(account: Partial<Account>, uid: string): Promise<void> { + // TODO some checks: + // - status Unconfirmed + // - uid availability + + // THIS IS DUPLICATED CODE + const issuerPair = keyring.getPair(account.address); + if (issuerPair.isLocked) { + console.debug(`[account-service] Unlocking address ${account.address} ...`); + const isAuth = await this.auth(); + if (!isAuth) throw new Error('ERROR.AUTH_REQUIRED'); + issuerPair.unlock(this._password); + } + + // build tx + const tx = this.api.tx.identity.confirmIdentity(uid); + + // try run tx (also code duplication) + try { + const { status } = await ExtrinsicUtils.submit(tx, issuerPair); + console.info(`${this._logPrefix}Extrinsic status`, status.toHuman()); + } catch (err) { + const error = new ExtrinsicError(this.api, err, 'ERROR.SEND_CERT_FAILED'); + console.error(`${this._logPrefix}Cannot confirm: ${error?.message || error}`); + throw error; + } + + // TODO process status + } + /** * Load account data (balance, tx history, etc.). * This load can be skipped, when data already loaded (See options) diff --git a/src/app/account/auth/auth.form.ts b/src/app/account/auth/auth.form.ts index 57b408f7acf53560f4ce5678e327a347d6e861ce..e7b2a14f0c30afddc1e373a4a123dcb83d4c5661 100644 --- a/src/app/account/auth/auth.form.ts +++ b/src/app/account/auth/auth.form.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, inject, Injector, Input, OnInit, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, inject, Input, OnInit, Output } from '@angular/core'; import { FormBuilder, Validators } from '@angular/forms'; import { ModalController } from '@ionic/angular'; import { RegisterModal } from '../register/register.modal'; @@ -49,7 +49,6 @@ export class AuthForm extends AppForm<AuthData> implements OnInit { } constructor( - injector: Injector, settings: SettingsService, formBuilder: FormBuilder, private modalCtrl: ModalController, @@ -57,7 +56,6 @@ export class AuthForm extends AppForm<AuthData> implements OnInit { public network: NetworkService ) { super( - injector, formBuilder.group({ salt: [null, Validators.required], password: [null, Validators.required], diff --git a/src/app/account/auth/authv2.form.ts b/src/app/account/auth/authv2.form.ts index 9b28cbdb381ecfd8d7875c8a76ed1d6927f79579..e326a1cf1c138829ece6e73bec9ca5601620ef62 100644 --- a/src/app/account/auth/authv2.form.ts +++ b/src/app/account/auth/authv2.form.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Injector, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { FormBuilder, Validators } from '@angular/forms'; import { ModalController } from '@ionic/angular'; import { RegisterModal } from '../register/register.modal'; @@ -25,14 +25,12 @@ export class AuthV2Form extends AppForm<AuthData> implements OnInit { @Input() canRegister: boolean; constructor( - injector: Injector, settings: SettingsService, formBuilder: FormBuilder, private modalCtrl: ModalController, public network: NetworkService ) { super( - injector, formBuilder.group({ mnemonic: [null, Validators.required], }) diff --git a/src/app/account/confirm/identity-confirm.form.html b/src/app/account/confirm/identity-confirm.form.html new file mode 100644 index 0000000000000000000000000000000000000000..a67f14d0e95eb9f5db78a13b21cb5a654ebcebb5 --- /dev/null +++ b/src/app/account/confirm/identity-confirm.form.html @@ -0,0 +1,35 @@ +<form [formGroup]="form" novalidate (ngSubmit)="doSubmit($event)" (keyup.enter)="doSubmit($event)"> + <ion-text color="dark" class="ion-text-wrap ion-text-center"> + <p [innerHTML]="'ACCOUNT.CONFIRM_IDENTITY.PSEUDO_HELP' | translate"></p> + </ion-text> + + <ion-list> + <!-- error --> + <ion-item lines="none" *ngIf="error && !loading" @slideUpDownAnimation> + <ion-icon color="danger" slot="start" name="alert-circle"></ion-icon> + <ion-label color="danger" class="error" [innerHTML]="error | translate"></ion-label> + </ion-item> + + <!-- Pseudo --> + @if (_form | formGetControl: 'pseudo'; as control) { + <ion-item lines="none"> + <ion-input + [formControl]="control" + [label]="'ACCOUNT.NEW.PSEUDO' | translate" + labelPlacement="floating" + autocomplete="off" + [counter]="true" + [helperText]="control.valid ? ('ACCOUNT.NEW.PSEUDO_AVAILABLE' | translate) : ''" + [errorText]="control | formError: errorTranslatorOptions" + maxlength="42" + required="true" + inputmode="text" + type="text" + enterkeyhint="send" + [debounce]="450" + (ionInput)="markForCheck()" + ></ion-input> + </ion-item> + } + </ion-list> +</form> diff --git a/src/app/account/confirm/identity-confirm.form.ts b/src/app/account/confirm/identity-confirm.form.ts new file mode 100644 index 0000000000000000000000000000000000000000..3aae8f5b74ea7dbc45266ed7302a704784c91707 --- /dev/null +++ b/src/app/account/confirm/identity-confirm.form.ts @@ -0,0 +1,56 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { AppForm } from '@app/shared/form.class'; +import { FormBuilder, FormControl, Validators } from '@angular/forms'; +import { IdentityConfirmValidators } from '@app/account/confirm/identity-confirm.validator'; +import { IndexerService } from '@app/network/indexer/indexer.service'; +import { SharedValidators } from '@app/shared/form/form-validators'; +import { filter, first } from 'rxjs/operators'; +import { debounceTime } from 'rxjs'; + +export interface IdentityConfirmData { + pseudo: string; +} +@Component({ + selector: 'app-identity-confirm-form', + templateUrl: 'identity-confirm.form.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class IdentityConfirmForm extends AppForm<IdentityConfirmData> implements OnInit { + constructor(formBuilder: FormBuilder, indexerService: IndexerService) { + super(); + + this.setForm( + formBuilder.group({ + pseudo: new FormControl<string>( + null, + // Validators + [Validators.required, Validators.minLength(3), SharedValidators.uid], + // Async validators + IdentityConfirmValidators.availableUid(indexerService) + ), + }) + ); + + this._i18nPrefix = 'ACCOUNT.NEW.'; + } + + ngOnInit() { + console.debug(`${this._logPrefix} Init`); + super.ngOnInit(); + + // Force control to be touched, if invalid and more than 3 characters + const pseudoControl = this.form.get('pseudo'); + this.registerSubscription( + pseudoControl.statusChanges + .pipe( + debounceTime(450), + filter(() => !pseudoControl.touched && pseudoControl.invalid), + first() + ) + .subscribe(() => { + pseudoControl.markAsTouched({ onlySelf: true }); + this.markForCheck(); + }) + ); + } +} diff --git a/src/app/account/confirm/identity-confirm.modal.html b/src/app/account/confirm/identity-confirm.modal.html new file mode 100644 index 0000000000000000000000000000000000000000..c720f078fc7733dc426061a70bc072e9581a5b66 --- /dev/null +++ b/src/app/account/confirm/identity-confirm.modal.html @@ -0,0 +1,48 @@ +<ion-header> + <ion-toolbar color="secondary"> + <ion-buttons slot="start"> + @if (mobile) { + <ion-button (click)="cancel()"> + <ion-icon slot="icon-only" name="arrow-back"></ion-icon> + </ion-button> + } + </ion-buttons> + + <ion-title translate>ACCOUNT.CONFIRM_IDENTITY.TITLE</ion-title> + + <ion-buttons slot="end"> + <ion-spinner *ngIf="pending"></ion-spinner> + + @if (mobile) { + <ion-button [disabled]="!valid" (click)="doSubmit()"> + <ion-icon slot="icon-only" name="send"></ion-icon> + </ion-button> + } + </ion-buttons> + </ion-toolbar> +</ion-header> + +<ion-content class="ion-padding-top"> + <app-identity-confirm-form #form (validate)="doSubmit()" (cancel)="cancel()"></app-identity-confirm-form> +</ion-content> + +@if (!mobile) { + <ion-footer> + <ion-toolbar> + <ion-row class="ion-no-padding" nowrap> + <ion-col></ion-col> + + <!-- buttons --> + <ion-col size="auto"> + <ion-button fill="clear" color="dark" (click)="cancel()"> + <ion-label translate>COMMON.BTN_CANCEL</ion-label> + </ion-button> + + <ion-button [fill]="invalid ? 'clear' : 'solid'" [disabled]="invalid" (click)="doSubmit()" (keyup.enter)="doSubmit()"> + <ion-label translate>ACCOUNT.BTN_CONFIRM_MEMBERSHIP</ion-label> + </ion-button> + </ion-col> + </ion-row> + </ion-toolbar> + </ion-footer> +} diff --git a/src/app/account/confirm/identity-confirm.modal.ts b/src/app/account/confirm/identity-confirm.modal.ts new file mode 100644 index 0000000000000000000000000000000000000000..f02737b8ced318e9e3cf327f623bcf73df4f103a --- /dev/null +++ b/src/app/account/confirm/identity-confirm.modal.ts @@ -0,0 +1,54 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnInit, ViewChild } from '@angular/core'; +import { ModalController } from '@ionic/angular'; +import { Account } from '@app/account/account.model'; +import { IdentityConfirmForm } from '@app/account/confirm/identity-confirm.form'; +import { SettingsService } from '@app/settings/settings.service'; + +export interface IdentityConfirmModalOptions { + account: Account; +} + +export declare type IdentityConfirmModalRole = 'CANCEL' | 'VALIDATE'; +@Component({ + selector: 'app-identity-confirm-modal', + templateUrl: 'identity-confirm.modal.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class IdentityConfirmModal implements IdentityConfirmModalOptions, OnInit { + protected readonly mobile = inject(SettingsService).mobile; + + @Input() account: Account; + + get pending() { + return this.form.pending; + } + + get invalid() { + return this.form.invalid; + } + + get valid() { + return this.form.valid; + } + + @ViewChild('form', { static: true }) form: IdentityConfirmForm; + + constructor( + private viewCtrl: ModalController, + private _cd: ChangeDetectorRef + ) {} + + ngOnInit() { + this.form.markAsReady({ emitEvent: false }); + this.form.markAsLoaded(); + this.form.enable(); + } + + cancel() { + this.viewCtrl.dismiss(null, <IdentityConfirmModalRole>'CANCEL'); + } + + doSubmit() { + this.viewCtrl.dismiss(this.form.value, <IdentityConfirmModalRole>'VALIDATE'); + } +} diff --git a/src/app/account/confirm/identity-confirm.module.ts b/src/app/account/confirm/identity-confirm.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..d7c69daf57a2ee993b17cdcdabdb4fe6b736c02a --- /dev/null +++ b/src/app/account/confirm/identity-confirm.module.ts @@ -0,0 +1,13 @@ +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { IdentityConfirmForm } from '@app/account/confirm/identity-confirm.form'; +import { IdentityConfirmModal } from '@app/account/confirm/identity-confirm.modal'; +import { AppSharedModule } from '@app/shared/shared.module'; + +@NgModule({ + imports: [AppSharedModule], + declarations: [IdentityConfirmForm, IdentityConfirmModal], + exports: [AppSharedModule, IdentityConfirmForm, IdentityConfirmModal, TranslateModule], + schemas: [CUSTOM_ELEMENTS_SCHEMA], +}) +export class AppIdentityConfirmModule {} diff --git a/src/app/account/confirm/identity-confirm.validator.ts b/src/app/account/confirm/identity-confirm.validator.ts new file mode 100644 index 0000000000000000000000000000000000000000..7b3404a11e1329b1951fce63bc4f5b1081c25ab5 --- /dev/null +++ b/src/app/account/confirm/identity-confirm.validator.ts @@ -0,0 +1,23 @@ +import { AsyncValidatorFn } from '@angular/forms'; +import { isNotEmptyArray, isNotNilOrBlank } from '@app/shared/functions'; +import { IndexerService } from '@app/network/indexer/indexer.service'; +import { firstValueFrom } from 'rxjs'; + +export abstract class IdentityConfirmValidators { + static availableUid(indexer: IndexerService): AsyncValidatorFn { + return async (control) => { + const name = control.value; + if (isNotNilOrBlank(name) && indexer.started) { + const { data } = await firstValueFrom(indexer.wotSearch({ uid: name }, { first: 1, fetchPolicy: 'no-cache' })); + if (isNotEmptyArray(data)) { + return { availableUid: true }; + } + } + return undefined; // No error + }; + } + + static readonly I18N_ERROR_KEYS = { + availableUid: 'ACCOUNT.NEW.PSEUDO_NOT_AVAILABLE', + }; +} diff --git a/src/app/account/register/register.form.ts b/src/app/account/register/register.form.ts index 2b3d511edc443449ec0894e153c2936c3ebacd16..9a974976b9b326f63471a279082b79e060729262 100644 --- a/src/app/account/register/register.form.ts +++ b/src/app/account/register/register.form.ts @@ -1,4 +1,4 @@ -import { Component, Injector, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { AbstractControl, FormBuilder, FormControl, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; import { AccountsService } from '@app/account/accounts.service'; import { SettingsService } from '@app/settings/settings.service'; @@ -56,22 +56,21 @@ export class RegisterForm extends AppForm<AuthData> implements OnInit { @ViewChild(SwiperDirective) swiperDir: SwiperDirective; constructor( - injector: Injector, private accountService: AccountsService, private networkService: NetworkService, public formBuilder: FormBuilder, protected settings?: SettingsService ) { - super(injector); + super(); this.setForm( formBuilder.group({ - words: new FormControl(null, Validators.required), - wordNumber: new FormControl(null, Validators.required), - code: new FormControl(null, Validators.required), - codeConfirmation: new FormControl(null, Validators.compose([Validators.required, this.equalsValidator('code')])), - name: new FormControl(null), - address: new FormControl(null), + words: new FormControl<string>(null, Validators.required), + wordNumber: new FormControl<number>(null, Validators.required), + code: new FormControl<string>(null, Validators.required), + codeConfirmation: new FormControl<string>(null, Validators.compose([Validators.required, this.equalsValidator('code')])), + name: new FormControl<string>(null), + address: new FormControl<string>(null), }) ); diff --git a/src/app/account/unlock/unlock.form.ts b/src/app/account/unlock/unlock.form.ts index e251281b90706a3cd439139ccc9e56e5a6d19672..b9fa44717a9536c0607a919d57b0f3cb07d7ad69 100644 --- a/src/app/account/unlock/unlock.form.ts +++ b/src/app/account/unlock/unlock.form.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Injector, Input, OnInit, Optional, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Optional, Output } from '@angular/core'; import { AbstractControl, FormBuilder, FormControl, FormGroup, FormGroupDirective, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; import { SettingsService } from '@app/settings/settings.service'; import { environment } from '@environments/environment'; @@ -35,12 +35,11 @@ export class UnlockForm extends AppForm<string> implements OnInit { readonly maskPredicate: MaskitoElementPredicateAsync = async (el) => (el as HTMLIonInputElement).getInputElement(); constructor( - injector: Injector, public formBuilder: FormBuilder, protected settings?: SettingsService, @Optional() protected formGroupDir?: FormGroupDirective ) { - super(injector); + super(); this.debug = !environment.production; } diff --git a/src/app/account/wallet/wallet.page.html b/src/app/account/wallet/wallet.page.html index 53128d3b1eaf536ff365a1a32e7eee15fe9114fa..0238549e084dc6564913930c3994e55ae45305d5 100644 --- a/src/app/account/wallet/wallet.page.html +++ b/src/app/account/wallet/wallet.page.html @@ -67,14 +67,25 @@ <div id="container"> <div class="ion-text-center ion-padding-top" *ngIf="!mobile"> - <ion-button *rxIf="account$; let account" (click)="transfer()" [disabled]="loading"> - <ion-icon slot="start" name="paper-plane"></ion-icon> - <ion-label translate>COMMON.BTN_SEND_MONEY</ion-label> - </ion-button> + @if (account$ | push; as account) { + <!-- Send TX --> + <ion-button (click)="transfer()" [disabled]="loading"> + <ion-icon slot="start" name="paper-plane"></ion-icon> + <ion-label translate>COMMON.BTN_SEND_MONEY</ion-label> + </ion-button> + + <!-- Confirm identity --> + @if (account.meta?.status === IdentityStatusEnum.Unconfirmed) { + <ion-button (click)="confirmIdentity()" [disabled]="loading" color="tertiary"> + <!-- <ion-icon slot="start" name="checkmark-circle-outline"></ion-icon>--> + <ion-label translate>ACCOUNT.BTN_CONFIRM_MEMBERSHIP</ion-label> + </ion-button> + } + } </div> <ion-list> - <ion-item *rxIf="error$; let error" lines="none" color="light"> + <ion-item *rxIf="error$; let error" lines="none" color="light" @fadeInOutAnimation> <ion-icon slot="start" name="alert-circle" color="danger"></ion-icon> <ion-label color="danger">{{ error | translate }}</ion-label> </ion-item> @@ -121,16 +132,37 @@ @if (account.meta?.index) { <ion-item> <ion-icon aria-hidden="true" slot="start" name="calendar-clear-outline"></ion-icon> - <ion-label> - <h3 translate>COMMON.UID</h3> - <p> - {{ 'WOT.REGISTERED_SINCE' | translate }} {{ account.meta.createdOn | blockTime | dateFormat }} - - {{ 'WOT.REGISTERED_SINCE_BLOCK' | translate }} {{ account.meta.createdOn | blockNumber }} - </p> - </ion-label> - <ion-badge [color]="account.meta.isMember ? 'warning' : 'medium'" slot="end"> - {{ account.meta.uid }} ({{ account.meta.status }}) - </ion-badge> + + <!-- special case for unconfirmed (UID is not known yet)--> + @if (account.meta.status === IdentityStatusEnum.Unconfirmed) { + <ion-label color="danger" [innerHTML]="'WOT.IDENTITY_UNCONFIRMED' | translate"></ion-label> + + @if (mobile) { + <ion-button slot="end" (click)="confirmIdentity()" [disabled]="loading" color="tertiary"> + <ion-label translate>COMMON.BTN_CONFIRM</ion-label> + </ion-button> + } @else { + <ion-button slot="end" (click)="confirmIdentity()" [disabled]="loading" fill="clear" color="tertiary"> + <ion-label translate>COMMON.BTN_CONFIRM</ion-label> + </ion-button> + } + } @else { + <ion-label> + <h3 translate>COMMON.UID</h3> + <p> + {{ 'WOT.REGISTERED_SINCE' | translate }} {{ account.meta.createdOn | blockTime | dateFormat }} - + {{ 'WOT.REGISTERED_SINCE_BLOCK' | translate }}{{ account.meta.createdOn | blockNumber }} + </p> + </ion-label> + <ion-buttons slot="end" class="vertical-alignment"> + <ion-badge [color]="account.meta.isMember ? 'warning' : 'medium'"> + {{ account.meta?.uid }} + </ion-badge> + @if (!account.meta.isMember) { + <ion-note color="danger">({{ 'WOT.STATUS_ENUM.' + account.meta.status | translate }})</ion-note> + } + </ion-buttons> + } </ion-item> <!-- Received cert count --> diff --git a/src/app/account/wallet/wallet.page.ts b/src/app/account/wallet/wallet.page.ts index 2e0f39f9709a9bc7048a4025098605976fe49838..ca08ac231e1b41141827c8e43c0c41645087a54b 100644 --- a/src/app/account/wallet/wallet.page.ts +++ b/src/app/account/wallet/wallet.page.ts @@ -3,9 +3,9 @@ import { ChangeDetectionStrategy, Component, Inject, OnInit, ViewChild } from '@ import { Clipboard } from '@capacitor/clipboard'; import { AppPage, AppPageState } from '@app/shared/pages/base-page.class'; import { Account } from '@app/account/account.model'; -import { isNil, isNotEmptyArray, isNotNilOrBlank } from '@app/shared/functions'; +import { isNil, isNotEmptyArray, isNotNil, isNotNilOrBlank } from '@app/shared/functions'; import { NetworkService } from '@app/network/network.service'; -import { ActionSheetOptions, IonModal, IonPopover, PopoverOptions } from '@ionic/angular'; +import { ActionSheetOptions, IonModal, IonPopover, ModalController, PopoverOptions } from '@ionic/angular'; import { ActivatedRoute, Router } from '@angular/router'; import { RxStateProperty, RxStateSelect } from '@app/shared/decorator/state.decorator'; import { distinctUntilChanged, filter, mergeMap, switchMap } from 'rxjs/operators'; @@ -17,6 +17,10 @@ import { TranslateModule } from '@ngx-translate/core'; import { AppSharedModule } from '@app/shared/shared.module'; import { AppAccountModule } from '@app/account/account.module'; import { AppAuthModule } from '@app/account/auth/auth.module'; +import { IdentityStatusEnum } from '@app/network/indexer/indexer-types.generated'; +import { AppIdentityConfirmModule } from '@app/account/confirm/identity-confirm.module'; +import { IdentityConfirmModal } from '@app/account/confirm/identity-confirm.modal'; +import { fadeInOutAnimation, slideUpDownAnimation } from '@app/shared/animations'; export interface WalletState extends AppPageState { accounts: Account[]; @@ -26,6 +30,7 @@ export interface WalletState extends AppPageState { balance: number; receivedCertCount: number; givenCertCount: number; + status: IdentityStatusEnum; } @Component({ @@ -35,13 +40,16 @@ export interface WalletState extends AppPageState { changeDetection: ChangeDetectionStrategy.OnPush, providers: [RxState], standalone: true, - imports: [TranslateModule, AppSharedModule, AppAccountModule, AppAuthModule], + animations: [fadeInOutAnimation, slideUpDownAnimation], + imports: [TranslateModule, AppSharedModule, AppAccountModule, AppAuthModule, AppIdentityConfirmModule], }) export class WalletPage extends AppPage<WalletState> implements OnInit { static NEW = Object.freeze(<Account>{ address: '', }); + IdentityStatusEnum = IdentityStatusEnum; + protected qrCodeValue: string; protected qrCodeTitle: string; @@ -54,6 +62,7 @@ export class WalletPage extends AppPage<WalletState> implements OnInit { @RxStateSelect() accounts$: Observable<Account[]>; @RxStateSelect() receivedCertCount$: Observable<number>; @RxStateSelect() givenCertCount$: Observable<number>; + @RxStateSelect() status$: Observable<IdentityStatusEnum>; get balance(): number { if (!this.account?.data) return undefined; @@ -80,6 +89,7 @@ export class WalletPage extends AppPage<WalletState> implements OnInit { protected route: ActivatedRoute, protected networkService: NetworkService, protected accountService: AccountsService, + protected modalCtrl: ModalController, @Inject(APP_TRANSFER_CONTROLLER) protected transferController: ITransferController ) { super({ @@ -153,6 +163,14 @@ export class WalletPage extends AppPage<WalletState> implements OnInit { map(({ total }) => total) ) ); + + this._state.connect( + 'status', + this.account$.pipe( + map((account) => account?.meta?.status), + filter(isNotNil) + ) + ); } async ngOnInit() { @@ -205,4 +223,42 @@ export class WalletPage extends AppPage<WalletState> implements OnInit { transfer(opts?: TransferFormOptions) { return this.transferController.transfer({ account: this.account, ...opts }); } + + get isUnconfirmed() { + return this.account.meta?.status == IdentityStatusEnum.Unconfirmed; + } + + async confirmIdentity(): Promise<void> { + console.info(`${this._logPrefix}Opening identity confirm modal...`); + + const modal = await this.modalCtrl.create({ + component: IdentityConfirmModal, + presentingElement: this._presentingElement, + canDismiss: true, + }); + await modal.present(); + const { data, role } = await modal.onWillDismiss(); + + if (!data || role === 'CANCEL') { + console.info(`${this._logPrefix}User cancelled identity confirmation`); + this.markAsLoaded(); + return; + } + + console.info(`${this._logPrefix}User confirmed. Pseudo: ${data.pseudo}`); + this.markAsLoading(); + + try { + await this.showToast({ id: 'confirm-identity', message: 'INFO.CONFIRM_IDENTITY_PENDING', duration: -1 }); + + // Send confirmation + await this.accountService.confirm(this.account, data.pseudo); + + // Show toast + await this.showToast({ id: 'confirm-identity', message: 'INFO.CONFIRM_IDENTITY_DONE', swipeGesture: 'vertical', color: 'secondary' }); + } catch (err) { + this.showErrorToast(err, { id: 'confirm-identity' }); + this.markAsLoaded(); + } + } } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 373ce9f1c33fce7e2679896c0bc15d1c71792d2b..6bcbbf20703610a86a177fb00d926c697cb182aa 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -10,6 +10,7 @@ import { fadeInAnimation } from '@app/shared/animations'; import { SettingsService } from '@app/settings/settings.service'; import { TranslateService } from '@ngx-translate/core'; import { AlertController } from '@ionic/angular'; +import { WotController } from './wot/wot.controller'; export interface IMenuItem { title: string; @@ -70,6 +71,7 @@ export class AppComponent { protected settings: SettingsService, private accountService: AccountsService, private transferController: TransferController, + private wotController: WotController, private translate: TranslateService, private alertController: AlertController, private router: Router diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 35c322af862815db97501561e7a99528b58f1a8b..b0229f05ac0d6834bea8fb066b685e81be7012dc 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -25,6 +25,9 @@ import { AppTransferModule } from '@app/transfer/send/transfer.module'; import { APP_GRAPHQL_TYPE_POLICIES } from '@app/shared/services/network/graphql/graphql.service'; import { INDEXER_GRAPHQL_TYPE_POLICIES } from '@app/network/indexer/indexer.config'; import { POD_GRAPHQL_TYPE_POLICIES } from '@app/network/pod/pod.config'; +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'; export function createTranslateLoader(http: HttpClient) { if (environment.production) { @@ -59,6 +62,7 @@ export function createTranslateLoader(http: HttpClient) { AppSharedModule, AppAccountModule.forRoot(), AppTransferModule.forRoot(), + AppWotModule.forRoot(), ], providers: [ PlatformService, @@ -106,6 +110,14 @@ export function createTranslateLoader(http: HttpClient) { backColor: '#0000', }, }, + + // Custom error i18nk keys + { + provide: APP_FORM_ERROR_I18N_KEYS, + useValue: { + ...IdentityConfirmValidators.I18N_ERROR_KEYS, + }, + }, ], bootstrap: [AppComponent], }) diff --git a/src/app/network/indexer/indexer-helpers.generated.ts b/src/app/network/indexer/indexer-helpers.generated.ts index 39ddea059e5d3fabb074796af5e463f827395f0c..d32e89bbf0e70d8779ac6d4fb9c70cc84074e802 100644 --- a/src/app/network/indexer/indexer-helpers.generated.ts +++ b/src/app/network/indexer/indexer-helpers.generated.ts @@ -1,8 +1,6 @@ -// Auto-generated via `npx graphql-codegen`, do not edit -/* eslint-disable */ import { FieldPolicy, FieldReadFunction, TypePolicies, TypePolicy } from '@apollo/client/cache'; export type AccountKeySpecifier = ('id' | 'identity' | 'linkedIdentity' | 'linkedIdentityId' | 'transfersIssued' | 'transfersIssuedAggregate' | 'transfersIssued_connection' | 'transfersReceived' | 'transfersReceivedAggregate' | 'transfersReceived_connection' | 'wasIdentity' | 'wasIdentityAggregate' | 'wasIdentity_connection' | AccountKeySpecifier)[]; -export type AccountFieldPolicy = { +export interface AccountFieldPolicy { id?: FieldPolicy<any> | FieldReadFunction<any>, identity?: FieldPolicy<any> | FieldReadFunction<any>, linkedIdentity?: FieldPolicy<any> | FieldReadFunction<any>, @@ -16,40 +14,40 @@ export type AccountFieldPolicy = { wasIdentity?: FieldPolicy<any> | FieldReadFunction<any>, wasIdentityAggregate?: FieldPolicy<any> | FieldReadFunction<any>, wasIdentity_connection?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type AccountAggregateKeySpecifier = ('aggregate' | 'nodes' | AccountAggregateKeySpecifier)[]; -export type AccountAggregateFieldPolicy = { +export interface AccountAggregateFieldPolicy { aggregate?: FieldPolicy<any> | FieldReadFunction<any>, nodes?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type AccountAggregateFieldsKeySpecifier = ('count' | 'max' | 'min' | AccountAggregateFieldsKeySpecifier)[]; -export type AccountAggregateFieldsFieldPolicy = { +export interface AccountAggregateFieldsFieldPolicy { count?: FieldPolicy<any> | FieldReadFunction<any>, max?: FieldPolicy<any> | FieldReadFunction<any>, min?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type AccountConnectionKeySpecifier = ('edges' | 'pageInfo' | AccountConnectionKeySpecifier)[]; -export type AccountConnectionFieldPolicy = { +export interface AccountConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type AccountEdgeKeySpecifier = ('cursor' | 'node' | AccountEdgeKeySpecifier)[]; -export type AccountEdgeFieldPolicy = { +export interface AccountEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type AccountMaxFieldsKeySpecifier = ('id' | 'linkedIdentityId' | AccountMaxFieldsKeySpecifier)[]; -export type AccountMaxFieldsFieldPolicy = { +export interface AccountMaxFieldsFieldPolicy { id?: FieldPolicy<any> | FieldReadFunction<any>, linkedIdentityId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type AccountMinFieldsKeySpecifier = ('id' | 'linkedIdentityId' | AccountMinFieldsKeySpecifier)[]; -export type AccountMinFieldsFieldPolicy = { +export interface AccountMinFieldsFieldPolicy { id?: FieldPolicy<any> | FieldReadFunction<any>, linkedIdentityId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type BlockKeySpecifier = ('calls' | 'callsAggregate' | 'callsCount' | 'calls_connection' | 'events' | 'eventsAggregate' | 'eventsCount' | 'events_connection' | 'extrinsics' | 'extrinsicsAggregate' | 'extrinsicsCount' | 'extrinsics_connection' | 'extrinsicsicRoot' | 'hash' | 'height' | 'id' | 'implName' | 'implVersion' | 'parentHash' | 'specName' | 'specVersion' | 'stateRoot' | 'timestamp' | 'validator' | BlockKeySpecifier)[]; -export type BlockFieldPolicy = { +export interface BlockFieldPolicy { calls?: FieldPolicy<any> | FieldReadFunction<any>, callsAggregate?: FieldPolicy<any> | FieldReadFunction<any>, callsCount?: FieldPolicy<any> | FieldReadFunction<any>, @@ -74,19 +72,19 @@ export type BlockFieldPolicy = { stateRoot?: FieldPolicy<any> | FieldReadFunction<any>, timestamp?: FieldPolicy<any> | FieldReadFunction<any>, validator?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type BlockConnectionKeySpecifier = ('edges' | 'pageInfo' | BlockConnectionKeySpecifier)[]; -export type BlockConnectionFieldPolicy = { +export interface BlockConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type BlockEdgeKeySpecifier = ('cursor' | 'node' | BlockEdgeKeySpecifier)[]; -export type BlockEdgeFieldPolicy = { +export interface BlockEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CallKeySpecifier = ('address' | 'args' | 'argsStr' | 'block' | 'blockId' | 'error' | 'events' | 'eventsAggregate' | 'events_connection' | 'extrinsic' | 'extrinsicId' | 'id' | 'name' | 'pallet' | 'parent' | 'parentId' | 'subcalls' | 'subcallsAggregate' | 'subcalls_connection' | 'success' | CallKeySpecifier)[]; -export type CallFieldPolicy = { +export interface CallFieldPolicy { address?: FieldPolicy<any> | FieldReadFunction<any>, args?: FieldPolicy<any> | FieldReadFunction<any>, argsStr?: FieldPolicy<any> | FieldReadFunction<any>, @@ -107,30 +105,30 @@ export type CallFieldPolicy = { subcallsAggregate?: FieldPolicy<any> | FieldReadFunction<any>, subcalls_connection?: FieldPolicy<any> | FieldReadFunction<any>, success?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CallAggregateKeySpecifier = ('aggregate' | 'nodes' | CallAggregateKeySpecifier)[]; -export type CallAggregateFieldPolicy = { +export interface CallAggregateFieldPolicy { aggregate?: FieldPolicy<any> | FieldReadFunction<any>, nodes?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CallAggregateFieldsKeySpecifier = ('count' | 'max' | 'min' | CallAggregateFieldsKeySpecifier)[]; -export type CallAggregateFieldsFieldPolicy = { +export interface CallAggregateFieldsFieldPolicy { count?: FieldPolicy<any> | FieldReadFunction<any>, max?: FieldPolicy<any> | FieldReadFunction<any>, min?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CallConnectionKeySpecifier = ('edges' | 'pageInfo' | CallConnectionKeySpecifier)[]; -export type CallConnectionFieldPolicy = { +export interface CallConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CallEdgeKeySpecifier = ('cursor' | 'node' | CallEdgeKeySpecifier)[]; -export type CallEdgeFieldPolicy = { +export interface CallEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CallMaxFieldsKeySpecifier = ('address' | 'argsStr' | 'blockId' | 'extrinsicId' | 'id' | 'name' | 'pallet' | 'parentId' | CallMaxFieldsKeySpecifier)[]; -export type CallMaxFieldsFieldPolicy = { +export interface CallMaxFieldsFieldPolicy { address?: FieldPolicy<any> | FieldReadFunction<any>, argsStr?: FieldPolicy<any> | FieldReadFunction<any>, blockId?: FieldPolicy<any> | FieldReadFunction<any>, @@ -139,9 +137,9 @@ export type CallMaxFieldsFieldPolicy = { name?: FieldPolicy<any> | FieldReadFunction<any>, pallet?: FieldPolicy<any> | FieldReadFunction<any>, parentId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CallMinFieldsKeySpecifier = ('address' | 'argsStr' | 'blockId' | 'extrinsicId' | 'id' | 'name' | 'pallet' | 'parentId' | CallMinFieldsKeySpecifier)[]; -export type CallMinFieldsFieldPolicy = { +export interface CallMinFieldsFieldPolicy { address?: FieldPolicy<any> | FieldReadFunction<any>, argsStr?: FieldPolicy<any> | FieldReadFunction<any>, blockId?: FieldPolicy<any> | FieldReadFunction<any>, @@ -150,9 +148,9 @@ export type CallMinFieldsFieldPolicy = { name?: FieldPolicy<any> | FieldReadFunction<any>, pallet?: FieldPolicy<any> | FieldReadFunction<any>, parentId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertKeySpecifier = ('certHistory' | 'certHistoryAggregate' | 'certHistory_connection' | 'createdIn' | 'createdInId' | 'createdOn' | 'expireOn' | 'id' | 'isActive' | 'issuer' | 'issuerId' | 'receiver' | 'receiverId' | 'updatedIn' | 'updatedInId' | 'updatedOn' | CertKeySpecifier)[]; -export type CertFieldPolicy = { +export interface CertFieldPolicy { certHistory?: FieldPolicy<any> | FieldReadFunction<any>, certHistoryAggregate?: FieldPolicy<any> | FieldReadFunction<any>, certHistory_connection?: FieldPolicy<any> | FieldReadFunction<any>, @@ -169,14 +167,14 @@ export type CertFieldPolicy = { updatedIn?: FieldPolicy<any> | FieldReadFunction<any>, updatedInId?: FieldPolicy<any> | FieldReadFunction<any>, updatedOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertAggregateKeySpecifier = ('aggregate' | 'nodes' | CertAggregateKeySpecifier)[]; -export type CertAggregateFieldPolicy = { +export interface CertAggregateFieldPolicy { aggregate?: FieldPolicy<any> | FieldReadFunction<any>, nodes?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertAggregateFieldsKeySpecifier = ('avg' | 'count' | 'max' | 'min' | 'stddev' | 'stddevPop' | 'stddevSamp' | 'sum' | 'varPop' | 'varSamp' | 'variance' | CertAggregateFieldsKeySpecifier)[]; -export type CertAggregateFieldsFieldPolicy = { +export interface CertAggregateFieldsFieldPolicy { avg?: FieldPolicy<any> | FieldReadFunction<any>, count?: FieldPolicy<any> | FieldReadFunction<any>, max?: FieldPolicy<any> | FieldReadFunction<any>, @@ -188,25 +186,25 @@ export type CertAggregateFieldsFieldPolicy = { varPop?: FieldPolicy<any> | FieldReadFunction<any>, varSamp?: FieldPolicy<any> | FieldReadFunction<any>, variance?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertAvgFieldsKeySpecifier = ('createdOn' | 'expireOn' | 'updatedOn' | CertAvgFieldsKeySpecifier)[]; -export type CertAvgFieldsFieldPolicy = { +export interface CertAvgFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any>, expireOn?: FieldPolicy<any> | FieldReadFunction<any>, updatedOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertConnectionKeySpecifier = ('edges' | 'pageInfo' | CertConnectionKeySpecifier)[]; -export type CertConnectionFieldPolicy = { +export interface CertConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEdgeKeySpecifier = ('cursor' | 'node' | CertEdgeKeySpecifier)[]; -export type CertEdgeFieldPolicy = { +export interface CertEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventKeySpecifier = ('blockNumber' | 'cert' | 'certId' | 'event' | 'eventId' | 'eventType' | 'id' | CertEventKeySpecifier)[]; -export type CertEventFieldPolicy = { +export interface CertEventFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, cert?: FieldPolicy<any> | FieldReadFunction<any>, certId?: FieldPolicy<any> | FieldReadFunction<any>, @@ -214,14 +212,14 @@ export type CertEventFieldPolicy = { eventId?: FieldPolicy<any> | FieldReadFunction<any>, eventType?: FieldPolicy<any> | FieldReadFunction<any>, id?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventAggregateKeySpecifier = ('aggregate' | 'nodes' | CertEventAggregateKeySpecifier)[]; -export type CertEventAggregateFieldPolicy = { +export interface CertEventAggregateFieldPolicy { aggregate?: FieldPolicy<any> | FieldReadFunction<any>, nodes?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventAggregateFieldsKeySpecifier = ('avg' | 'count' | 'max' | 'min' | 'stddev' | 'stddevPop' | 'stddevSamp' | 'sum' | 'varPop' | 'varSamp' | 'variance' | CertEventAggregateFieldsKeySpecifier)[]; -export type CertEventAggregateFieldsFieldPolicy = { +export interface CertEventAggregateFieldsFieldPolicy { avg?: FieldPolicy<any> | FieldReadFunction<any>, count?: FieldPolicy<any> | FieldReadFunction<any>, max?: FieldPolicy<any> | FieldReadFunction<any>, @@ -233,65 +231,65 @@ export type CertEventAggregateFieldsFieldPolicy = { varPop?: FieldPolicy<any> | FieldReadFunction<any>, varSamp?: FieldPolicy<any> | FieldReadFunction<any>, variance?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventAvgFieldsKeySpecifier = ('blockNumber' | CertEventAvgFieldsKeySpecifier)[]; -export type CertEventAvgFieldsFieldPolicy = { +export interface CertEventAvgFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventConnectionKeySpecifier = ('edges' | 'pageInfo' | CertEventConnectionKeySpecifier)[]; -export type CertEventConnectionFieldPolicy = { +export interface CertEventConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventEdgeKeySpecifier = ('cursor' | 'node' | CertEventEdgeKeySpecifier)[]; -export type CertEventEdgeFieldPolicy = { +export interface CertEventEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventMaxFieldsKeySpecifier = ('blockNumber' | 'certId' | 'eventId' | 'id' | CertEventMaxFieldsKeySpecifier)[]; -export type CertEventMaxFieldsFieldPolicy = { +export interface CertEventMaxFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, certId?: FieldPolicy<any> | FieldReadFunction<any>, eventId?: FieldPolicy<any> | FieldReadFunction<any>, id?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventMinFieldsKeySpecifier = ('blockNumber' | 'certId' | 'eventId' | 'id' | CertEventMinFieldsKeySpecifier)[]; -export type CertEventMinFieldsFieldPolicy = { +export interface CertEventMinFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, certId?: FieldPolicy<any> | FieldReadFunction<any>, eventId?: FieldPolicy<any> | FieldReadFunction<any>, id?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventStddevFieldsKeySpecifier = ('blockNumber' | CertEventStddevFieldsKeySpecifier)[]; -export type CertEventStddevFieldsFieldPolicy = { +export interface CertEventStddevFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventStddevPopFieldsKeySpecifier = ('blockNumber' | CertEventStddevPopFieldsKeySpecifier)[]; -export type CertEventStddevPopFieldsFieldPolicy = { +export interface CertEventStddevPopFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventStddevSampFieldsKeySpecifier = ('blockNumber' | CertEventStddevSampFieldsKeySpecifier)[]; -export type CertEventStddevSampFieldsFieldPolicy = { +export interface CertEventStddevSampFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventSumFieldsKeySpecifier = ('blockNumber' | CertEventSumFieldsKeySpecifier)[]; -export type CertEventSumFieldsFieldPolicy = { +export interface CertEventSumFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventVarPopFieldsKeySpecifier = ('blockNumber' | CertEventVarPopFieldsKeySpecifier)[]; -export type CertEventVarPopFieldsFieldPolicy = { +export interface CertEventVarPopFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventVarSampFieldsKeySpecifier = ('blockNumber' | CertEventVarSampFieldsKeySpecifier)[]; -export type CertEventVarSampFieldsFieldPolicy = { +export interface CertEventVarSampFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertEventVarianceFieldsKeySpecifier = ('blockNumber' | CertEventVarianceFieldsKeySpecifier)[]; -export type CertEventVarianceFieldsFieldPolicy = { +export interface CertEventVarianceFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertMaxFieldsKeySpecifier = ('createdInId' | 'createdOn' | 'expireOn' | 'id' | 'issuerId' | 'receiverId' | 'updatedInId' | 'updatedOn' | CertMaxFieldsKeySpecifier)[]; -export type CertMaxFieldsFieldPolicy = { +export interface CertMaxFieldsFieldPolicy { createdInId?: FieldPolicy<any> | FieldReadFunction<any>, createdOn?: FieldPolicy<any> | FieldReadFunction<any>, expireOn?: FieldPolicy<any> | FieldReadFunction<any>, @@ -300,9 +298,9 @@ export type CertMaxFieldsFieldPolicy = { receiverId?: FieldPolicy<any> | FieldReadFunction<any>, updatedInId?: FieldPolicy<any> | FieldReadFunction<any>, updatedOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertMinFieldsKeySpecifier = ('createdInId' | 'createdOn' | 'expireOn' | 'id' | 'issuerId' | 'receiverId' | 'updatedInId' | 'updatedOn' | CertMinFieldsKeySpecifier)[]; -export type CertMinFieldsFieldPolicy = { +export interface CertMinFieldsFieldPolicy { createdInId?: FieldPolicy<any> | FieldReadFunction<any>, createdOn?: FieldPolicy<any> | FieldReadFunction<any>, expireOn?: FieldPolicy<any> | FieldReadFunction<any>, @@ -311,51 +309,51 @@ export type CertMinFieldsFieldPolicy = { receiverId?: FieldPolicy<any> | FieldReadFunction<any>, updatedInId?: FieldPolicy<any> | FieldReadFunction<any>, updatedOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertStddevFieldsKeySpecifier = ('createdOn' | 'expireOn' | 'updatedOn' | CertStddevFieldsKeySpecifier)[]; -export type CertStddevFieldsFieldPolicy = { +export interface CertStddevFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any>, expireOn?: FieldPolicy<any> | FieldReadFunction<any>, updatedOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertStddevPopFieldsKeySpecifier = ('createdOn' | 'expireOn' | 'updatedOn' | CertStddevPopFieldsKeySpecifier)[]; -export type CertStddevPopFieldsFieldPolicy = { +export interface CertStddevPopFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any>, expireOn?: FieldPolicy<any> | FieldReadFunction<any>, updatedOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertStddevSampFieldsKeySpecifier = ('createdOn' | 'expireOn' | 'updatedOn' | CertStddevSampFieldsKeySpecifier)[]; -export type CertStddevSampFieldsFieldPolicy = { +export interface CertStddevSampFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any>, expireOn?: FieldPolicy<any> | FieldReadFunction<any>, updatedOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertSumFieldsKeySpecifier = ('createdOn' | 'expireOn' | 'updatedOn' | CertSumFieldsKeySpecifier)[]; -export type CertSumFieldsFieldPolicy = { +export interface CertSumFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any>, expireOn?: FieldPolicy<any> | FieldReadFunction<any>, updatedOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertVarPopFieldsKeySpecifier = ('createdOn' | 'expireOn' | 'updatedOn' | CertVarPopFieldsKeySpecifier)[]; -export type CertVarPopFieldsFieldPolicy = { +export interface CertVarPopFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any>, expireOn?: FieldPolicy<any> | FieldReadFunction<any>, updatedOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertVarSampFieldsKeySpecifier = ('createdOn' | 'expireOn' | 'updatedOn' | CertVarSampFieldsKeySpecifier)[]; -export type CertVarSampFieldsFieldPolicy = { +export interface CertVarSampFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any>, expireOn?: FieldPolicy<any> | FieldReadFunction<any>, updatedOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type CertVarianceFieldsKeySpecifier = ('createdOn' | 'expireOn' | 'updatedOn' | CertVarianceFieldsKeySpecifier)[]; -export type CertVarianceFieldsFieldPolicy = { +export interface CertVarianceFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any>, expireOn?: FieldPolicy<any> | FieldReadFunction<any>, updatedOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyKeySpecifier = ('blockNumber' | 'id' | 'identity' | 'identityId' | 'next' | 'nextId' | 'previous' | 'previousId' | ChangeOwnerKeyKeySpecifier)[]; -export type ChangeOwnerKeyFieldPolicy = { +export interface ChangeOwnerKeyFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, id?: FieldPolicy<any> | FieldReadFunction<any>, identity?: FieldPolicy<any> | FieldReadFunction<any>, @@ -364,14 +362,14 @@ export type ChangeOwnerKeyFieldPolicy = { nextId?: FieldPolicy<any> | FieldReadFunction<any>, previous?: FieldPolicy<any> | FieldReadFunction<any>, previousId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyAggregateKeySpecifier = ('aggregate' | 'nodes' | ChangeOwnerKeyAggregateKeySpecifier)[]; -export type ChangeOwnerKeyAggregateFieldPolicy = { +export interface ChangeOwnerKeyAggregateFieldPolicy { aggregate?: FieldPolicy<any> | FieldReadFunction<any>, nodes?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyAggregateFieldsKeySpecifier = ('avg' | 'count' | 'max' | 'min' | 'stddev' | 'stddevPop' | 'stddevSamp' | 'sum' | 'varPop' | 'varSamp' | 'variance' | ChangeOwnerKeyAggregateFieldsKeySpecifier)[]; -export type ChangeOwnerKeyAggregateFieldsFieldPolicy = { +export interface ChangeOwnerKeyAggregateFieldsFieldPolicy { avg?: FieldPolicy<any> | FieldReadFunction<any>, count?: FieldPolicy<any> | FieldReadFunction<any>, max?: FieldPolicy<any> | FieldReadFunction<any>, @@ -383,67 +381,67 @@ export type ChangeOwnerKeyAggregateFieldsFieldPolicy = { varPop?: FieldPolicy<any> | FieldReadFunction<any>, varSamp?: FieldPolicy<any> | FieldReadFunction<any>, variance?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyAvgFieldsKeySpecifier = ('blockNumber' | ChangeOwnerKeyAvgFieldsKeySpecifier)[]; -export type ChangeOwnerKeyAvgFieldsFieldPolicy = { +export interface ChangeOwnerKeyAvgFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyConnectionKeySpecifier = ('edges' | 'pageInfo' | ChangeOwnerKeyConnectionKeySpecifier)[]; -export type ChangeOwnerKeyConnectionFieldPolicy = { +export interface ChangeOwnerKeyConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyEdgeKeySpecifier = ('cursor' | 'node' | ChangeOwnerKeyEdgeKeySpecifier)[]; -export type ChangeOwnerKeyEdgeFieldPolicy = { +export interface ChangeOwnerKeyEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyMaxFieldsKeySpecifier = ('blockNumber' | 'id' | 'identityId' | 'nextId' | 'previousId' | ChangeOwnerKeyMaxFieldsKeySpecifier)[]; -export type ChangeOwnerKeyMaxFieldsFieldPolicy = { +export interface ChangeOwnerKeyMaxFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, id?: FieldPolicy<any> | FieldReadFunction<any>, identityId?: FieldPolicy<any> | FieldReadFunction<any>, nextId?: FieldPolicy<any> | FieldReadFunction<any>, previousId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyMinFieldsKeySpecifier = ('blockNumber' | 'id' | 'identityId' | 'nextId' | 'previousId' | ChangeOwnerKeyMinFieldsKeySpecifier)[]; -export type ChangeOwnerKeyMinFieldsFieldPolicy = { +export interface ChangeOwnerKeyMinFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, id?: FieldPolicy<any> | FieldReadFunction<any>, identityId?: FieldPolicy<any> | FieldReadFunction<any>, nextId?: FieldPolicy<any> | FieldReadFunction<any>, previousId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyStddevFieldsKeySpecifier = ('blockNumber' | ChangeOwnerKeyStddevFieldsKeySpecifier)[]; -export type ChangeOwnerKeyStddevFieldsFieldPolicy = { +export interface ChangeOwnerKeyStddevFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyStddevPopFieldsKeySpecifier = ('blockNumber' | ChangeOwnerKeyStddevPopFieldsKeySpecifier)[]; -export type ChangeOwnerKeyStddevPopFieldsFieldPolicy = { +export interface ChangeOwnerKeyStddevPopFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyStddevSampFieldsKeySpecifier = ('blockNumber' | ChangeOwnerKeyStddevSampFieldsKeySpecifier)[]; -export type ChangeOwnerKeyStddevSampFieldsFieldPolicy = { +export interface ChangeOwnerKeyStddevSampFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeySumFieldsKeySpecifier = ('blockNumber' | ChangeOwnerKeySumFieldsKeySpecifier)[]; -export type ChangeOwnerKeySumFieldsFieldPolicy = { +export interface ChangeOwnerKeySumFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyVarPopFieldsKeySpecifier = ('blockNumber' | ChangeOwnerKeyVarPopFieldsKeySpecifier)[]; -export type ChangeOwnerKeyVarPopFieldsFieldPolicy = { +export interface ChangeOwnerKeyVarPopFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyVarSampFieldsKeySpecifier = ('blockNumber' | ChangeOwnerKeyVarSampFieldsKeySpecifier)[]; -export type ChangeOwnerKeyVarSampFieldsFieldPolicy = { +export interface ChangeOwnerKeyVarSampFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ChangeOwnerKeyVarianceFieldsKeySpecifier = ('blockNumber' | ChangeOwnerKeyVarianceFieldsKeySpecifier)[]; -export type ChangeOwnerKeyVarianceFieldsFieldPolicy = { +export interface ChangeOwnerKeyVarianceFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventKeySpecifier = ('args' | 'argsStr' | 'block' | 'blockId' | 'call' | 'callId' | 'extrinsic' | 'extrinsicId' | 'id' | 'index' | 'name' | 'pallet' | 'phase' | EventKeySpecifier)[]; -export type EventFieldPolicy = { +export interface EventFieldPolicy { args?: FieldPolicy<any> | FieldReadFunction<any>, argsStr?: FieldPolicy<any> | FieldReadFunction<any>, block?: FieldPolicy<any> | FieldReadFunction<any>, @@ -457,14 +455,14 @@ export type EventFieldPolicy = { name?: FieldPolicy<any> | FieldReadFunction<any>, pallet?: FieldPolicy<any> | FieldReadFunction<any>, phase?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventAggregateKeySpecifier = ('aggregate' | 'nodes' | EventAggregateKeySpecifier)[]; -export type EventAggregateFieldPolicy = { +export interface EventAggregateFieldPolicy { aggregate?: FieldPolicy<any> | FieldReadFunction<any>, nodes?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventAggregateFieldsKeySpecifier = ('avg' | 'count' | 'max' | 'min' | 'stddev' | 'stddevPop' | 'stddevSamp' | 'sum' | 'varPop' | 'varSamp' | 'variance' | EventAggregateFieldsKeySpecifier)[]; -export type EventAggregateFieldsFieldPolicy = { +export interface EventAggregateFieldsFieldPolicy { avg?: FieldPolicy<any> | FieldReadFunction<any>, count?: FieldPolicy<any> | FieldReadFunction<any>, max?: FieldPolicy<any> | FieldReadFunction<any>, @@ -476,23 +474,23 @@ export type EventAggregateFieldsFieldPolicy = { varPop?: FieldPolicy<any> | FieldReadFunction<any>, varSamp?: FieldPolicy<any> | FieldReadFunction<any>, variance?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventAvgFieldsKeySpecifier = ('index' | EventAvgFieldsKeySpecifier)[]; -export type EventAvgFieldsFieldPolicy = { +export interface EventAvgFieldsFieldPolicy { index?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventConnectionKeySpecifier = ('edges' | 'pageInfo' | EventConnectionKeySpecifier)[]; -export type EventConnectionFieldPolicy = { +export interface EventConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventEdgeKeySpecifier = ('cursor' | 'node' | EventEdgeKeySpecifier)[]; -export type EventEdgeFieldPolicy = { +export interface EventEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventMaxFieldsKeySpecifier = ('argsStr' | 'blockId' | 'callId' | 'extrinsicId' | 'id' | 'index' | 'name' | 'pallet' | 'phase' | EventMaxFieldsKeySpecifier)[]; -export type EventMaxFieldsFieldPolicy = { +export interface EventMaxFieldsFieldPolicy { argsStr?: FieldPolicy<any> | FieldReadFunction<any>, blockId?: FieldPolicy<any> | FieldReadFunction<any>, callId?: FieldPolicy<any> | FieldReadFunction<any>, @@ -502,9 +500,9 @@ export type EventMaxFieldsFieldPolicy = { name?: FieldPolicy<any> | FieldReadFunction<any>, pallet?: FieldPolicy<any> | FieldReadFunction<any>, phase?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventMinFieldsKeySpecifier = ('argsStr' | 'blockId' | 'callId' | 'extrinsicId' | 'id' | 'index' | 'name' | 'pallet' | 'phase' | EventMinFieldsKeySpecifier)[]; -export type EventMinFieldsFieldPolicy = { +export interface EventMinFieldsFieldPolicy { argsStr?: FieldPolicy<any> | FieldReadFunction<any>, blockId?: FieldPolicy<any> | FieldReadFunction<any>, callId?: FieldPolicy<any> | FieldReadFunction<any>, @@ -514,37 +512,37 @@ export type EventMinFieldsFieldPolicy = { name?: FieldPolicy<any> | FieldReadFunction<any>, pallet?: FieldPolicy<any> | FieldReadFunction<any>, phase?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventStddevFieldsKeySpecifier = ('index' | EventStddevFieldsKeySpecifier)[]; -export type EventStddevFieldsFieldPolicy = { +export interface EventStddevFieldsFieldPolicy { index?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventStddevPopFieldsKeySpecifier = ('index' | EventStddevPopFieldsKeySpecifier)[]; -export type EventStddevPopFieldsFieldPolicy = { +export interface EventStddevPopFieldsFieldPolicy { index?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventStddevSampFieldsKeySpecifier = ('index' | EventStddevSampFieldsKeySpecifier)[]; -export type EventStddevSampFieldsFieldPolicy = { +export interface EventStddevSampFieldsFieldPolicy { index?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventSumFieldsKeySpecifier = ('index' | EventSumFieldsKeySpecifier)[]; -export type EventSumFieldsFieldPolicy = { +export interface EventSumFieldsFieldPolicy { index?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventVarPopFieldsKeySpecifier = ('index' | EventVarPopFieldsKeySpecifier)[]; -export type EventVarPopFieldsFieldPolicy = { +export interface EventVarPopFieldsFieldPolicy { index?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventVarSampFieldsKeySpecifier = ('index' | EventVarSampFieldsKeySpecifier)[]; -export type EventVarSampFieldsFieldPolicy = { +export interface EventVarSampFieldsFieldPolicy { index?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type EventVarianceFieldsKeySpecifier = ('index' | EventVarianceFieldsKeySpecifier)[]; -export type EventVarianceFieldsFieldPolicy = { +export interface EventVarianceFieldsFieldPolicy { index?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicKeySpecifier = ('block' | 'blockId' | 'call' | 'callId' | 'calls' | 'callsAggregate' | 'calls_connection' | 'error' | 'events' | 'eventsAggregate' | 'events_connection' | 'fee' | 'hash' | 'id' | 'index' | 'signature' | 'success' | 'tip' | 'version' | ExtrinsicKeySpecifier)[]; -export type ExtrinsicFieldPolicy = { +export interface ExtrinsicFieldPolicy { block?: FieldPolicy<any> | FieldReadFunction<any>, blockId?: FieldPolicy<any> | FieldReadFunction<any>, call?: FieldPolicy<any> | FieldReadFunction<any>, @@ -564,14 +562,14 @@ export type ExtrinsicFieldPolicy = { success?: FieldPolicy<any> | FieldReadFunction<any>, tip?: FieldPolicy<any> | FieldReadFunction<any>, version?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicAggregateKeySpecifier = ('aggregate' | 'nodes' | ExtrinsicAggregateKeySpecifier)[]; -export type ExtrinsicAggregateFieldPolicy = { +export interface ExtrinsicAggregateFieldPolicy { aggregate?: FieldPolicy<any> | FieldReadFunction<any>, nodes?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicAggregateFieldsKeySpecifier = ('avg' | 'count' | 'max' | 'min' | 'stddev' | 'stddevPop' | 'stddevSamp' | 'sum' | 'varPop' | 'varSamp' | 'variance' | ExtrinsicAggregateFieldsKeySpecifier)[]; -export type ExtrinsicAggregateFieldsFieldPolicy = { +export interface ExtrinsicAggregateFieldsFieldPolicy { avg?: FieldPolicy<any> | FieldReadFunction<any>, count?: FieldPolicy<any> | FieldReadFunction<any>, max?: FieldPolicy<any> | FieldReadFunction<any>, @@ -583,26 +581,26 @@ export type ExtrinsicAggregateFieldsFieldPolicy = { varPop?: FieldPolicy<any> | FieldReadFunction<any>, varSamp?: FieldPolicy<any> | FieldReadFunction<any>, variance?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicAvgFieldsKeySpecifier = ('fee' | 'index' | 'tip' | 'version' | ExtrinsicAvgFieldsKeySpecifier)[]; -export type ExtrinsicAvgFieldsFieldPolicy = { +export interface ExtrinsicAvgFieldsFieldPolicy { fee?: FieldPolicy<any> | FieldReadFunction<any>, index?: FieldPolicy<any> | FieldReadFunction<any>, tip?: FieldPolicy<any> | FieldReadFunction<any>, version?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicConnectionKeySpecifier = ('edges' | 'pageInfo' | ExtrinsicConnectionKeySpecifier)[]; -export type ExtrinsicConnectionFieldPolicy = { +export interface ExtrinsicConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicEdgeKeySpecifier = ('cursor' | 'node' | ExtrinsicEdgeKeySpecifier)[]; -export type ExtrinsicEdgeFieldPolicy = { +export interface ExtrinsicEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicMaxFieldsKeySpecifier = ('blockId' | 'callId' | 'fee' | 'id' | 'index' | 'tip' | 'version' | ExtrinsicMaxFieldsKeySpecifier)[]; -export type ExtrinsicMaxFieldsFieldPolicy = { +export interface ExtrinsicMaxFieldsFieldPolicy { blockId?: FieldPolicy<any> | FieldReadFunction<any>, callId?: FieldPolicy<any> | FieldReadFunction<any>, fee?: FieldPolicy<any> | FieldReadFunction<any>, @@ -610,9 +608,9 @@ export type ExtrinsicMaxFieldsFieldPolicy = { index?: FieldPolicy<any> | FieldReadFunction<any>, tip?: FieldPolicy<any> | FieldReadFunction<any>, version?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicMinFieldsKeySpecifier = ('blockId' | 'callId' | 'fee' | 'id' | 'index' | 'tip' | 'version' | ExtrinsicMinFieldsKeySpecifier)[]; -export type ExtrinsicMinFieldsFieldPolicy = { +export interface ExtrinsicMinFieldsFieldPolicy { blockId?: FieldPolicy<any> | FieldReadFunction<any>, callId?: FieldPolicy<any> | FieldReadFunction<any>, fee?: FieldPolicy<any> | FieldReadFunction<any>, @@ -620,58 +618,58 @@ export type ExtrinsicMinFieldsFieldPolicy = { index?: FieldPolicy<any> | FieldReadFunction<any>, tip?: FieldPolicy<any> | FieldReadFunction<any>, version?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicStddevFieldsKeySpecifier = ('fee' | 'index' | 'tip' | 'version' | ExtrinsicStddevFieldsKeySpecifier)[]; -export type ExtrinsicStddevFieldsFieldPolicy = { +export interface ExtrinsicStddevFieldsFieldPolicy { fee?: FieldPolicy<any> | FieldReadFunction<any>, index?: FieldPolicy<any> | FieldReadFunction<any>, tip?: FieldPolicy<any> | FieldReadFunction<any>, version?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicStddevPopFieldsKeySpecifier = ('fee' | 'index' | 'tip' | 'version' | ExtrinsicStddevPopFieldsKeySpecifier)[]; -export type ExtrinsicStddevPopFieldsFieldPolicy = { +export interface ExtrinsicStddevPopFieldsFieldPolicy { fee?: FieldPolicy<any> | FieldReadFunction<any>, index?: FieldPolicy<any> | FieldReadFunction<any>, tip?: FieldPolicy<any> | FieldReadFunction<any>, version?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicStddevSampFieldsKeySpecifier = ('fee' | 'index' | 'tip' | 'version' | ExtrinsicStddevSampFieldsKeySpecifier)[]; -export type ExtrinsicStddevSampFieldsFieldPolicy = { +export interface ExtrinsicStddevSampFieldsFieldPolicy { fee?: FieldPolicy<any> | FieldReadFunction<any>, index?: FieldPolicy<any> | FieldReadFunction<any>, tip?: FieldPolicy<any> | FieldReadFunction<any>, version?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicSumFieldsKeySpecifier = ('fee' | 'index' | 'tip' | 'version' | ExtrinsicSumFieldsKeySpecifier)[]; -export type ExtrinsicSumFieldsFieldPolicy = { +export interface ExtrinsicSumFieldsFieldPolicy { fee?: FieldPolicy<any> | FieldReadFunction<any>, index?: FieldPolicy<any> | FieldReadFunction<any>, tip?: FieldPolicy<any> | FieldReadFunction<any>, version?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicVarPopFieldsKeySpecifier = ('fee' | 'index' | 'tip' | 'version' | ExtrinsicVarPopFieldsKeySpecifier)[]; -export type ExtrinsicVarPopFieldsFieldPolicy = { +export interface ExtrinsicVarPopFieldsFieldPolicy { fee?: FieldPolicy<any> | FieldReadFunction<any>, index?: FieldPolicy<any> | FieldReadFunction<any>, tip?: FieldPolicy<any> | FieldReadFunction<any>, version?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicVarSampFieldsKeySpecifier = ('fee' | 'index' | 'tip' | 'version' | ExtrinsicVarSampFieldsKeySpecifier)[]; -export type ExtrinsicVarSampFieldsFieldPolicy = { +export interface ExtrinsicVarSampFieldsFieldPolicy { fee?: FieldPolicy<any> | FieldReadFunction<any>, index?: FieldPolicy<any> | FieldReadFunction<any>, tip?: FieldPolicy<any> | FieldReadFunction<any>, version?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ExtrinsicVarianceFieldsKeySpecifier = ('fee' | 'index' | 'tip' | 'version' | ExtrinsicVarianceFieldsKeySpecifier)[]; -export type ExtrinsicVarianceFieldsFieldPolicy = { +export interface ExtrinsicVarianceFieldsFieldPolicy { fee?: FieldPolicy<any> | FieldReadFunction<any>, index?: FieldPolicy<any> | FieldReadFunction<any>, tip?: FieldPolicy<any> | FieldReadFunction<any>, version?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type IdentityKeySpecifier = ('account' | 'accountId' | 'certIssued' | 'certIssuedAggregate' | 'certIssued_connection' | 'certReceived' | 'certReceivedAggregate' | 'certReceived_connection' | 'createdIn' | 'createdInId' | 'createdOn' | 'expireOn' | 'id' | 'index' | 'isMember' | 'lastChangeOn' | 'linkedAccount' | 'linkedAccountAggregate' | 'linkedAccount_connection' | 'membershipHistory' | 'membershipHistoryAggregate' | 'membershipHistory_connection' | 'name' | 'ownerKeyChange' | 'ownerKeyChangeAggregate' | 'ownerKeyChange_connection' | 'smithCertIssued' | 'smithCertIssuedAggregate' | 'smithCertIssued_connection' | 'smithCertReceived' | 'smithCertReceivedAggregate' | 'smithCertReceived_connection' | 'smithStatus' | 'status' | 'udHistory' | IdentityKeySpecifier)[]; -export type IdentityFieldPolicy = { +export interface IdentityFieldPolicy { account?: FieldPolicy<any> | FieldReadFunction<any>, accountId?: FieldPolicy<any> | FieldReadFunction<any>, certIssued?: FieldPolicy<any> | FieldReadFunction<any>, @@ -707,36 +705,36 @@ export type IdentityFieldPolicy = { smithStatus?: FieldPolicy<any> | FieldReadFunction<any>, status?: FieldPolicy<any> | FieldReadFunction<any>, udHistory?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type IdentityConnectionKeySpecifier = ('edges' | 'pageInfo' | IdentityConnectionKeySpecifier)[]; -export type IdentityConnectionFieldPolicy = { +export interface IdentityConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type IdentityEdgeKeySpecifier = ('cursor' | 'node' | IdentityEdgeKeySpecifier)[]; -export type IdentityEdgeFieldPolicy = { +export interface IdentityEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ItemsCounterKeySpecifier = ('id' | 'level' | 'total' | 'type' | ItemsCounterKeySpecifier)[]; -export type ItemsCounterFieldPolicy = { +export interface ItemsCounterFieldPolicy { id?: FieldPolicy<any> | FieldReadFunction<any>, level?: FieldPolicy<any> | FieldReadFunction<any>, total?: FieldPolicy<any> | FieldReadFunction<any>, type?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ItemsCounterConnectionKeySpecifier = ('edges' | 'pageInfo' | ItemsCounterConnectionKeySpecifier)[]; -export type ItemsCounterConnectionFieldPolicy = { +export interface ItemsCounterConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type ItemsCounterEdgeKeySpecifier = ('cursor' | 'node' | ItemsCounterEdgeKeySpecifier)[]; -export type ItemsCounterEdgeFieldPolicy = { +export interface ItemsCounterEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventKeySpecifier = ('blockNumber' | 'event' | 'eventId' | 'eventType' | 'id' | 'identity' | 'identityId' | MembershipEventKeySpecifier)[]; -export type MembershipEventFieldPolicy = { +export interface MembershipEventFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, event?: FieldPolicy<any> | FieldReadFunction<any>, eventId?: FieldPolicy<any> | FieldReadFunction<any>, @@ -744,14 +742,14 @@ export type MembershipEventFieldPolicy = { id?: FieldPolicy<any> | FieldReadFunction<any>, identity?: FieldPolicy<any> | FieldReadFunction<any>, identityId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventAggregateKeySpecifier = ('aggregate' | 'nodes' | MembershipEventAggregateKeySpecifier)[]; -export type MembershipEventAggregateFieldPolicy = { +export interface MembershipEventAggregateFieldPolicy { aggregate?: FieldPolicy<any> | FieldReadFunction<any>, nodes?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventAggregateFieldsKeySpecifier = ('avg' | 'count' | 'max' | 'min' | 'stddev' | 'stddevPop' | 'stddevSamp' | 'sum' | 'varPop' | 'varSamp' | 'variance' | MembershipEventAggregateFieldsKeySpecifier)[]; -export type MembershipEventAggregateFieldsFieldPolicy = { +export interface MembershipEventAggregateFieldsFieldPolicy { avg?: FieldPolicy<any> | FieldReadFunction<any>, count?: FieldPolicy<any> | FieldReadFunction<any>, max?: FieldPolicy<any> | FieldReadFunction<any>, @@ -763,90 +761,90 @@ export type MembershipEventAggregateFieldsFieldPolicy = { varPop?: FieldPolicy<any> | FieldReadFunction<any>, varSamp?: FieldPolicy<any> | FieldReadFunction<any>, variance?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventAvgFieldsKeySpecifier = ('blockNumber' | MembershipEventAvgFieldsKeySpecifier)[]; -export type MembershipEventAvgFieldsFieldPolicy = { +export interface MembershipEventAvgFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventConnectionKeySpecifier = ('edges' | 'pageInfo' | MembershipEventConnectionKeySpecifier)[]; -export type MembershipEventConnectionFieldPolicy = { +export interface MembershipEventConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventEdgeKeySpecifier = ('cursor' | 'node' | MembershipEventEdgeKeySpecifier)[]; -export type MembershipEventEdgeFieldPolicy = { +export interface MembershipEventEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventMaxFieldsKeySpecifier = ('blockNumber' | 'eventId' | 'id' | 'identityId' | MembershipEventMaxFieldsKeySpecifier)[]; -export type MembershipEventMaxFieldsFieldPolicy = { +export interface MembershipEventMaxFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, eventId?: FieldPolicy<any> | FieldReadFunction<any>, id?: FieldPolicy<any> | FieldReadFunction<any>, identityId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventMinFieldsKeySpecifier = ('blockNumber' | 'eventId' | 'id' | 'identityId' | MembershipEventMinFieldsKeySpecifier)[]; -export type MembershipEventMinFieldsFieldPolicy = { +export interface MembershipEventMinFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, eventId?: FieldPolicy<any> | FieldReadFunction<any>, id?: FieldPolicy<any> | FieldReadFunction<any>, identityId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventStddevFieldsKeySpecifier = ('blockNumber' | MembershipEventStddevFieldsKeySpecifier)[]; -export type MembershipEventStddevFieldsFieldPolicy = { +export interface MembershipEventStddevFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventStddevPopFieldsKeySpecifier = ('blockNumber' | MembershipEventStddevPopFieldsKeySpecifier)[]; -export type MembershipEventStddevPopFieldsFieldPolicy = { +export interface MembershipEventStddevPopFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventStddevSampFieldsKeySpecifier = ('blockNumber' | MembershipEventStddevSampFieldsKeySpecifier)[]; -export type MembershipEventStddevSampFieldsFieldPolicy = { +export interface MembershipEventStddevSampFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventSumFieldsKeySpecifier = ('blockNumber' | MembershipEventSumFieldsKeySpecifier)[]; -export type MembershipEventSumFieldsFieldPolicy = { +export interface MembershipEventSumFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventVarPopFieldsKeySpecifier = ('blockNumber' | MembershipEventVarPopFieldsKeySpecifier)[]; -export type MembershipEventVarPopFieldsFieldPolicy = { +export interface MembershipEventVarPopFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventVarSampFieldsKeySpecifier = ('blockNumber' | MembershipEventVarSampFieldsKeySpecifier)[]; -export type MembershipEventVarSampFieldsFieldPolicy = { +export interface MembershipEventVarSampFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type MembershipEventVarianceFieldsKeySpecifier = ('blockNumber' | MembershipEventVarianceFieldsKeySpecifier)[]; -export type MembershipEventVarianceFieldsFieldPolicy = { +export interface MembershipEventVarianceFieldsFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type NodeKeySpecifier = ('id' | NodeKeySpecifier)[]; -export type NodeFieldPolicy = { +export interface NodeFieldPolicy { id?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type PageInfoKeySpecifier = ('endCursor' | 'hasNextPage' | 'hasPreviousPage' | 'startCursor' | PageInfoKeySpecifier)[]; -export type PageInfoFieldPolicy = { +export interface PageInfoFieldPolicy { endCursor?: FieldPolicy<any> | FieldReadFunction<any>, hasNextPage?: FieldPolicy<any> | FieldReadFunction<any>, hasPreviousPage?: FieldPolicy<any> | FieldReadFunction<any>, startCursor?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertKeySpecifier = ('createdOn' | 'id' | 'issuer' | 'issuerId' | 'receiver' | 'receiverId' | SmithCertKeySpecifier)[]; -export type SmithCertFieldPolicy = { +export interface SmithCertFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any>, id?: FieldPolicy<any> | FieldReadFunction<any>, issuer?: FieldPolicy<any> | FieldReadFunction<any>, issuerId?: FieldPolicy<any> | FieldReadFunction<any>, receiver?: FieldPolicy<any> | FieldReadFunction<any>, receiverId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertAggregateKeySpecifier = ('aggregate' | 'nodes' | SmithCertAggregateKeySpecifier)[]; -export type SmithCertAggregateFieldPolicy = { +export interface SmithCertAggregateFieldPolicy { aggregate?: FieldPolicy<any> | FieldReadFunction<any>, nodes?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertAggregateFieldsKeySpecifier = ('avg' | 'count' | 'max' | 'min' | 'stddev' | 'stddevPop' | 'stddevSamp' | 'sum' | 'varPop' | 'varSamp' | 'variance' | SmithCertAggregateFieldsKeySpecifier)[]; -export type SmithCertAggregateFieldsFieldPolicy = { +export interface SmithCertAggregateFieldsFieldPolicy { avg?: FieldPolicy<any> | FieldReadFunction<any>, count?: FieldPolicy<any> | FieldReadFunction<any>, max?: FieldPolicy<any> | FieldReadFunction<any>, @@ -858,65 +856,65 @@ export type SmithCertAggregateFieldsFieldPolicy = { varPop?: FieldPolicy<any> | FieldReadFunction<any>, varSamp?: FieldPolicy<any> | FieldReadFunction<any>, variance?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertAvgFieldsKeySpecifier = ('createdOn' | SmithCertAvgFieldsKeySpecifier)[]; -export type SmithCertAvgFieldsFieldPolicy = { +export interface SmithCertAvgFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertConnectionKeySpecifier = ('edges' | 'pageInfo' | SmithCertConnectionKeySpecifier)[]; -export type SmithCertConnectionFieldPolicy = { +export interface SmithCertConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertEdgeKeySpecifier = ('cursor' | 'node' | SmithCertEdgeKeySpecifier)[]; -export type SmithCertEdgeFieldPolicy = { +export interface SmithCertEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertMaxFieldsKeySpecifier = ('createdOn' | 'id' | 'issuerId' | 'receiverId' | SmithCertMaxFieldsKeySpecifier)[]; -export type SmithCertMaxFieldsFieldPolicy = { +export interface SmithCertMaxFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any>, id?: FieldPolicy<any> | FieldReadFunction<any>, issuerId?: FieldPolicy<any> | FieldReadFunction<any>, receiverId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertMinFieldsKeySpecifier = ('createdOn' | 'id' | 'issuerId' | 'receiverId' | SmithCertMinFieldsKeySpecifier)[]; -export type SmithCertMinFieldsFieldPolicy = { +export interface SmithCertMinFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any>, id?: FieldPolicy<any> | FieldReadFunction<any>, issuerId?: FieldPolicy<any> | FieldReadFunction<any>, receiverId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertStddevFieldsKeySpecifier = ('createdOn' | SmithCertStddevFieldsKeySpecifier)[]; -export type SmithCertStddevFieldsFieldPolicy = { +export interface SmithCertStddevFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertStddevPopFieldsKeySpecifier = ('createdOn' | SmithCertStddevPopFieldsKeySpecifier)[]; -export type SmithCertStddevPopFieldsFieldPolicy = { +export interface SmithCertStddevPopFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertStddevSampFieldsKeySpecifier = ('createdOn' | SmithCertStddevSampFieldsKeySpecifier)[]; -export type SmithCertStddevSampFieldsFieldPolicy = { +export interface SmithCertStddevSampFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertSumFieldsKeySpecifier = ('createdOn' | SmithCertSumFieldsKeySpecifier)[]; -export type SmithCertSumFieldsFieldPolicy = { +export interface SmithCertSumFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertVarPopFieldsKeySpecifier = ('createdOn' | SmithCertVarPopFieldsKeySpecifier)[]; -export type SmithCertVarPopFieldsFieldPolicy = { +export interface SmithCertVarPopFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertVarSampFieldsKeySpecifier = ('createdOn' | SmithCertVarSampFieldsKeySpecifier)[]; -export type SmithCertVarSampFieldsFieldPolicy = { +export interface SmithCertVarSampFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type SmithCertVarianceFieldsKeySpecifier = ('createdOn' | SmithCertVarianceFieldsKeySpecifier)[]; -export type SmithCertVarianceFieldsFieldPolicy = { +export interface SmithCertVarianceFieldsFieldPolicy { createdOn?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferKeySpecifier = ('amount' | 'blockNumber' | 'comment' | 'from' | 'fromId' | 'id' | 'timestamp' | 'to' | 'toId' | TransferKeySpecifier)[]; -export type TransferFieldPolicy = { +export interface TransferFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, comment?: FieldPolicy<any> | FieldReadFunction<any>, @@ -926,14 +924,14 @@ export type TransferFieldPolicy = { timestamp?: FieldPolicy<any> | FieldReadFunction<any>, to?: FieldPolicy<any> | FieldReadFunction<any>, toId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferAggregateKeySpecifier = ('aggregate' | 'nodes' | TransferAggregateKeySpecifier)[]; -export type TransferAggregateFieldPolicy = { +export interface TransferAggregateFieldPolicy { aggregate?: FieldPolicy<any> | FieldReadFunction<any>, nodes?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferAggregateFieldsKeySpecifier = ('avg' | 'count' | 'max' | 'min' | 'stddev' | 'stddevPop' | 'stddevSamp' | 'sum' | 'varPop' | 'varSamp' | 'variance' | TransferAggregateFieldsKeySpecifier)[]; -export type TransferAggregateFieldsFieldPolicy = { +export interface TransferAggregateFieldsFieldPolicy { avg?: FieldPolicy<any> | FieldReadFunction<any>, count?: FieldPolicy<any> | FieldReadFunction<any>, max?: FieldPolicy<any> | FieldReadFunction<any>, @@ -945,24 +943,24 @@ export type TransferAggregateFieldsFieldPolicy = { varPop?: FieldPolicy<any> | FieldReadFunction<any>, varSamp?: FieldPolicy<any> | FieldReadFunction<any>, variance?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferAvgFieldsKeySpecifier = ('amount' | 'blockNumber' | TransferAvgFieldsKeySpecifier)[]; -export type TransferAvgFieldsFieldPolicy = { +export interface TransferAvgFieldsFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferConnectionKeySpecifier = ('edges' | 'pageInfo' | TransferConnectionKeySpecifier)[]; -export type TransferConnectionFieldPolicy = { +export interface TransferConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferEdgeKeySpecifier = ('cursor' | 'node' | TransferEdgeKeySpecifier)[]; -export type TransferEdgeFieldPolicy = { +export interface TransferEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferMaxFieldsKeySpecifier = ('amount' | 'blockNumber' | 'comment' | 'fromId' | 'id' | 'timestamp' | 'toId' | TransferMaxFieldsKeySpecifier)[]; -export type TransferMaxFieldsFieldPolicy = { +export interface TransferMaxFieldsFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, comment?: FieldPolicy<any> | FieldReadFunction<any>, @@ -970,9 +968,9 @@ export type TransferMaxFieldsFieldPolicy = { id?: FieldPolicy<any> | FieldReadFunction<any>, timestamp?: FieldPolicy<any> | FieldReadFunction<any>, toId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferMinFieldsKeySpecifier = ('amount' | 'blockNumber' | 'comment' | 'fromId' | 'id' | 'timestamp' | 'toId' | TransferMinFieldsKeySpecifier)[]; -export type TransferMinFieldsFieldPolicy = { +export interface TransferMinFieldsFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, comment?: FieldPolicy<any> | FieldReadFunction<any>, @@ -980,63 +978,63 @@ export type TransferMinFieldsFieldPolicy = { id?: FieldPolicy<any> | FieldReadFunction<any>, timestamp?: FieldPolicy<any> | FieldReadFunction<any>, toId?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferStddevFieldsKeySpecifier = ('amount' | 'blockNumber' | TransferStddevFieldsKeySpecifier)[]; -export type TransferStddevFieldsFieldPolicy = { +export interface TransferStddevFieldsFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferStddevPopFieldsKeySpecifier = ('amount' | 'blockNumber' | TransferStddevPopFieldsKeySpecifier)[]; -export type TransferStddevPopFieldsFieldPolicy = { +export interface TransferStddevPopFieldsFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferStddevSampFieldsKeySpecifier = ('amount' | 'blockNumber' | TransferStddevSampFieldsKeySpecifier)[]; -export type TransferStddevSampFieldsFieldPolicy = { +export interface TransferStddevSampFieldsFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferSumFieldsKeySpecifier = ('amount' | 'blockNumber' | TransferSumFieldsKeySpecifier)[]; -export type TransferSumFieldsFieldPolicy = { +export interface TransferSumFieldsFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferVarPopFieldsKeySpecifier = ('amount' | 'blockNumber' | TransferVarPopFieldsKeySpecifier)[]; -export type TransferVarPopFieldsFieldPolicy = { +export interface TransferVarPopFieldsFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferVarSampFieldsKeySpecifier = ('amount' | 'blockNumber' | TransferVarSampFieldsKeySpecifier)[]; -export type TransferVarSampFieldsFieldPolicy = { +export interface TransferVarSampFieldsFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type TransferVarianceFieldsKeySpecifier = ('amount' | 'blockNumber' | TransferVarianceFieldsKeySpecifier)[]; -export type TransferVarianceFieldsFieldPolicy = { +export interface TransferVarianceFieldsFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type UdHistoryKeySpecifier = ('amount' | 'blockNumber' | 'id' | 'identity' | 'identityId' | 'timestamp' | UdHistoryKeySpecifier)[]; -export type UdHistoryFieldPolicy = { +export interface UdHistoryFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, id?: FieldPolicy<any> | FieldReadFunction<any>, identity?: FieldPolicy<any> | FieldReadFunction<any>, identityId?: FieldPolicy<any> | FieldReadFunction<any>, timestamp?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type UdHistoryConnectionKeySpecifier = ('edges' | 'pageInfo' | UdHistoryConnectionKeySpecifier)[]; -export type UdHistoryConnectionFieldPolicy = { +export interface UdHistoryConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type UdHistoryEdgeKeySpecifier = ('cursor' | 'node' | UdHistoryEdgeKeySpecifier)[]; -export type UdHistoryEdgeFieldPolicy = { +export interface UdHistoryEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type UdReevalKeySpecifier = ('blockNumber' | 'event' | 'eventId' | 'id' | 'membersCount' | 'monetaryMass' | 'newUdAmount' | 'timestamp' | UdReevalKeySpecifier)[]; -export type UdReevalFieldPolicy = { +export interface UdReevalFieldPolicy { blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, event?: FieldPolicy<any> | FieldReadFunction<any>, eventId?: FieldPolicy<any> | FieldReadFunction<any>, @@ -1045,19 +1043,19 @@ export type UdReevalFieldPolicy = { monetaryMass?: FieldPolicy<any> | FieldReadFunction<any>, newUdAmount?: FieldPolicy<any> | FieldReadFunction<any>, timestamp?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type UdReevalConnectionKeySpecifier = ('edges' | 'pageInfo' | UdReevalConnectionKeySpecifier)[]; -export type UdReevalConnectionFieldPolicy = { +export interface UdReevalConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type UdReevalEdgeKeySpecifier = ('cursor' | 'node' | UdReevalEdgeKeySpecifier)[]; -export type UdReevalEdgeFieldPolicy = { +export interface UdReevalEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type UniversalDividendKeySpecifier = ('amount' | 'blockNumber' | 'event' | 'eventId' | 'id' | 'membersCount' | 'monetaryMass' | 'timestamp' | UniversalDividendKeySpecifier)[]; -export type UniversalDividendFieldPolicy = { +export interface UniversalDividendFieldPolicy { amount?: FieldPolicy<any> | FieldReadFunction<any>, blockNumber?: FieldPolicy<any> | FieldReadFunction<any>, event?: FieldPolicy<any> | FieldReadFunction<any>, @@ -1066,19 +1064,19 @@ export type UniversalDividendFieldPolicy = { membersCount?: FieldPolicy<any> | FieldReadFunction<any>, monetaryMass?: FieldPolicy<any> | FieldReadFunction<any>, timestamp?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type UniversalDividendConnectionKeySpecifier = ('edges' | 'pageInfo' | UniversalDividendConnectionKeySpecifier)[]; -export type UniversalDividendConnectionFieldPolicy = { +export interface UniversalDividendConnectionFieldPolicy { edges?: FieldPolicy<any> | FieldReadFunction<any>, pageInfo?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type UniversalDividendEdgeKeySpecifier = ('cursor' | 'node' | UniversalDividendEdgeKeySpecifier)[]; -export type UniversalDividendEdgeFieldPolicy = { +export interface UniversalDividendEdgeFieldPolicy { cursor?: FieldPolicy<any> | FieldReadFunction<any>, node?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type query_rootKeySpecifier = ('accountConnection' | 'blockConnection' | 'callConnection' | 'certConnection' | 'certEventConnection' | 'changeOwnerKeyConnection' | 'eventConnection' | 'extrinsicConnection' | 'getUdHistory_connection' | 'identityConnection' | 'itemsCounterConnection' | 'membershipEventConnection' | 'node' | 'smithCertConnection' | 'transferConnection' | 'udHistoryConnection' | 'udReevalConnection' | 'universalDividendConnection' | query_rootKeySpecifier)[]; -export type query_rootFieldPolicy = { +export interface query_rootFieldPolicy { accountConnection?: FieldPolicy<any> | FieldReadFunction<any>, blockConnection?: FieldPolicy<any> | FieldReadFunction<any>, callConnection?: FieldPolicy<any> | FieldReadFunction<any>, @@ -1097,9 +1095,9 @@ export type query_rootFieldPolicy = { udHistoryConnection?: FieldPolicy<any> | FieldReadFunction<any>, udReevalConnection?: FieldPolicy<any> | FieldReadFunction<any>, universalDividendConnection?: FieldPolicy<any> | FieldReadFunction<any> -}; +} export type subscription_rootKeySpecifier = ('accountConnection' | 'blockConnection' | 'callConnection' | 'certConnection' | 'certEventConnection' | 'changeOwnerKeyConnection' | 'eventConnection' | 'extrinsicConnection' | 'getUdHistory_connection' | 'identityConnection' | 'itemsCounterConnection' | 'membershipEventConnection' | 'node' | 'smithCertConnection' | 'transferConnection' | 'udHistoryConnection' | 'udReevalConnection' | 'universalDividendConnection' | subscription_rootKeySpecifier)[]; -export type subscription_rootFieldPolicy = { +export interface subscription_rootFieldPolicy { accountConnection?: FieldPolicy<any> | FieldReadFunction<any>, blockConnection?: FieldPolicy<any> | FieldReadFunction<any>, callConnection?: FieldPolicy<any> | FieldReadFunction<any>, @@ -1118,8 +1116,8 @@ export type subscription_rootFieldPolicy = { udHistoryConnection?: FieldPolicy<any> | FieldReadFunction<any>, udReevalConnection?: FieldPolicy<any> | FieldReadFunction<any>, universalDividendConnection?: FieldPolicy<any> | FieldReadFunction<any> -}; -export type StrictTypedTypePolicies = { +} +export interface StrictTypedTypePolicies { Account?: Omit<TypePolicy, "fields" | "keyFields"> & { keyFields?: false | AccountKeySpecifier | (() => undefined | AccountKeySpecifier), fields?: AccountFieldPolicy, @@ -1744,5 +1742,5 @@ export type StrictTypedTypePolicies = { keyFields?: false | subscription_rootKeySpecifier | (() => undefined | subscription_rootKeySpecifier), fields?: subscription_rootFieldPolicy, } -}; +} export type TypedTypePolicies = StrictTypedTypePolicies & TypePolicies; \ No newline at end of file diff --git a/src/app/network/indexer/indexer-types.generated.ts b/src/app/network/indexer/indexer-types.generated.ts index a5510cdb899b41b3eef37464d7f65607141665fe..632e8cce87aeff8370cb802524fad9b2492dcc56 100644 --- a/src/app/network/indexer/indexer-types.generated.ts +++ b/src/app/network/indexer/indexer-types.generated.ts @@ -4788,6 +4788,16 @@ export type WotSearchLastQueryVariables = Exact<{ export type WotSearchLastQuery = { __typename?: 'query_root', accountConnection: { __typename?: 'AccountConnection', pageInfo: { __typename?: 'PageInfo', endCursor: string, hasNextPage: boolean }, edges: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'Account', id: string, identity?: { __typename?: 'Identity', id: string, index: number, name: string, accountId?: string | null, status?: IdentityStatusEnum | null, isMember: boolean, createdOn: number, membershipHistory: Array<{ __typename: 'MembershipEvent', id: string, eventType?: EventTypeEnum | null }> } | null } }> } }; +export type WotSearchByUidQueryVariables = Exact<{ + name: Scalars['String']['input']; + first: Scalars['Int']['input']; + after?: InputMaybe<Scalars['String']['input']>; + orderBy?: InputMaybe<Array<AccountOrderBy> | AccountOrderBy>; +}>; + + +export type WotSearchByUidQuery = { __typename?: 'query_root', accountConnection: { __typename?: 'AccountConnection', pageInfo: { __typename?: 'PageInfo', endCursor: string, hasNextPage: boolean }, edges: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'Account', id: string, identity?: { __typename?: 'Identity', id: string, index: number, name: string, accountId?: string | null, status?: IdentityStatusEnum | null, isMember: boolean, createdOn: number, membershipHistory: Array<{ __typename: 'MembershipEvent', id: string, eventType?: EventTypeEnum | null }> } | null } }> } }; + export const LightIdentityFragmentDoc = gql` fragment LightIdentity on Identity { id @@ -5142,6 +5152,29 @@ export const WotSearchLastDocument = gql` super(apollo); } } +export const WotSearchByUidDocument = gql` + query WotSearchByUid($name: String!, $first: Int!, $after: String, $orderBy: [AccountOrderBy!]) { + accountConnection( + first: $first + after: $after + orderBy: $orderBy + where: {identity: {name: {_eq: $name}}} + ) { + ...LightAccountConnection + } +} + ${LightAccountConnectionFragmentDoc}`; + + @Injectable({ + providedIn: 'root' + }) + export class WotSearchByUidGQL extends Apollo.Query<WotSearchByUidQuery, WotSearchByUidQueryVariables> { + document = WotSearchByUidDocument; + client = 'indexer'; + constructor(apollo: Apollo.Apollo) { + super(apollo); + } + } type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; @@ -5159,7 +5192,8 @@ export const WotSearchLastDocument = gql` private transferConnectionByAddressGql: TransferConnectionByAddressGQL, private wotSearchByTextGql: WotSearchByTextGQL, private wotSearchByAddressGql: WotSearchByAddressGQL, - private wotSearchLastGql: WotSearchLastGQL + private wotSearchLastGql: WotSearchLastGQL, + private wotSearchByUidGql: WotSearchByUidGQL ) {} blockById(variables: BlockByIdQueryVariables, options?: QueryOptionsAlone<BlockByIdQueryVariables>) { @@ -5225,6 +5259,14 @@ export const WotSearchLastDocument = gql` wotSearchLastWatch(variables: WotSearchLastQueryVariables, options?: WatchQueryOptionsAlone<WotSearchLastQueryVariables>) { return this.wotSearchLastGql.watch(variables, options) } + + wotSearchByUid(variables: WotSearchByUidQueryVariables, options?: QueryOptionsAlone<WotSearchByUidQueryVariables>) { + return this.wotSearchByUidGql.fetch(variables, options) + } + + wotSearchByUidWatch(variables: WotSearchByUidQueryVariables, options?: WatchQueryOptionsAlone<WotSearchByUidQueryVariables>) { + return this.wotSearchByUidGql.watch(variables, options) + } } export interface PossibleTypesResultData { diff --git a/src/app/network/indexer/indexer-wot.gql b/src/app/network/indexer/indexer-wot.gql index 7c29f4ecce88956645adf7cb328bbd2ae288c5a0..f6f1607a029bd45c07d416bc8e68fb7761d99864 100644 --- a/src/app/network/indexer/indexer-wot.gql +++ b/src/app/network/indexer/indexer-wot.gql @@ -25,3 +25,10 @@ query WotSearchLast($first: Int!, $after: String, $orderBy: [AccountOrderBy!], $ ...LightAccountConnection } } + +# search identities by exact name +query WotSearchByUid($name: String!, $first: Int!, $after: String, $orderBy: [AccountOrderBy!]) { + accountConnection(first: $first, after: $after, orderBy: $orderBy, where: { identity: {name: { _eq: $name } }}) { + ...LightAccountConnection + } +} diff --git a/src/app/network/indexer/indexer.service.ts b/src/app/network/indexer/indexer.service.ts index 92f6ac997c4b6aefd0bb9aa944871b95b8743469..b4b961de19451c2e0b5c5f3563d37896b491c429 100644 --- a/src/app/network/indexer/indexer.service.ts +++ b/src/app/network/indexer/indexer.service.ts @@ -94,7 +94,21 @@ export class IndexerService extends GraphqlService<IndexerState> { }; let data$: Observable<LightAccountConnectionFragment>; - if (isNotNilOrBlank(filter.address)) { + if (isNotNilOrBlank(filter.uid)) { + data$ = this.graphqlService + .wotSearchByUid( + { + name: filter.uid, + after: options.after, + first: options.first, + orderBy: { identity: { index: OrderBy.Asc } }, + }, + { + fetchPolicy: options.fetchPolicy || 'cache-first', + } + ) + .pipe(map(({ data }) => data.accountConnection as LightAccountConnectionFragment)); + } else if (isNotNilOrBlank(filter.address)) { data$ = this.graphqlService .wotSearchByAddress( { diff --git a/src/app/shared/constants.ts b/src/app/shared/constants.ts new file mode 100644 index 0000000000000000000000000000000000000000..8518878af99edb52278f951a654bbbc9cefd14d3 --- /dev/null +++ b/src/app/shared/constants.ts @@ -0,0 +1,13 @@ +export const PUBKEY_REGEXP = /^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$/; +export const UID_REGEXP = /^[0-9a-zA-Z-_]{3,42}$/; +export const ADDRESS_REGEXP = /^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}$/; + +export const DATE_ISO_PATTERN = 'YYYY-MM-DDTHH:mm:ss.SSSZ'; +export const DATE_PATTERN = 'YYYY-MM-DD'; +export const DATE_MATCH_REGEXP = new RegExp(/^\d+[-]\d+[-]\d+$/g); +export const DEFAULT_PLACEHOLDER_CHAR = '\u005F'; +export const SPACE_PLACEHOLDER_CHAR = '\u2000'; + +export const INTEGER_REGEXP = /^[-]?\d+$/; +export const NUMBER_REGEXP = /^[-]?\d+(\.\d+)?$/; +export const NUMBER_RANGE_REGEXP = /^(\d+-\d+)|([><=]*\d+)$/; diff --git a/src/app/shared/form.class.ts b/src/app/shared/form.class.ts index 715c13de06c6aa4eeb7e9f82244475f904dd1f85..fd9c19d7d209d2fa59bfe6da947e70fdedf158aa 100644 --- a/src/app/shared/form.class.ts +++ b/src/app/shared/form.class.ts @@ -1,4 +1,15 @@ -import { ChangeDetectorRef, Directive, EventEmitter, Injector, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { + booleanAttribute, + ChangeDetectorRef, + Directive, + EventEmitter, + inject, + Input, + numberAttribute, + OnDestroy, + OnInit, + Output, +} from '@angular/core'; import { FormGroup } from '@angular/forms'; import { BehaviorSubject, Subscription } from 'rxjs'; @@ -6,6 +17,10 @@ import { TranslateService } from '@ngx-translate/core'; import { FormUtils } from '@app/shared/forms'; import { WaitForOptions, waitForTrue } from '@app/shared/observables'; import { SettingsService } from '@app/settings/settings.service'; +import { environment } from '@environments/environment'; +import { logPrefix } from '@app/shared/logs'; +import { FormErrorTranslator, FormErrorTranslatorOptions, IFormPathTranslator } from '@app/shared/form/form-error-translator.service'; +import { changeCaseToUnderscore } from '@app/shared/functions'; export declare interface OnReady { ngOnReady(); @@ -42,21 +57,27 @@ export interface IAppForm { @Directive() // eslint-disable-next-line @angular-eslint/directive-class-suffix -export abstract class AppForm<T> implements IAppForm, OnInit, OnDestroy { - error: string = null; +export abstract class AppForm<T> implements IAppForm, IFormPathTranslator, OnInit, OnDestroy { + private _subscription = new Subscription(); + + protected readonly _debug: boolean = !environment.production; + protected readonly _logPrefix: string; - protected translate: TranslateService; - protected _cd: ChangeDetectorRef; + protected _form: FormGroup; + protected _i18nPrefix: string = 'COMMON.'; + protected _cd: ChangeDetectorRef = inject(ChangeDetectorRef); protected _enable = false; protected ready$ = new BehaviorSubject<boolean>(false); protected loading$ = new BehaviorSubject<boolean>(true); - private _subscription = new Subscription(); - private _form: FormGroup; + protected readonly translate: TranslateService = inject(TranslateService); + protected readonly errorTranslator = inject(FormErrorTranslator); - @Input() debug = false; - @Input() mobile: boolean; - @Input() tabindex: number; + @Input({ transform: booleanAttribute }) debug = !environment.production; + @Input({ transform: booleanAttribute }) mobile: boolean = inject(SettingsService).mobile; + @Input() errorTranslatorOptions: FormErrorTranslatorOptions; + @Input({ transform: numberAttribute }) tabindex: number; + @Input() error: string = null; get loading(): boolean { return this.loading$.value; @@ -127,19 +148,34 @@ export abstract class AppForm<T> implements IAppForm, OnInit, OnDestroy { return this._form; } + get statusChanges() { + return this._form.statusChanges; + } + @Output() cancel = new EventEmitter<void>(); @Output() validate = new EventEmitter<T>(); - protected constructor(injector: Injector, form?: FormGroup) { - this.translate = injector.get(TranslateService); - this.mobile = injector.get(SettingsService).mobile; - this._cd = injector.get(ChangeDetectorRef); + protected constructor( + form?: FormGroup, + options?: { + name?: string; + } + ) { if (form) this.setForm(form); + + // Log + this._logPrefix = logPrefix(this.constructor, options); } ngOnInit() { - this._enable ? this.enable() : this.disable(); + // Init defaults + this.errorTranslatorOptions = this.errorTranslatorOptions || { + pathTranslator: this, // By default, will use translateFormPath() below to translate a form's path + }; + + if (this._enable) this.enable(); + else this.disable(); } ngOnDestroy() { @@ -163,7 +199,7 @@ export abstract class AppForm<T> implements IAppForm, OnInit, OnDestroy { * @param event * @param opts allow to skip validation check, using {checkValid: false} */ - async doSubmit(event: any, opts?: { checkValid?: boolean }) { + async doSubmit(event: Event, opts?: { checkValid?: boolean }) { if (!this._form) { this.markAllAsTouched({ emitEvent: true }); return; @@ -183,7 +219,7 @@ export abstract class AppForm<T> implements IAppForm, OnInit, OnDestroy { } // Emit event - this.validate.emit(event); + this.validate.emit(this._form.value); } setForm(form: FormGroup) { @@ -299,8 +335,30 @@ export abstract class AppForm<T> implements IAppForm, OnInit, OnDestroy { if (!opts || opts.emitEvent !== false) this.markForCheck(); } + translateFormPath(path: string) { + const i18nFieldName = this.getFormPathI18nKey(path); + return this.translate?.instant(i18nFieldName) || i18nFieldName; + } + /* -- protected methods -- */ + protected getFormError(form: FormGroup, opts?: FormErrorTranslatorOptions): string { + if (!form || !this.errorTranslator) return undefined; + return this.errorTranslator.translateFormErrors(this.form, { + pathTranslator: this, + ...opts, + }); + } + + /** + * Translate path into a i18n key (e.g. 'pubkey' into 'COMMON.PUBKEY') + * @param path + * @protected + */ + protected getFormPathI18nKey(path: string) { + return (this._i18nPrefix || '') + changeCaseToUnderscore(path).toUpperCase(); + } + protected registerSubscription(sub: Subscription) { this._subscription.add(sub); } diff --git a/src/app/shared/form/form-error-translator.service.ts b/src/app/shared/form/form-error-translator.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..40db7f6c46ed4b587e06f7e717a626348ff308d9 --- /dev/null +++ b/src/app/shared/form/form-error-translator.service.ts @@ -0,0 +1,130 @@ +import { changeCaseToUnderscore, isEmptyArray, isNil } from '../functions'; +import { Inject, Injectable, InjectionToken, Optional } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { AbstractControl } from '@angular/forms'; +import { FormErrors } from './form-errors'; +import { SharedValidators } from './form-validators'; +import { ObjectMap } from '../types'; +import { FormErrorUtils } from '@app/shared/form/form-errors'; + +export const APP_FORM_ERROR_I18N_KEYS = new InjectionToken<ObjectMap<string>>('appFormErrorKeys'); + +export interface IFormPathTranslator { + /** + * Translate a path on a form, into an i18n string + * @param path + */ + translateFormPath: (path: string) => string; +} + +export interface FormErrorTranslatorOptions { + i18nPrefix?: string; + pathTranslator?: IFormPathTranslator; + separator?: string; + recursive?: boolean; +} + +@Injectable({ providedIn: 'root' }) +export class FormErrorTranslator implements IFormPathTranslator { + private readonly errorI18nKeys: ObjectMap<string>; + + constructor( + private translate: TranslateService, + @Optional() @Inject(APP_FORM_ERROR_I18N_KEYS) errorI18nKeys: ObjectMap<string> + ) { + this.errorI18nKeys = { + ...SharedValidators.I18N_ERROR_KEYS, + ...errorI18nKeys, + }; + } + + translateFormErrors(control: AbstractControl, opts?: FormErrorTranslatorOptions): string { + if (!control || !control.invalid) return ''; + + const separator = (opts && opts.separator) || ', '; + const recursive = !opts || opts.recursive !== false; + const errors = FormErrorUtils.getFormErrors(control, { recursive }); + return ( + errors && + Object.keys(errors).reduce((res, path) => { + const childControl = control.get(path); + // Should be a control map of errors + if (childControl) { + // Try to convert the control path + const i18nPath = this.translateFormPath(path, opts); + + // OK, we have a field name: use it + const columnErrors = Object.keys(childControl.errors).map((errorKey) => this.translateError(errorKey, childControl.errors[errorKey])); + if (isEmptyArray(columnErrors)) return res; + // Add separator + if (res.length) res += separator; + return res + i18nPath + ': ' + columnErrors.join(separator); + } + + // Or try as global form error + const formError = this.translateError(path, errors[path]); + if (isNil(formError)) return res; + return res + (res.length ? separator : '') + formError; + }, '') + ); + } + + translateErrors(errors: FormErrors, opts?: FormErrorTranslatorOptions): string { + const separator = (opts && opts.separator) || ', '; + return ( + errors && + Object.keys(errors).reduce((res, path) => { + const pathErrors = errors[path]; + // Should be a control map of errors + + if (pathErrors) { + // Try to convert the control path + const i18nPath = this.translateFormPath(path, opts); + + // OK, we have a field name: use it + const columnErrors = Object.keys(pathErrors).map((errorKey) => this.translateError(errorKey, pathErrors[errorKey])); + if (isEmptyArray(columnErrors)) return res; + // Add separator + if (res.length) res += separator; + return res + i18nPath + ': ' + columnErrors.join(separator); + } + + // Or try as global form error + const formError = this.translateError(path, pathErrors); + if (isNil(formError)) return res; + return res + (res.length ? separator : '') + formError; + }, '') + ); + } + + translateFormPath(path: string, opts?: FormErrorTranslatorOptions): string { + if (opts?.pathTranslator) { + return opts?.pathTranslator.translateFormPath(path); + } + const i18nKey = ((opts && opts.i18nPrefix) || '') + changeCaseToUnderscore(path).toUpperCase(); + return this.translate.instant(i18nKey); + } + + translateError(errorKey: string, errorContent?: any) { + const i18nKey = + this.errorI18nKeys[errorKey] || + // Try to generate a standard error key, like 'ERROR.FIELD_xxx_xxx' + 'ERROR.FIELD_' + changeCaseToUnderscore(errorKey).toUpperCase(); + + let i18nMessage = this.translate.instant(i18nKey, errorContent); + if (i18nKey !== i18nMessage) return i18nMessage; + + // Try to use the error content, as an i18n key + if (typeof errorContent === 'string') { + i18nMessage = this.translate.instant(errorContent); + if (errorContent !== i18nMessage) return i18nMessage; + } + + // Not translated: show error + console.error( + `[form-error-adapter] Cannot translate error key '${errorKey}'. Please add more formErrorsKey into APP_FORM_ERROR_I18N_KEYS injection token` + ); + + return errorKey; + } +} diff --git a/src/app/shared/form/form-errors.ts b/src/app/shared/form/form-errors.ts new file mode 100644 index 0000000000000000000000000000000000000000..6cd1a3a16fb58cfb164f89dc86b350fe9eac6eff --- /dev/null +++ b/src/app/shared/form/form-errors.ts @@ -0,0 +1,78 @@ +import { AbstractControl, FormArray, FormGroup, ValidationErrors } from '@angular/forms'; + +export interface FormErrors { + [key: string]: ValidationErrors; +} + +export abstract class FormErrorUtils { + static getFormErrors( + control: AbstractControl, + opts?: { + controlName?: string; + result?: FormErrors; + recursive?: boolean; + } + ): FormErrors { + if (!control || control.valid) return undefined; + + opts = opts || {}; + opts.result = opts.result || {}; + + // Form group + if (control instanceof FormGroup) { + // Copy errors + if (control.errors) { + if (opts.controlName) { + opts.result[opts.controlName] = { + ...control.errors, + }; + } else { + opts.result = { + ...opts.result, + ...control.errors, + }; + } + } + + if (!opts || opts.recursive !== false) { + // Loop on children controls + for (const key in control.controls) { + const child = control.controls[key]; + if (child?.enabled) { + this.getFormErrors(child, { + ...opts, + controlName: opts.controlName ? [opts.controlName, key].join('.') : key, + result: opts.result, // Make sure to keep the same result object + }); + } + } + } + } + + // Form array + else if (control instanceof FormArray) { + control.controls.forEach((child, index) => { + this.getFormErrors(child, { + ...opts, + controlName: (opts.controlName || '') + '.' + index, + result: opts.result, // Make sure to keep the same result object + }); + }); + } + + // Other type of control (e.g. simple control) + else if (control.errors) { + if (opts.controlName) { + opts.result[opts.controlName] = { + ...control.errors, + }; + } else { + opts.result = { + ...opts.result, + ...control.errors, + }; + } + } + return opts.result; + } +} diff --git a/src/app/shared/form/form-validators.ts b/src/app/shared/form/form-validators.ts new file mode 100644 index 0000000000000000000000000000000000000000..d8bc4ba1b1817a85de11b1242f9125d0c203655a --- /dev/null +++ b/src/app/shared/form/form-validators.ts @@ -0,0 +1,407 @@ +import { AbstractControl, FormControl, UntypedFormControl, UntypedFormGroup, ValidationErrors, ValidatorFn } from '@angular/forms'; +import { isMoment, Moment, unitOfTime } from 'moment'; +import { ADDRESS_REGEXP, PUBKEY_REGEXP, UID_REGEXP } from '../constants'; +import { isEmptyArray, isNil, isNilOrBlank, isNotNil, isNotNilOrBlank, toBoolean } from '../functions'; +import { fromDateISOString } from '../dates'; + +// @dynamic +export class SharedValidators { + private static _DOUBLE_REGEXP_CACHE = { + NO_LIMIT: /^-?\d+([.,]\d*)?$/, + 1: /^-?\d+([.,]\d)?$/, // 1 decimal max + 2: /^-?\d+([.,]\d{1,2})?$/, // 2 decimals max + 3: /^-?\d+([.,]\d{1,3})?$/, // 3 decimals max + }; + + private static getDoubleRegexp(maxDecimals: number): RegExp { + if (isNil(maxDecimals)) return this._DOUBLE_REGEXP_CACHE.NO_LIMIT; + + if (maxDecimals < 0) throw new Error(`Invalid maxDecimals value: ${maxDecimals}`); + const regexp = this._DOUBLE_REGEXP_CACHE[maxDecimals]; + if (regexp) return regexp; + + // New: add to cache + this._DOUBLE_REGEXP_CACHE[maxDecimals] = new RegExp(`^-?\\d+([.,]\\d{1,${maxDecimals}})?$`); + return this._DOUBLE_REGEXP_CACHE[maxDecimals]; + } + + static readonly I18N_ERROR_KEYS = { + required: 'ERROR.FIELD_REQUIRED', + min: 'ERROR.FIELD_MIN', + max: 'ERROR.FIELD_MAX', + minlength: 'ERROR.FIELD_TOO_SHORT_WITH_LENGTH', + maxlength: 'ERROR.FIELD_TOO_LONG_WITH_LENGTH', + accent: 'ERROR.FIELD_ACCENT', + pubkey: 'ERROR.FIELD_NOT_VALID_PUBKEY', + address: 'ERROR.FIELD_NOT_VALID_ADDRESS', + uid: 'ERROR.INVALID_USER_ID', + date: 'ERROR.FIELD_NOT_VALID_DATE', + dateIsAfter: 'ERROR.FIELD_NOT_VALID_DATE_AFTER', + dateIsBefore: 'ERROR.FIELD_NOT_VALID_DATE_BEFORE', + dateRange: 'ERROR.FIELD_NOT_VALID_DATE_RANGE', + dateMinDuration: 'ERROR.FIELD_NOT_VALID_DATE_MIN_DURATION', + dateMaxDuration: 'ERROR.FIELD_NOT_VALID_DATE_MAX_DURATION', + maxDecimals: 'ERROR.FIELD_MAXIMUM_DECIMALS', + decimal: 'ERROR.FIELD_NOT_VALID_DECIMAL', + integer: 'ERROR.FIELD_NOT_INT', + email: 'ERROR.FIELD_NOT_VALID_EMAIL', + pattern: 'ERROR.FIELD_NOT_VALID_PATTERN', + unique: 'ERROR.FIELD_NOT_UNIQUE', + }; + + static empty(control: UntypedFormControl): ValidationErrors | null { + if (isNotNilOrBlank(control.value)) { + return { empty: true }; + } + return null; + } + + static pubkey(control: UntypedFormControl): ValidationErrors | null { + const value = control.value; + if (value && (typeof value !== 'string' || !PUBKEY_REGEXP.test(value))) { + return { pubkey: true }; + } + return null; + } + + static address(control: UntypedFormControl): ValidationErrors | null { + const value = control.value; + if (value && (typeof value !== 'string' || !ADDRESS_REGEXP.test(value))) { + return { address: true }; + } + return null; + } + + static uid(control: FormControl<string>): ValidationErrors { + const value = control.value; + if (value && (typeof value !== 'string' || !UID_REGEXP.test(value))) { + return { uid: true }; + } + return null; + } + + static integer(control: UntypedFormControl): ValidationErrors | null { + if (isNilOrBlank(control.value)) return null; + const value = parseFloat(control.value); + if (isNaN(value) || (value | 0) !== value) { + return { integer: true }; + } + return null; + } + + static decimal(opts?: { maxDecimals?: number }): ValidatorFn { + const regexp = this.getDoubleRegexp(opts?.maxDecimals); + return (control: UntypedFormControl): ValidationErrors | null => { + const value = control.value; + if (Number.isNaN(value)) { + // DEBUG + //console.debug("WARN: Getting a NaN value (decimal was expected) !"); + return null; + } + if (isNotNil(value) && value !== '' && !regexp.test(value as string)) { + return isNotNil(opts?.maxDecimals) ? { maxDecimals: { maxDecimals: opts.maxDecimals } } : { decimal: true }; + } + return null; + }; + } + + /** + * Check precision. e.g. if precision=0.5 then value=0.6 is invalid, but 1.5 is valid + * + * @param precision + */ + static precision(precision: number): ValidatorFn { + if (isNil(precision)) throw new Error('Required a not nil precision'); + + // WARN: we should define a multiplier, because modulo in javascript will only work on integer + // (e.g. "7 % 0.1" in javascript will NOT return zero (but 0.09999999999999962) + const precisionNbDecimals = (precision.toString().split('.')[1] || '').length; + const multiplier = Math.pow(10, precisionNbDecimals); + const multipliedPrecision = Math.round(multiplier * precision); + + // The validator function + return (control: UntypedFormControl): ValidationErrors | null => { + const value = control.value; + if (isNilOrBlank(value) || Number.isNaN(value)) { + return null; + } + // WARN: Convert value into integer, before applying modulo operator + const mod = Math.round(+value * multiplier) % multipliedPrecision; + if (mod !== 0) { + // DEBUG + //console.debug(`WARN Getting a ${value} with an invalid precision (expected precision is: ${precision})`); + return { precision: { precision } }; + } + return null; + }; + } + + static date(control: UntypedFormControl): ValidationErrors | null { + const value = control.value; + const date = !value || isMoment(value) ? value : fromDateISOString(value); + if (date && (!date.isValid() || date.year() < 1900)) { + return { date: true }; + } + return null; + } + + static dateIsAfter(previousValue: Moment, errorParam: string, granularity?: unitOfTime.StartOf): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value = fromDateISOString(control.value); + if (isNotNil(value) && isNotNil(previousValue) && value.isSameOrBefore(previousValue, granularity)) { + // Return the error + return { dateIsAfter: { minDate: errorParam } }; + } + return null; + }; + } + + static dateIsBefore(maxValue: Moment, errorParam: string, granularity?: unitOfTime.StartOf): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value = fromDateISOString(control.value); + if (isNotNil(value) && isNotNil(maxValue) && value.isSameOrAfter(maxValue, granularity)) { + // Return the error + return { dateIsBefore: { maxDate: errorParam } }; + } + return null; + }; + } + + static dateRangeEnd(startDateFieldName: string, msg?: string): ValidatorFn { + const errorCode = msg ? 'msg' : 'dateRange'; + const error = msg ? { msg } : { dateRange: true }; + return (control: AbstractControl): ValidationErrors | null => { + // Form group not created yet: skip + if (!control.parent) return null; + + const startControl = control.parent.get(startDateFieldName); + if (!startControl) { + console.warn(`Cannot find brother control '${startDateFieldName}' in the parent form`); + return null; + } + const startDate = fromDateISOString(startControl.value); + const endDate = fromDateISOString(control.value); + // Error if value <= beforeDate + if (isNotNil(endDate) && isNotNil(startDate) && endDate.isSameOrBefore(startDate)) { + return error; + } + // OK: clear existing errors + SharedValidators.clearError(control, errorCode); + return null; + }; + } + + static dateRangeStart(endDateFieldName: string, msg?: string): ValidatorFn { + const errorCode = msg ? 'msg' : 'dateRange'; + const error = msg ? { msg } : { dateRange: true }; + return (control: AbstractControl): ValidationErrors | null => { + // Form group not created yet: skip + if (!control.parent) return null; + + const endDateControl = control.parent.get(endDateFieldName); + if (!endDateControl) { + console.warn(`Cannot find control '${endDateFieldName}' in the parent form`); + return null; + } + const endDate = fromDateISOString(endDateControl.value); + const startDate = fromDateISOString(control.value); + // Error if value <= beforeDate + if (isNotNil(startDate) && isNotNil(endDate) && startDate.isSameOrAfter(endDate)) { + return error; + } + // OK: clear existing errors + SharedValidators.clearError(control, errorCode); + return null; + }; + } + + static copyParentErrors(errorNames?: string[]): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + // Skip if control already has some errors + if (control.errors) return null; + // Skip if parent form group not created yet + if (!control.parent) return null; + + // Form group not created yet + if (!control.parent) return null; + + const errors = control.parent.errors; + + // No errors, or copy all errors + if (!errors || isEmptyArray(errorNames)) return errors; + + // Copy only expected errors + return Object.keys(errors).reduce((res, key) => { + if (errorNames.includes(key)) { + res[key] = errors[key]; + } + return res; + }, {}); + }; + } + + static clearError(control: AbstractControl, errorCode: string) { + if (control.hasError(errorCode)) { + const errors = control.errors; + if (errors && errors[errorCode]) { + // Only one error: reset errors + if (Object.getOwnPropertyNames(errors).length === 1) { + control.setErrors(null); + } + // Other errors exists: just remove this error + else { + delete errors[errorCode]; + control.setErrors(errors); + } + } + } + } +} + +// @dynamic +export class SharedFormGroupValidators { + static dateRange(startDateField: string, endDateField: string, opts?: { msg?: string; fieldOnly?: boolean } | string): ValidatorFn { + const msg = typeof opts === 'string' ? opts : opts?.msg; + const fieldOnly = typeof opts === 'object' ? opts?.fieldOnly : undefined; + const errorCode = isNotNilOrBlank(msg) ? 'msg' : 'dateRange'; + const rangeError = msg ? { msg } : { dateRange: true }; + return (group: UntypedFormGroup): ValidationErrors | null => { + const startDate = fromDateISOString(group.get(startDateField).value); + const endField = group.get(endDateField); + const endDate = fromDateISOString(endField.value); + if (isNotNil(startDate) && isNotNil(endDate) && startDate.isAfter(endDate)) { + // Update end field + endField.markAsPending(); + endField.setErrors({ + ...endField.errors, + ...rangeError, + }); + // Return the error (should be applied to the parent form, by default) + return fieldOnly ? null : rangeError; + } + // OK: remove the existing on the end field + SharedValidators.clearError(endField, errorCode); + return null; + }; + } + + static dateMaxDuration(startDateField: string, endDateField: string, maxDuration: number, durationUnit?: moment.unitOfTime.Diff): ValidatorFn { + const maxDurationError = { dateMaxDuration: true }; + return (group: UntypedFormGroup): ValidationErrors | null => { + const startDate = fromDateISOString(group.get(startDateField).value); + const endField = group.get(endDateField); + const endDate = fromDateISOString(endField.value); + if (isNotNil(startDate) && isNotNil(endDate) && Math.abs(startDate.diff(endDate, durationUnit)) > maxDuration) { + // Update end field + endField.markAsTouched({ onlySelf: true }); + endField.markAsPending(); + endField.setErrors({ + ...endField.errors, + ...maxDurationError, + }); + // Return the error (should be apply to the parent form) + return maxDurationError; + } + // OK: remove the existing on the end field + SharedValidators.clearError(endField, 'dateMaxDuration'); + return null; + }; + } + + static dateMinDuration(startDateField: string, endDateField: string, minDuration: number, durationUnit?: moment.unitOfTime.Diff): ValidatorFn { + return (group: UntypedFormGroup): ValidationErrors | null => { + const endField = group.get(endDateField); + const startDate = fromDateISOString(group.get(startDateField).value); + const endDate = fromDateISOString(endField.value); + if (isNotNil(startDate) && isNotNil(endDate) && Math.abs(startDate.diff(endDate, durationUnit)) < minDuration) { + // Update end field + const endFieldErrors: ValidationErrors = endField.errors || {}; + endFieldErrors['dateMinDuration'] = true; + endField.setErrors(endFieldErrors); + endField.markAsTouched({ onlySelf: true }); + // Return the error (should be apply to the parent form) + return { dateMinDuration: true }; + } + // OK: remove the existing on the end field + else { + SharedValidators.clearError(endField, 'dateMinDuration'); + } + return null; + }; + } + + static requiredIf( + fieldName: string, + anotherFieldToCheck: string | AbstractControl, + opts?: { fieldOnly?: boolean; predicate?: (control: AbstractControl) => boolean } + ): ValidatorFn { + const predicate = opts?.predicate || ((control) => isNotNilOrBlank(control.value)); + return (group: UntypedFormGroup): ValidationErrors | null => { + const control = group.get(fieldName); + const anotherControl = anotherFieldToCheck instanceof AbstractControl ? anotherFieldToCheck : group.get(anotherFieldToCheck); + if (!anotherControl) throw new Error('Unable to find field to check!'); + if (isNilOrBlank(control.value) && predicate(anotherControl)) { + const error = { required: true }; + control.setErrors(error); + control.markAsTouched({ onlySelf: true }); + return opts?.fieldOnly ? null : error; + } + SharedValidators.clearError(control, 'required'); + return null; + }; + } + + static requiredIfTrue(fieldName: string, anotherFieldToCheck: string | AbstractControl, opts?: { fieldOnly?: boolean }): ValidatorFn { + return (group: UntypedFormGroup): ValidationErrors | null => { + const control = group.get(fieldName); + const anotherControl = anotherFieldToCheck instanceof AbstractControl ? anotherFieldToCheck : group.get(anotherFieldToCheck); + if (!anotherControl) throw new Error('Unable to find field to check!'); + if (isNilOrBlank(control.value) && toBoolean(anotherControl.value, false)) { + const error = { required: true }; + control.setErrors(error); + control.markAsTouched({ onlySelf: true }); + return opts?.fieldOnly ? null : error; + } + SharedValidators.clearError(control, 'required'); + return null; + }; + } + + static requiredIfEmpty(fieldName: string, anotherFieldToCheck: string, opts?: { fieldOnly?: boolean }): ValidatorFn { + return (group: UntypedFormGroup): ValidationErrors | null => { + const control = group.get(fieldName); + if (isNilOrBlank(control.value) && isNilOrBlank(group.get(anotherFieldToCheck).value)) { + const error = { required: true }; + control.setErrors(error); + control.markAsTouched({ onlySelf: true }); + return opts?.fieldOnly ? null : error; + } + SharedValidators.clearError(control, 'required'); + return null; + }; + } + + static propagateIfDirty(fieldName: string, fieldNameToPropagate: string, valueToPropagate: any): ValidatorFn { + return (group: UntypedFormGroup): null => { + const control = group.get(fieldName); + const controlToPropagate = group.get(fieldNameToPropagate); + if (control.dirty && controlToPropagate.value !== valueToPropagate) { + controlToPropagate.setValue(valueToPropagate); + } + return null; + }; + } + + /** + * Same as compose, but keep only the first errors (instead of the union) + * + * @param validators + */ + first(validators: (ValidatorFn | null | undefined)[]): ValidationErrors | null { + return (control) => + validators + .filter(isNotNil) + .map((validator) => validator(control)) + .find(isNotNil) || null; + } +} diff --git a/src/app/shared/functions.ts b/src/app/shared/functions.ts index a61324f76a7dbc08940986ea3b4589a8abbb4eeb..53484d0b5d434e386efffa6f4f462b0a9a1d4f95 100644 --- a/src/app/shared/functions.ts +++ b/src/app/shared/functions.ts @@ -2,6 +2,7 @@ import { KeysEnum, KeyValueType } from '@app/shared/types'; import { setTimeout } from '@rx-angular/cdk/zone-less/browser'; import { u32, u64 } from '@polkadot/types-codec'; import { Codec } from '@polkadot/types-codec/types'; +import { INTEGER_REGEXP, NUMBER_RANGE_REGEXP, NUMBER_REGEXP, PUBKEY_REGEXP, UID_REGEXP } from '@app/shared/constants'; export function isNil<T>(obj: T | null | undefined): boolean { return obj === undefined || obj === null; @@ -206,19 +207,21 @@ export function sort<T>(array: T[], attribute: string, opts?: Intl.CollatorOptio .slice() // copy .sort((a, b) => compareFn(a[attribute], b[attribute])); } -const INTEGER_REGEXP = /^[-]?\d+$/; export function isInt(value: string): boolean { return isNotNil(value) && INTEGER_REGEXP.test(value); } -const NUMBER_REGEXP = /^[-]?\d+(\.\d+)?$/; export function isNumber(value: string): boolean { return isNotNil(value) && NUMBER_REGEXP.test(value); } - -const NUMBER_RANGE_REGEXP = /^(\d+-\d+)|([><=]*\d+)$/; export function isNumberRange(value: string): boolean { return isNotNil(value) && NUMBER_RANGE_REGEXP.test(value); } +export function isValidUid(value: string): boolean { + return isNotNil(value) && UID_REGEXP.test(value); +} +export function isValidPubkey(value: string): boolean { + return isNotNil(value) && PUBKEY_REGEXP.test(value); +} // eslint-disable-next-line @typescript-eslint/no-explicit-any export function getPropertyByPath(obj: any, path: string, defaultValue?: any): any { diff --git a/src/app/shared/logs.ts b/src/app/shared/logs.ts new file mode 100644 index 0000000000000000000000000000000000000000..5a493153f12e57046fb5554d298e6c14ad31b4fe --- /dev/null +++ b/src/app/shared/logs.ts @@ -0,0 +1,6 @@ +import { changeCaseToUnderscore } from '@app/shared/functions'; +import { Constructor } from '@app/shared/types'; + +export function logPrefix<T extends Constructor | Function>(constructor: T, options?: { name?: string }) { + return `[${options?.name || changeCaseToUnderscore(constructor.name).replace(/_/g, '-')}] `; +} diff --git a/src/app/shared/pages/base-page.class.ts b/src/app/shared/pages/base-page.class.ts index 2ce90a80ecdd6d9980feff161a85ec7411d5e5e3..04a0696164750d178a22d705f3b12803babf4803 100644 --- a/src/app/shared/pages/base-page.class.ts +++ b/src/app/shared/pages/base-page.class.ts @@ -12,6 +12,8 @@ import { RxStateProperty, RxStateRegister, RxStateSelect } from '@app/shared/dec import { FormGroup } from '@angular/forms'; import { ContextService } from '@app/shared/services/storage/context.service'; import { AppError } from '@app/shared/types'; +import { logPrefix } from '@app/shared/logs'; +import { setTimeout } from '@rx-angular/cdk/zone-less/browser'; export interface AppPageState { error: string; @@ -98,12 +100,12 @@ export abstract class AppPage<S extends AppPageState = AppPageState, O extends A if (form) this.setForm(form); // Init state - if (this._options?.initialState && this._state) { + if (this._state && this._options?.initialState) { this._state.set(this._options.initialState); } - // DEV - this._logPrefix = `[${this._options.name}] `; + // Log + this._logPrefix = logPrefix(this.constructor, this._options); } ngOnInit() { @@ -163,9 +165,14 @@ export abstract class AppPage<S extends AppPageState = AppPageState, O extends A return Promise.resolve(initialState); } - protected setError(err: string | { message: string }, opts = { emitEvent: true }) { - this.error = (typeof err === 'object' ? err.message : err) || 'ERROR.UNKNOWN_ERROR'; + protected setError(err: string | { message: string }, opts = { emitEvent: true, hideDelay: 4000 }) { + const error = (typeof err === 'object' ? err.message : err) || 'ERROR.UNKNOWN_ERROR'; + this.error = error; if (opts.emitEvent !== false) this.markForCheck(); + + setTimeout(() => { + if (this.error === error) this.resetError(); + }, opts.hideDelay); } protected resetError(opts = { emitEvent: true }) { @@ -228,6 +235,7 @@ export abstract class AppPage<S extends AppPageState = AppPageState, O extends A type: 'error', icon: 'alert', swipeGesture: 'vertical', + duration: 5000, // 5s (longer) ...opts, }); } diff --git a/src/app/shared/pipes/form.pipes.ts b/src/app/shared/pipes/form.pipes.ts index 8fa4087a351a4def1d254d2d219ffaf178e13119..ed6ee4314570daa2a66a021e8dd77e41d0c25f97 100644 --- a/src/app/shared/pipes/form.pipes.ts +++ b/src/app/shared/pipes/form.pipes.ts @@ -1,7 +1,86 @@ import { ChangeDetectorRef, OnDestroy, Pipe, PipeTransform } from '@angular/core'; -import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms'; +import { AbstractControl, FormArray, FormControl, FormControlStatus, FormGroup } from '@angular/forms'; import { Subscription } from 'rxjs'; -import { equals } from '../functions'; +import { equals, isNotNil } from '../functions'; +import { FormErrorTranslator, FormErrorTranslatorOptions } from '@app/shared/form/form-error-translator.service'; + +@Pipe({ + name: 'formError', + pure: false, +}) +export class FormErrorPipe implements PipeTransform, OnDestroy { + private _value = ''; + + private _lastForm: AbstractControl | null = null; + private _lastOptions: FormErrorTranslatorOptions | null = null; + private _onFormStatusChanges: Subscription | undefined; + + constructor( + private service: FormErrorTranslator, + private _ref: ChangeDetectorRef + ) {} + + transform(form: AbstractControl, opts?: FormErrorTranslatorOptions): string { + if (!form) { + this._dispose(); + return ''; + } + + // if we ask another time for the same form and opts, return the last value + if (form === this._lastForm && equals(opts, this._lastOptions)) { + return this._value; + } + + // store the query, in case it changes + this._lastForm = form; + + // store the params, in case they change + this._lastOptions = opts; + + // set the value + this._updateValue(form, opts); + + // if there is a subscription to onLangChange, clean it + this._dispose(); + + // subscribe to onTranslationChange event, in case the translations change + if (!this._onFormStatusChanges) { + this._onFormStatusChanges = form.statusChanges.subscribe((status) => { + this._updateValue(form, opts, status); + }); + } + + return this._value; + } + + ngOnDestroy(): void { + this._dispose(); + } + + private _updateValue(form: AbstractControl, opts?: FormErrorTranslatorOptions, status?: FormControlStatus) { + // Form is invalid: compute error + if (status ? status === 'INVALID' : form.invalid) { + const newValue = this.service.translateFormErrors(form, opts); + if (newValue !== this._value) { + this._value = newValue; + this._ref.markForCheck(); + } + } + // For is valid (or pending): clean if need + else if (this._value !== undefined) { + this._value = undefined; + this._ref.markForCheck(); + } + } + + /** + * Clean any existing subscription to change events + */ + private _dispose(): void { + this._onFormStatusChanges?.unsubscribe(); + this._onFormStatusChanges = undefined; + } +} @Pipe({ name: 'formGet', @@ -25,8 +104,9 @@ export class FormGetControlPipe implements PipeTransform { name: 'formGetArray', }) export class FormGetArrayPipe implements PipeTransform { - transform(form: AbstractControl, path?: Array<string | number> | string): FormArray { - return ((form && path && form.get(path)) || form) as FormArray; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + transform<TControl extends AbstractControl<any> = any>(form: AbstractControl, path?: Array<string | number> | string): FormArray<TControl> { + return (form && isNotNil(path) ? form.get(path) : form) as FormArray<TControl>; } } @@ -34,8 +114,12 @@ export class FormGetArrayPipe implements PipeTransform { name: 'formGetGroup', }) export class FormGetGroupPipe implements PipeTransform { - transform(form: AbstractControl, path?: Array<string | number> | string): FormGroup { - return ((form && path && form.get(path)) || form) as FormGroup; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + transform<TControl extends { [K in keyof TControl]: AbstractControl<any> } = any>( + form: AbstractControl, + path?: Array<string | number> | string + ): FormGroup<TControl> { + return (form && isNotNil(path) ? form.get(path) : form) as FormGroup<TControl>; } } @@ -45,8 +129,7 @@ export class FormGetGroupPipe implements PipeTransform { }) export class FormGetValuePipe implements PipeTransform, OnDestroy { // eslint-disable-next-line @typescript-eslint/no-explicit-any - value: any = undefined; - + private _value: any = undefined; private _lastControl: AbstractControl | null = null; private _lastPath: Array<string | number> | string; private _onControlValueChanges: Subscription | undefined; @@ -62,7 +145,7 @@ export class FormGetValuePipe implements PipeTransform, OnDestroy { // if we ask another time for the same form and opts, return the last value if (form === this._lastControl && equals(path, this._lastPath)) { - return this.value; + return this._value; } // store the query, in case it changes @@ -73,16 +156,16 @@ export class FormGetValuePipe implements PipeTransform, OnDestroy { // set the value const control = path ? form.get(path) : form; - this.value = control?.value; + this._value = control?.value; - // if there is a subscription to onLangChange, clean it + // if there is already a subscription, clean it this._dispose(); if (control) { // subscribe to valueChanges event this._onControlValueChanges = control.valueChanges.subscribe((value) => { - if (value !== this.value) { - this.value = value; + if (value !== this._value) { + this._value = value; this._ref.markForCheck(); } }); @@ -91,15 +174,15 @@ export class FormGetValuePipe implements PipeTransform, OnDestroy { if (listenStatusChanges) { this._onControlStatusChanges = control.statusChanges.subscribe(() => { const value = control.value; - if (value !== this.value) { - this.value = value; + if (value !== this._value) { + this._value = value; this._ref.markForCheck(); } }); } } - return this.value; + return this._value; } ngOnDestroy(): void { diff --git a/src/app/shared/pipes/pipes.module.ts b/src/app/shared/pipes/pipes.module.ts index 2ada7bbd893e2a6e4487edb17d50f9c6d79b0994..b0affc4c58070536c9dc97c659394b4dd1bd1f04 100644 --- a/src/app/shared/pipes/pipes.module.ts +++ b/src/app/shared/pipes/pipes.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { IonicModule } from '@ionic/angular'; -import { BlockTimePipe, BlocksToDurationPipe } from './block-timestamp.pipe'; +import { BlocksToDurationPipe, BlockTimePipe } from './block-timestamp.pipe'; import { DateFormatPipe } from './date-format.pipe'; import { DateDiffDurationPipe } from './date-diff-duration.pipe'; import { DateFromNowPipe } from './date-from-now.pipe'; @@ -32,7 +32,7 @@ import { TranslatablePipe, } from './string.pipes'; import { NgInitDirective } from './ng-init.pipe'; -import { FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe } from './form.pipes'; +import { FormErrorPipe, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe } from './form.pipes'; import { PropertyGetPipe } from './property.pipes'; import { AmountFormatPipe } from '@app/shared/pipes/amount.pipe'; import { AddressFormatPipe, AddressToPubkeyPipePipe } from '@app/shared/pipes/address.pipes'; @@ -84,6 +84,7 @@ import { BlockNumberPipe } from '@app/shared/pipes/block-number.pipe'; FormGetArrayPipe, FormGetGroupPipe, FormGetValuePipe, + FormErrorPipe, // Block BlockTimePipe, BlockNumberPipe, @@ -138,6 +139,7 @@ import { BlockNumberPipe } from '@app/shared/pipes/block-number.pipe'; FormGetArrayPipe, FormGetGroupPipe, FormGetValuePipe, + FormErrorPipe, // Block BlockTimePipe, diff --git a/src/app/shared/services/rx-service.class.ts b/src/app/shared/services/rx-service.class.ts index 79eddf324a156e2e523dcf61342c0857203f76f4..2b9e7f2f7751c79751c3be6cfaf6c56bfeb369eb 100644 --- a/src/app/shared/services/rx-service.class.ts +++ b/src/app/shared/services/rx-service.class.ts @@ -2,6 +2,7 @@ import { Subscription } from 'rxjs'; import { environment } from '@environments/environment'; import { Directive, OnDestroy } from '@angular/core'; import { RxState } from '@rx-angular/state'; +import { logPrefix } from '@app/shared/logs'; export interface RxBaseServiceOptions<T extends object> { name?: string; @@ -15,8 +16,8 @@ export abstract class RxBaseService<T extends object = Object, O extends RxBaseS { private _subscription: Subscription = null; - protected readonly _debug: boolean; - protected readonly _logPrefix: string = null; + protected readonly _debug = !environment.production; + protected readonly _logPrefix: string; get data(): T { return this.get(); @@ -25,12 +26,13 @@ export abstract class RxBaseService<T extends object = Object, O extends RxBaseS protected constructor(protected options?: O) { super(); + // Init state if (options?.initialState) { this.set(options.initialState); } - this._debug = !environment.production; - this._logPrefix = `[${options?.name || 'base-service'}] `; + // Log + this._logPrefix = logPrefix(this.constructor, options); } ngOnDestroy() { diff --git a/src/app/shared/services/service.class.ts b/src/app/shared/services/service.class.ts index f9e10bf208723404ca84d7def366ef06c70c32d2..e86e83fd51d6311666669b826a7c75f787cf3fb2 100644 --- a/src/app/shared/services/service.class.ts +++ b/src/app/shared/services/service.class.ts @@ -1,6 +1,7 @@ import { Subscription } from 'rxjs'; import { environment } from '@environments/environment'; import { Directive, OnDestroy } from '@angular/core'; +import { logPrefix } from '@app/shared/logs'; export interface BaseServiceOptions { name?: string; @@ -10,12 +11,12 @@ export interface BaseServiceOptions { export abstract class BaseService<O extends BaseServiceOptions = BaseServiceOptions> implements OnDestroy { private _subscription: Subscription = null; - protected readonly _debug: boolean; - protected readonly _logPrefix: string = null; + protected readonly _debug = !environment.production; + protected readonly _logPrefix: string; protected constructor(protected options?: O) { - this._debug = !environment.production; - this._logPrefix = `[${options?.name || 'base-service'}] `; + // Log + this._logPrefix = logPrefix(this.constructor, options); } ngOnDestroy() { diff --git a/src/app/shared/types.ts b/src/app/shared/types.ts index 68d5d069bbc75a401037e562f54f3e81892ceb8d..2b57f2e732c148073f720050cc6caa0f7fd193f3 100644 --- a/src/app/shared/types.ts +++ b/src/app/shared/types.ts @@ -47,3 +47,6 @@ export interface ListItems<T, F> { fetchMoreFn: FetchMoreFn<LoadResult<T>>; fetchSize: number; } + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export declare type Constructor<T = any> = new (...args: any[]) => T; diff --git a/src/app/transfer/send/transfer.page.html b/src/app/transfer/send/transfer.page.html index 157411553c43a38ec4e363d0a979adc890d69b19..6cf58fa7318f6000b7d3ed4979824ab4b25024d3 100644 --- a/src/app/transfer/send/transfer.page.html +++ b/src/app/transfer/send/transfer.page.html @@ -121,7 +121,7 @@ <ion-button (click)="doSubmit()" [disabled]="loading || invalid" color="tertiary"> <ion-icon slot="start" name="paper-plane"></ion-icon> - <ion-label translate>TRANSFER.BTN_SEND</ion-label> + <ion-label translate>COMMON.BTN_SEND</ion-label> </ion-button> </ng-template> diff --git a/src/app/wot/wot-details.page.html b/src/app/wot/wot-details.page.html index cd7b8a9ce2265f0ae78bd48e0afa0c5c0f0b72c6..1e20f914a19686a82666bf333350c35769494a1a 100644 --- a/src/app/wot/wot-details.page.html +++ b/src/app/wot/wot-details.page.html @@ -91,12 +91,24 @@ @if (account.meta?.index) { <ion-item> <ion-icon aria-hidden="true" slot="start" name="calendar-clear-outline"></ion-icon> - <ion-label> - <h3 translate>COMMON.UID</h3> - <p>{{ 'WOT.REGISTERED_SINCE' | translate }}</p> - </ion-label> - - <ion-badge color="warning" slot="end">{{ account.meta?.uid }}</ion-badge> + <!-- special case for unconfirmed (UID is not known yet)--> + @if (account.meta.status === IdentityStatusEnum.Unconfirmed) { + <ion-label color="danger" [innerHTML]="'WOT.IDENTITY_UNCONFIRMED' | translate"></ion-label> + } @else { + <ion-label> + <h3 translate>COMMON.UID</h3> + <p>{{ 'WOT.REGISTERED_SINCE' | translate }}</p> + </ion-label> + + <ion-buttons slot="end" class="vertical-alignment"> + <ion-badge [color]="account.meta.isMember ? 'warning' : 'medium'"> + {{ account.meta?.uid }} + </ion-badge> + @if (!account.meta.isMember) { + <ion-note color="danger">({{ 'WOT.STATUS_ENUM.' + account.meta.status | translate }})</ion-note> + } + </ion-buttons> + } </ion-item> <!-- Received cert count --> diff --git a/src/app/wot/wot-details.page.ts b/src/app/wot/wot-details.page.ts index 5dd7ec3b281c3939f073dd1aa15f8b6c2d080bcc..f70530e9faa37520aa14486be5b55e59ff8fdd5f 100644 --- a/src/app/wot/wot-details.page.ts +++ b/src/app/wot/wot-details.page.ts @@ -12,6 +12,7 @@ import { filter, map } from 'rxjs/operators'; import { firstArrayValue, isNotNilOrBlank } from '@app/shared/functions'; import { IndexerService } from '@app/network/indexer/indexer.service'; import { address2PubkeyV1, pubkeyV1Checksum } from '@app/shared/currencies'; +import { IdentityStatusEnum } from '@app/network/indexer/indexer-types.generated'; export interface WotDetailsPageState extends AppPageState { address: string; @@ -155,4 +156,6 @@ export class WotDetailsPage extends AppPage<WotDetailsPageState> implements OnIn this.markAsLoaded(); } } + + protected readonly IdentityStatusEnum = IdentityStatusEnum; } diff --git a/src/app/wot/wot-lookup.page.html b/src/app/wot/wot-lookup.page.html index 68df96ce6a3f822281805046b1a6f42f86da3d93..d8e720da1566adba292cee1f13b2330b585e1071 100644 --- a/src/app/wot/wot-lookup.page.html +++ b/src/app/wot/wot-lookup.page.html @@ -141,7 +141,9 @@ </ion-text> <!-- not member --> @if (!item.meta?.isMember) { - <ion-text color="danger" translate>WOT.NOT_MEMBER_PARENTHESIS</ion-text> + <ion-text color="danger"> + ({{ (item.meta?.status ? 'WOT.STATUS_ENUM.' + item.meta.status : 'WOT.NOT_MEMBER_PARENTHESIS') | translate }}) + </ion-text> } </p> </ion-label> diff --git a/src/app/wot/wot.model.ts b/src/app/wot/wot.model.ts index 2e1b5ebf5c364960ff3d8959b80e46654f8f6967..e0df8c99947fad1489b7bff2cba1154cf109b05b 100644 --- a/src/app/wot/wot.model.ts +++ b/src/app/wot/wot.model.ts @@ -1,5 +1,7 @@ import { equals, isNil, isNilOrBlank } from '@app/shared/functions'; import { PredefinedColors } from '@app/shared/colors/colors.utils'; +import { InjectionToken } from '@angular/core'; +import { WotController } from './wot.controller'; export interface WotLookupOptions { debounceTime?: number; @@ -18,6 +20,7 @@ export interface WotSearchFilter { searchText?: string; last?: boolean; pending?: boolean; + uid?: string; } export class WotSearchFilterUtils { @@ -29,3 +32,5 @@ export class WotSearchFilterUtils { return !filter || (isNilOrBlank(filter.searchText) && isNil(filter.last) && isNilOrBlank(filter.address)); } } + +export const APP_WOT_CONTROLLER = new InjectionToken<WotController>('WotController'); diff --git a/src/app/wot/wot.module.ts b/src/app/wot/wot.module.ts index a1792678bebd4401b309496dc525b92700a46c20..14598f32c308a0ea668c6ad3366c036e564cea16 100644 --- a/src/app/wot/wot.module.ts +++ b/src/app/wot/wot.module.ts @@ -1,4 +1,4 @@ -import { NgModule } from '@angular/core'; +import { ModuleWithProviders, NgModule } from '@angular/core'; import { WotLookupPage } from './wot-lookup.page'; import { AppSharedModule } from '@app/shared/shared.module'; @@ -6,6 +6,8 @@ import { TranslateModule } from '@ngx-translate/core'; import { WotDetailsPage } from '@app/wot/wot-details.page'; import { AppTransferModule } from '@app/transfer/send/transfer.module'; import { AppAccountModule } from '@app/account/account.module'; +import { WotController } from './wot.controller'; +import { APP_WOT_CONTROLLER } from './wot.model'; @NgModule({ imports: [AppSharedModule, TranslateModule.forChild(), AppTransferModule, AppAccountModule], @@ -13,7 +15,11 @@ import { AppAccountModule } from '@app/account/account.module'; exports: [WotLookupPage, WotDetailsPage], }) export class AppWotModule { - constructor() { + static forRoot(): ModuleWithProviders<AppTransferModule> { console.debug('[wot] Creating module'); + return { + ngModule: AppWotModule, + providers: [WotController, { provide: APP_WOT_CONTROLLER, useExisting: WotController }], + }; } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index a484450d94fd753e925cef630101b9a1607aa4c2..fcdf4511ce70cc6d7e73ffb1442846fb91506df6 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -356,6 +356,7 @@ "NO_CERTIFICATION": "No validated certification", "NO_GIVEN_CERTIFICATION": "No given certification", "NOT_MEMBER_PARENTHESIS": "(non-member)", + "IDENTITY_UNCONFIRMED": "Identity <b>not confirmed</b> (non membre)", "IDENTITY_REVOKED_PARENTHESIS": "(identity revoked)", "MEMBER_PENDING_REVOCATION_PARENTHESIS": "(being revoked)", "EXPIRE_IN": "Expires", @@ -417,6 +418,14 @@ "SENT": "Sent certifications", "SENT_BY": "Certifications sent by {{uid}}", "ERROR": "Sent certifications with error" + }, + "STATUS_ENUM": { + "MEMBER": "Member", + "NOTMEMBER": "Not member", + "REMOVED": "Not member", + "REVOKED": "Revoked identity", + "UNCONFIRMED": "Unconfirmed identity", + "UNVALIDATED": "Waiting validation" } }, "LOGIN": { @@ -431,6 +440,7 @@ "PASSWORD": "Password", "PASSWORD_HELP": "Password", "PUBKEY_HELP": "Public key or pseudonym", + "MNEMONIC": "Mnemonic phrase", "NO_ACCOUNT_QUESTION": "Don't have an account yet?", "HAVE_ACCOUNT_QUESTION": "Already have an account ?", "CREATE_ACCOUNT": "Create an account", @@ -453,7 +463,9 @@ "SCRYPT_ADVANCED": "Advanced salt", "FILE": "Keychain file", "PUBKEY": "Public key or pseudonym", - "SCAN": "Scan a QR code" + "ADDRESS": "SS58 Address", + "SCAN": "Scan a QR code", + "MNEMONIC": "Mnemonic phrase" }, "SCRYPT": { "SIMPLE": "Light salt", @@ -505,6 +517,7 @@ "CERTIFICATION_COUNT": "Received certifications", "CERTIFICATION_COUNT_SHORT": "Certifications", "SIG_STOCK": "Stock of certifications to give", + "BTN_CONFIRM_MEMBERSHIP": "Confirm membership", "BTN_RECEIVE_MONEY": "Receive", "BTN_SELECT_ALTERNATIVES_IDENTITIES": "Switch to another identity...", "BTN_FIX_MEMBERSHIP": "Resubmit membership request...", @@ -569,6 +582,7 @@ "WALLET_LIST": { "TITLE": "My wallets", "BTN_NEW": "Add a wallet", + "BTN_NEW_DOTS": "Add a wallet...", "BTN_DOWNLOAD": "Download the list", "BTN_IMPORT_FILE_DOTS": "Import from a file...", "NO_WALLET": "No secondary wallet", @@ -672,11 +686,15 @@ "AMOUNT_HELP": "Amount", "COMMENT": "Comment", "COMMENT_HELP": "Comment (optional)", + "FEE": "+{{fee}} {{currency}} fees", "BTN_SEND": "Send", "BTN_ADD_COMMENT": "Add a comment", "REST": "Rest of account", "REST_TO": "to", "WARN_COMMENT_IS_PUBLIC": "Please note that <b>comments are public</b> (not encrypted).", + "BTN_SCAN_HELP": "Scan a QR Code", + "BTN_QR_CODE": "QR Code", + "QR_CODE_HELP": "Ask the recipient to flash this code. They can then deposit the payment later, whenever they want.", "MODAL": { "TITLE": "Transfer" } @@ -731,6 +749,7 @@ "NEED_LOGIN_FIRST": "Please sign in first.", "AMOUNT_REQUIRED": "Amount is required.", "AMOUNT_NEGATIVE": "Negative amount not allowed.", + "FEE_NEGATIVE": "Negative fee not allowed..", "NOT_ENOUGH_CREDIT": "Not enough credit.", "INVALID_NODE_SUMMARY": "Unreachable peer or invalid address", "INVALID_USER_ID": "Field 'pseudonym' must not contains spaces or special characters.", @@ -792,6 +811,7 @@ }, "INFO": { "POPUP_TITLE": "Information", + "CERTIFICATION_PENDING": "Certification sent. Waiting for validation...", "CERTIFICATION_DONE": "Identity successfully signed", "NOT_ENOUGH_CREDIT": "Not enough credit", "TRANSFER_SENT": "Transfer request successfully sent", @@ -929,98 +949,5 @@ "END_NOT_LOGIN": "This guided visit has <b>ended</b>.<br/><br/>If you wish to join the currency {{currency|capitalize}}, simply click <b>{{'LOGIN.CREATE_FREE_ACCOUNT'|translate}}</b> below.", "END_READONLY": "This guided visit has <b>ended</b>.<br/><br/>{{'MODE.READONLY.INSTALL_HELP'|translate}}." } - }, - "API": { - "COMMON": { - "LINK_DOC": "API documentation", - "LINK_DOC_HELP": "API documentation for developers", - "LINK_STANDARD_APP": "Standard version", - "LINK_STANDARD_APP_HELP": "Open standard version of {{'COMMON.APP_NAME'|translate}}", - "CONNECTION_ERROR": "Peer <b>{{server}}</b> unreachable or invalid address.<br/><br/>Check your Internet connection, or contact the web site administrator." - }, - "HOME": { - "TITLE": "{{'COMMON.APP_NAME'|translate}} API Documentation", - "MESSAGE": "Welcome to the {{'COMMON.APP_NAME'|translate}} <b>API documentation </b>.<br/>Connect your web site to <a href=\"http://duniter.org\" target=\"_system\">Duniter</a> very easily!", - "MESSAGE_SHORT": "Connect your websites to <a href=\"http://duniter.org\" target=\"_system\">Duniter</a> very easily!", - "DOC_HEADER": "Available services:" - }, - "TRANSFER": { - "TITLE": "{{'COMMON.APP_NAME'|translate}} - Online payment", - "TITLE_SHORT": "Online payment", - "SUMMARY": "Order summary:", - "AMOUNT": "Amount:", - "AMOUNTS_HELP": "Please select the amount:", - "NAME": "Name:", - "PUBKEY": "Public key of the recipient:", - "COMMENT": "Order reference:", - "NODE": "Peer address:", - "DEMO": { - "SALT": "demo", - "PASSWORD": "demo", - "PUBKEY": "3G28bL6deXQBYpPBpLFuECo46d3kfYMJwst7uhdVBnD1", - "HELP": "<b>Demonstration mode</b>: No payment will actually be sent during this simulation.<br/>Please use credentials: <b>{{'API.TRANSFER.DEMO.SALT'|translate}} / {{'API.TRANSFER.DEMO.PASSWORD'|translate}}</b>", - "BAD_CREDENTIALS": "Invalid credentials.<br/>In demonstration mode, credentials should be: {{'API.TRANSFER.DEMO.SALT'|translate}} / {{'API.TRANSFER.DEMO.PASSWORD'|translate}}" - }, - "INFO": { - "SUCCESS_REDIRECTING_WITH_NAME": "Payment sent.<br/>Redirect to <b>{{name}}</b>...", - "SUCCESS_REDIRECTING": "Payment sent.<br/>Redirect to the seller's website...", - "CANCEL_REDIRECTING_WITH_NAME": "Payment cancelled.<br/>Redirect to <b>{{name}}</b>...", - "CANCEL_REDIRECTING": "Payment cancelled.<br/>Redirect to the seller's website..." - }, - "ERROR": { - "TRANSFER_FAILED": "Payment failed" - } - }, - "DOC": { - "DESCRIPTION_DIVIDER": "Description", - "URL_DIVIDER": "Calling address", - "PARAMETERS_DIVIDER": "Parameters", - "AVAILABLE_PARAMETERS": "Here is the list of al available parameters:", - "DEMO_DIVIDER": "Try it !", - "DEMO_HELP": "To test this service, click on this button. The result content will be display below.", - "DEMO_RESULT": "Result returned by call:", - "DEMO_RESULT_PEER": "Peer address used:", - "DEMO_SUCCEED": "<i class=\"icon ion-checkmark\"></i> Success!", - "DEMO_CANCELLED": "<i class=\"icon ion-close\"></i> Canceled by user", - "INTEGRATE_DIVIDER": "Website integration", - "INTEGRATE_CODE": "Code:", - "INTEGRATE_RESULT": "Result preview:", - "INTEGRATE_PARAMETERS": "Parameters", - "TRANSFER": { - "TITLE": "Payments", - "DESCRIPTION": "From a site (eg online marketplace) you can delegate payment in free currency to Cesium API. To do this, simply open a page at the following address:", - "PARAM_PUBKEY": "Recipient's public key", - "PARAM_PUBKEY_HELP": "Recipient's public key (required)", - "PARAM_AMOUNT": "Amount", - "PARAM_AMOUNT_HELP": "Transaction amount (required)", - "PARAM_COMMENT": "Reference (or comment)", - "PARAM_COMMENT_HELP": "Reference or comment. You will allow for example to identify the payment in the BlockChain.", - "PARAM_NAME": "Name (of recipient or website)", - "PARAM_NAME_HELP": "The name of your website. This can be a readable name (eg \"My online site\"), or a web address (eg \"www.MySite.com\").", - "PARAM_REDIRECT_URL": "URL redirection", - "PARAM_REDIRECT_URL_HELP": "URL redirection after sending payment, after the payment has been sent. Can contain the following strings, which will be replaced by the values of the transaction: \"{tx}\", \"{hash}\", \"{comment}\", \"{amount}\" and \"{pubkey}\".", - "PARAM_CANCEL_URL": "URL if cancelled", - "PARAM_CANCEL_URL_HELP": "URL in case of cancellation. Can contain the following strings, which will be replaced: \"{comment}\", \"{amount}\" and \"{pubkey}\".", - "PARAM_PREFERRED_NODE": "Preferred Duniter peer", - "PARAM_PREFERRED_NODE_HELP": "Peer address (URL) to use preferably (\"g1.domain.com:443\" or \"https://g1.domain.com\")", - "EXAMPLES_HELP": "Examples of integration:", - "EXAMPLE_BUTTON": "HTML Button", - "EXAMPLE_BUTTON_DEFAULT_TEXT": "Pay in {{currency}}", - "EXAMPLE_BUTTON_DEFAULT_STYLE": "Custom style", - "EXAMPLE_BUTTON_TEXT_HELP": "Button text", - "EXAMPLE_BUTTON_BG_COLOR": "Background color", - "EXAMPLE_BUTTON_BG_COLOR_HELP": "eg: #fbc14c, yellow, lightgrey, rgb(180,180,180)", - "EXAMPLE_BUTTON_FONT_COLOR": "Font color", - "EXAMPLE_BUTTON_FONT_COLOR_HELP": "eg: black, orange, rgb(180,180,180)", - "EXAMPLE_BUTTON_TEXT_ICON": "Icon", - "EXAMPLE_BUTTON_TEXT_WIDTH": "Width", - "EXAMPLE_BUTTON_TEXT_WIDTH_HELP": "eg: 200px, 50%", - "EXAMPLE_BUTTON_ICON_NONE": "No icon", - "EXAMPLE_BUTTON_ICON_DUNITER": "Duniter logo", - "EXAMPLE_BUTTON_ICON_CESIUM": "Cesium logo", - "EXAMPLE_BUTTON_ICON_G1_COLOR": "Ğ1 logo", - "EXAMPLE_BUTTON_ICON_G1_BLACK": "Ğ1 logo (outline)" - } - } } } diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 7a405c784ab26dc9d48e3be544ffba33b3fe1247..47d6ba2030a10f06161f6ce8f877faf26bb1e4a5 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -38,6 +38,7 @@ "BTN_START": "Commencer", "BTN_CONTINUE": "Continuer", "BTN_CREATE": "Créer", + "BTN_CONFIRM": "Confirmer", "BTN_UNDERSTOOD": "J'ai compris", "BTN_OPTIONS": "Options", "BTN_HELP_TOUR": "Visite guidée", @@ -363,6 +364,7 @@ "NO_CERTIFICATION": "Aucune certification validée", "NO_GIVEN_CERTIFICATION": "Aucune certification émise", "NOT_MEMBER_PARENTHESIS": "(non membre)", + "IDENTITY_UNCONFIRMED": "Demande d'adhésion <b>non confirmée</b>", "IDENTITY_REVOKED_PARENTHESIS": "(identité révoquée)", "MEMBER_PENDING_REVOCATION_PARENTHESIS": "(en cours de révocation)", "EXPIRE_IN": "Expiration", @@ -424,6 +426,14 @@ "SENT": "Certifications émises", "SENT_BY": "Certifications émises par {{uid}}", "ERROR": "Certifications émises en erreur" + }, + "STATUS_ENUM": { + "MEMBER": "Membre", + "NOTMEMBER": "Non membre", + "REMOVED": "Non membre", + "REVOKED": "Identité révoquée", + "UNCONFIRMED": "Non confirmé", + "UNVALIDATED": "En attente de validation" } }, "LOGIN": { @@ -518,6 +528,7 @@ "CERTIFICATION_COUNT": "Certifications reçues", "CERTIFICATION_COUNT_SHORT": "Certifications", "SIG_STOCK": "Certifications envoyées", + "BTN_CONFIRM_MEMBERSHIP": "Confirmer mon adhésion", "BTN_RECEIVE_MONEY": "Encaisser", "BTN_SELECT_ALTERNATIVES_IDENTITIES": "Basculer vers une autre identité...", "BTN_FIX_MEMBERSHIP": "Renvoyer la demande d'adhésion...", @@ -573,9 +584,9 @@ "INPUT_WORD": "Mot n°{{number}}", "YOUR_SECRET_CODE": "Votre code secret" }, - "POPUP_REGISTER": { - "TITLE": "Choisissez un pseudonyme", - "HELP": "Un pseudonyme est obligatoire pour devenir membre." + "CONFIRM_IDENTITY": { + "TITLE": "Confirmer mon adhésion", + "PSEUDO_HELP": "Choisissez un pseudonyme.<br/>Il sert aux autres membres, pour vous identifier plus facilement.<div class='hidden-xs'><br/>Il <b>ne pourra pas être modifié</b>, sans refaire un compte.</div><br/><br/>Il ne doit contenir <b>ni espace, ni de caractère accentué</b>.<div class='hidden-xs'><br/>Exemple : <span class='gray'>SophieDupond, MarcelChemin, etc.</span>" }, "SELECT_IDENTITY_MODAL": { "TITLE": "Sélection de l'identité", @@ -692,7 +703,6 @@ "COMMENT": "Commentaire", "COMMENT_HELP": "Commentaire", "FEE": "+{{fee}} {{currency}} frais", - "BTN_SEND": "Envoyer", "BTN_ADD_COMMENT": "Ajouter un commentaire", "REST": "Reste du compte", "REST_TO": "à", @@ -722,8 +732,23 @@ "FIELD_MAX": "Valeur maximale : {{max}}", "FIELD_ACCENT": "Caractères accentués et virgules non autorisés", "FIELD_NOT_NUMBER": "Valeur numérique attendue", + "FIELD_MAXIMUM_DECIMALS": "Trop de décimales (max : {{maxDecimals}})", + "FIELD_NOT_VALID_DECIMAL": "Valeur décimale attendue", "FIELD_NOT_INT": "Valeur entière attendue", "FIELD_NOT_EMAIL": "Adresse email non valide", + "FIELD_NOT_VALID_DATE": "Date invalide", + "FIELD_NOT_VALID_DATE_AFTER": "Doit être postérieure à {{minDate}}", + "FIELD_NOT_VALID_DATE_BEFORE": "Doit être antérieure à {{maxDate}}", + "FIELD_NOT_VALID_DATE_RANGE": "Dates incohérentes", + "FIELD_NOT_VALID_DATE_MAX_DURATION": "Durée trop longue", + "FIELD_NOT_VALID_DATE_MIN_DURATION": "Durée trop courte", + "FIELD_NOT_VALID_HOUR_MINUTE": "Heure/minute invalide", + "FIELD_NOT_VALID_DATE_TIME": "Date/heure invalide", + "FIELD_NOT_VALID_LATITUDE": "Latitude invalide", + "FIELD_NOT_VALID_LONGITUDE": "Longitude invalide", + "FIELD_NOT_VALID_PATTERN": "Format incorrect", + "FIELD_NOT_VALID_PUBKEY": "Clé publique invalide", + "FIELD_NOT_VALID_ADDRESS": "Addresse SS58 invalide", "PASSWORD_NOT_CONFIRMED": "Ne correspond pas au mot de passe", "SALT_NOT_CONFIRMED": "Ne correspond pas à l'identifiant secret", "SEND_IDENTITY_FAILED": "Échec de l'inscription", @@ -757,7 +782,7 @@ "FEE_NEGATIVE": "Frais négatif non autorisé.", "NOT_ENOUGH_CREDIT": "Crédit insuffisant.", "INVALID_NODE_SUMMARY": "Nœud injoignable ou adresse invalide.", - "INVALID_USER_ID": "Le pseudonyme ne doit contenir ni espace ni caractère spécial ou accentué.", + "INVALID_USER_ID": "Ne doit contenir ni espace ni caractère spécial ou accentué.", "INVALID_COMMENT": "Le champ 'référence' ne doit pas contenir de caractères accentués.", "INVALID_PUBKEY": "La clé publique n'a pas le format attendu.", "INVALID_PUBKEY_CHECKSUM": "Somme de contrôle invalide.", @@ -818,6 +843,8 @@ "POPUP_TITLE": "Information", "CERTIFICATION_PENDING": "Certification envoyée. En attente de validation...", "CERTIFICATION_DONE": "Certification validée", + "CONFIRM_IDENTITY_PENDING": "Confirmation d'adhésion envoyée. En attente de validation...", + "CONFIRM_IDENTITY_DONE": "Confirmation d'adhésion validée", "NOT_ENOUGH_CREDIT": "Crédit insuffisant", "TRANSFER_SENT": "Virement envoyé", "COPY_TO_CLIPBOARD_DONE": "Copié dans le presse-papier", @@ -956,98 +983,5 @@ "END_NOT_LOGIN": "Cette visite guidée est <b>terminée</b> !<br/><br/>Si vous souhaitez rejoindre la monnaie {{currency|capitalize}}, il vous suffira de cliquer sur <b>{{'LOGIN.CREATE_FREE_ACCOUNT'|translate}}</b> ci-dessous.", "END_READONLY": "Cette visite guidée est <b>terminée</b>.<br/><br/>{{'MODE.READONLY.INSTALL_HELP'|translate}}." } - }, - "API": { - "COMMON": { - "LINK_DOC": "Documentation API", - "LINK_DOC_HELP": "Documentation pour les développeurs", - "LINK_STANDARD_APP": "Version classique", - "LINK_STANDARD_APP_HELP": "Ouvrir la version classique de {{'COMMON.APP_NAME'|translate}}", - "CONNECTION_ERROR": "Nœud <b>{{server}}</b> injoignable ou adresse invalide.<br/><br/>Vérifiez votre connexion Internet, ou contactez l'administrateur du site." - }, - "HOME": { - "TITLE": "Documentation API {{'COMMON.APP_NAME'|translate}}", - "MESSAGE": "Bienvenue dans la <b>documentation de l'API</b> {{'COMMON.APP_NAME'|translate}}.<br/>Connectez vos sites web à <a href=\"http://duniter.org\" target=\"_system\">Duniter</a> très simplement !", - "MESSAGE_SHORT": "Connectez vos sites à <a href=\"http://duniter.org\" target=\"_system\">Duniter</a> très simplement !", - "DOC_HEADER": "Services disponibles :" - }, - "TRANSFER": { - "TITLE": "{{'COMMON.APP_NAME'|translate}} - Paiement en ligne", - "TITLE_SHORT": "Paiement en ligne", - "SUMMARY": "Récapitulatif du paiement :", - "AMOUNT": "Montant :", - "AMOUNTS_HELP": "Veuillez choisir le montant :", - "NAME": "Nom :", - "PUBKEY": "Clé publique du destinataire :", - "COMMENT": "Référence de l'opération :", - "NODE": "Adresse du nœud :", - "DEMO": { - "SALT": "demo", - "PASSWORD": "demo", - "PUBKEY": "3G28bL6deXQBYpPBpLFuECo46d3kfYMJwst7uhdVBnD1", - "HELP": "<b>Mode démonstration</b> : Aucun paiement ne sera réellement envoyé pendant cette simulation.<br/>Veuillez utiliser les identifiants : <b>{{'API.TRANSFER.DEMO.SALT'|translate}} / {{'API.TRANSFER.DEMO.PASSWORD'|translate}}</b>", - "BAD_CREDENTIALS": "Vérifiez votre saisie.<br/>En mode démonstration, les identifiants sont : {{'API.TRANSFER.DEMO.SALT'|translate}} / {{'API.TRANSFER.DEMO.PASSWORD'|translate}}" - }, - "INFO": { - "SUCCESS_REDIRECTING_WITH_NAME": "Paiement envoyé.<br/>Redirection vers <b>{{name}}</b>...", - "SUCCESS_REDIRECTING": "Paiement envoyé.<br/>Redirection vers le site du vendeur...", - "CANCEL_REDIRECTING_WITH_NAME": "Paiement annulé.<br/>Redirection vers <b>{{name}}</b>...", - "CANCEL_REDIRECTING": "Paiement annulé.<br/>Redirection vers le site du vendeur..." - }, - "ERROR": { - "TRANSFER_FAILED": "Échec du paiement" - } - }, - "DOC": { - "DESCRIPTION_DIVIDER": "Description", - "URL_DIVIDER": "Adresse d'appel", - "PARAMETERS_DIVIDER": "Paramètres", - "AVAILABLE_PARAMETERS": "Voici la liste des paramètres possibles :", - "DEMO_DIVIDER": "Tester", - "DEMO_HELP": "Pour tester ce service, cliquez sur le bouton ci-contre. Le résultat s'affichera en dessous.", - "DEMO_RESULT": "Résultat retourné par l'appel :", - "DEMO_RESULT_PEER": "Adresse du nœud utilisé :", - "DEMO_SUCCEED": "<i class=\"icon ion-checkmark\"></i> Succès !", - "DEMO_CANCELLED": "<i class=\"icon ion-close\"></i> Annulé par l'utilisateur", - "INTEGRATE_DIVIDER": "Intégrer", - "INTEGRATE_CODE": "Code :", - "INTEGRATE_RESULT": "Prévisualisation du résultat :", - "INTEGRATE_PARAMETERS": "Paramètres", - "TRANSFER": { - "TITLE": "Paiements", - "DESCRIPTION": "Depuis un site (ex: vente en ligne) vous pouvez déléguer le paiement en monnaie libre à Cesium API. Pour cela, il vous suffit de déclencher l'ouverture d'une page sur l'adresse suivante :", - "PARAM_PUBKEY": "Clé publique du destinataire", - "PARAM_PUBKEY_HELP": "Clé publique du destinataire (obligatoire)", - "PARAM_AMOUNT": "Montant", - "PARAM_AMOUNT_HELP": "Montant de la transaction (obligatoire). Valeurs multiples autorisées, en utilisant un séparateur (point-virgule, barre verticale ou espace).", - "PARAM_COMMENT": "Référence (ou commentaire)", - "PARAM_COMMENT_HELP": "Référence ou commentaire. Vous permettra par exemple d'identifier le paiement dans la BlockChain.", - "PARAM_NAME": "Nom (du destinataire ou du site web)", - "PARAM_NAME_HELP": "Le nom du destinataire, ou du site web appelant. Cela peut être un nom lisible (\"Mon site en ligne\"), ou encore une pseudo-adresse web (\"MonSite.com\").", - "PARAM_REDIRECT_URL": "Adresse web de redirection", - "PARAM_REDIRECT_URL_HELP": "Adresse web (URL) de redirection, appelé quand le paiement a été envoyé. Peut contenir les chaînes suivantes, qui seront remplacées par les valeurs de la transaction : \"{tx}\", \"{hash}\", \"{comment}\", \"{amount}\", \"{pubkey}\" et \"{node}\".", - "PARAM_CANCEL_URL": "Adresse web d'annulation", - "PARAM_CANCEL_URL_HELP": "Adresse web (URL) en cas d'annulation du paiement, par l'utilisateur. Peut contenir les chaînes suivantes, qui seront remplacées dynamiquement : \"{comment}\", \"{amount}\" et \"{pubkey}\".", - "PARAM_PREFERRED_NODE": "Adresse du nœud préféré", - "PARAM_PREFERRED_NODE_HELP": "Adresse (URL) du nœud Duniter à utiliser de préférence (\"g1.domaine.com:443\" ou \"https://g1.domaine.com\").", - "EXAMPLES_HELP": "Voici des exemples d'intégration :", - "EXAMPLE_BUTTON": "Bouton HTML", - "EXAMPLE_BUTTON_DEFAULT_TEXT": "Payer en {{currency|currencySymbol}}", - "EXAMPLE_BUTTON_DEFAULT_STYLE": "Style personnalisé", - "EXAMPLE_BUTTON_TEXT_HELP": "Texte du bouton", - "EXAMPLE_BUTTON_BG_COLOR": "Couleur du fond", - "EXAMPLE_BUTTON_BG_COLOR_HELP": "Exemple : #fbc14c, black, lightgrey, rgb(180,180,180)", - "EXAMPLE_BUTTON_FONT_COLOR": "Couleur du texte", - "EXAMPLE_BUTTON_FONT_COLOR_HELP": "Exemple : black, orange, rgb(180,180,180)", - "EXAMPLE_BUTTON_TEXT_ICON": "Icône", - "EXAMPLE_BUTTON_TEXT_WIDTH": "Largeur", - "EXAMPLE_BUTTON_TEXT_WIDTH_HELP": "Exemple : 200px, 50%", - "EXAMPLE_BUTTON_ICON_NONE": "Aucune", - "EXAMPLE_BUTTON_ICON_DUNITER": "Logo Duniter", - "EXAMPLE_BUTTON_ICON_CESIUM": "Logo Cesium", - "EXAMPLE_BUTTON_ICON_G1_COLOR": "Logo Ğ1", - "EXAMPLE_BUTTON_ICON_G1_BLACK": "Logo Ğ1 (noir)" - } - } } } diff --git a/src/manifest.json b/src/manifest.json index a3ef3272675f80add0293f65ff4054c39b7cadc1..e4374c48cc8c3d270e348969dfe05b945bc33a36 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-alpha41", + "version": "2.0.0-alpha42", "default_locale": "fr", "description": "Cesium Wallet for Ğ1 libre currency", "icons": [ diff --git a/src/theme/_cesium.scss b/src/theme/_cesium.scss index a1f3690755e9c6f8884ae91de42f9e645add2aa6..3f430b3a6955ae8135fc78f374ebd58bd87b0d85 100644 --- a/src/theme/_cesium.scss +++ b/src/theme/_cesium.scss @@ -20,6 +20,14 @@ visibility: hidden; } +ion-buttons[slot='end'].vertical-alignment { + flex-direction: column; + ion-note { + width: 100%; + text-align: end; + } +} + /* -- error message -- */ .item.error, ion-item.error { diff --git a/src/zone-flags.ts b/src/zone-flags.ts index 1a45f391c5141d06d4222a67710624731ff2a8ac..ad0be3db93307d2e3e945566aa6bce2c46039624 100644 --- a/src/zone-flags.ts +++ b/src/zone-flags.ts @@ -10,17 +10,13 @@ zoneConfig.global.disable.requestAnimationFrame(); zoneConfig.global.disable.geolocation(); zoneConfig.global.disable.canvas(); zoneConfig.global.disable.XHR(); +zoneConfig.global.disable.ZoneAwarePromise(); zoneConfig.events.disable.UNPATCHED_EVENTS([ - 'mousemove', - 'mouseover', - // TODO: check if can disabled this events: - 'scroll', + //'mousemove', + //'mouseover', + //'scroll', ]); -// FIXME disable zone in .then() functions -//zoneConfig.global.disable.ZoneAwarePromise(); - -// FIXME: need to patch progression toolbar, to call markForCheck() -// Otherwise, the trip editor still show the loading bar +// FIXME: cannot disable timers for now (execution error at startup) //zoneConfig.global.disable.timers();