diff --git a/docs/compodoc/classes/AccountIndex.html b/docs/compodoc/classes/AccountIndex.html new file mode 100644 index 0000000..004d7cc --- /dev/null +++ b/docs/compodoc/classes/AccountIndex.html @@ -0,0 +1,670 @@ + + +
+ + ++
+ src/app/_eth/accountIndex.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(contractAddress: string, signerAddress?: string)
+ |
+
+ Defined in src/app/_eth/accountIndex.ts:10
+ |
+
+ + | +
+ + + + contract + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_eth/accountIndex.ts:10
+ |
+
+ + + + contractAddress + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_eth/accountIndex.ts:8
+ |
+
+ + + + signerAddress + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_eth/accountIndex.ts:9
+ |
+
+ + + + Public + Async + addToAccountRegistry + + + + | +||||||
+
+ addToAccountRegistry(address: string)
+ |
+ ||||||
+ Defined in src/app/_eth/accountIndex.ts:44
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+
+ |
+
+ + + + Public + Async + haveAccount + + + + | +||||||
+
+ haveAccount(address: string)
+ |
+ ||||||
+ Defined in src/app/_eth/accountIndex.ts:26
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+
+ |
+
+ + + + Public + Async + last + + + + | +||||||
+
+ last(numberOfAccounts: number)
+ |
+ ||||||
+ Defined in src/app/_eth/accountIndex.ts:30
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<Array<string>>
+
+
+
+
+ |
+
+ + + + Public + Async + totalAccounts + + + + | +
+
+ totalAccounts()
+ |
+
+ Defined in src/app/_eth/accountIndex.ts:22
+ |
+
+
+
+ Returns :
+ Promise<number>
+
+ |
+
import {environment} from '@src/environments/environment';
+import Web3 from 'web3';
+
+const abi: Array<any> = require('@src/assets/js/block-sync/data/AccountRegistry.json');
+const web3: Web3 = new Web3(environment.web3Provider);
+
+export class AccountIndex {
+ contractAddress: string;
+ signerAddress: string;
+ contract: any;
+
+ constructor(contractAddress: string, signerAddress?: string) {
+ this.contractAddress = contractAddress;
+ this.contract = new web3.eth.Contract(abi, this.contractAddress);
+ if (signerAddress) {
+ this.signerAddress = signerAddress;
+ } else {
+ this.signerAddress = web3.eth.accounts[0];
+ }
+ }
+
+ public async totalAccounts(): Promise<number> {
+ return await this.contract.methods.count().call();
+ }
+
+ public async haveAccount(address: string): Promise<boolean> {
+ return await this.contract.methods.accountIndex(address).call() !== 0;
+ }
+
+ public async last(numberOfAccounts: number): Promise<Array<string>> {
+ const count: number = await this.totalAccounts();
+ let lowest: number = count - numberOfAccounts - 1;
+ if (lowest < 0) {
+ lowest = 0;
+ }
+ const accounts: Array<string> = [];
+ for (let i = count - 1; i > lowest; i--) {
+ const account: string = await this.contract.methods.accounts(i).call();
+ accounts.push(account);
+ }
+ return accounts;
+ }
+
+ public async addToAccountRegistry(address: string): Promise<boolean> {
+ if (!await this.haveAccount(address)) {
+ return await this.contract.methods.add(address).send({from: this.signerAddress});
+ }
+ return true;
+ }
+}
+
+ +
+ src/testing/activated-route-stub.ts
+
+
+
An ActivateRoute test double with a paramMap
observable.
+Use the setParamMap()
method to add the next paramMap
value.
+ Properties+ |
+
+ + | +
+ Methods+ |
+
+
|
+
+constructor(initialParams?: Params)
+ |
+ ||||||
+ Defined in src/testing/activated-route-stub.ts:11
+ |
+ ||||||
+
+ Parameters :
+
+
|
+
+ + + + Readonly + paramMap + + + | +
+ Default value : this.subject.asObservable()
+ |
+
+ Defined in src/testing/activated-route-stub.ts:18
+ |
+
+ The mock paramMap observable + |
+
+ + + + Private + subject + + + | +
+ Default value : new ReplaySubject<ParamMap>()
+ |
+
+ Defined in src/testing/activated-route-stub.ts:11
+ |
+
+ + + + setParamMap + + + + | +||||||
+setParamMap(params?: Params)
+ |
+ ||||||
+ Defined in src/testing/activated-route-stub.ts:21
+ |
+ ||||||
+ Set the paramMap observables's next value +
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
import { convertToParamMap, ParamMap, Params } from '@angular/router';
+import { ReplaySubject } from 'rxjs';
+
+/**
+ * An ActivateRoute test double with a `paramMap` observable.
+ * Use the `setParamMap()` method to add the next `paramMap` value.
+ */
+export class ActivatedRouteStub {
+ // Use a ReplaySubject to share previous values with subscribers
+ // and pump new values into the `paramMap` observable
+ private subject = new ReplaySubject<ParamMap>();
+
+ constructor(initialParams?: Params) {
+ this.setParamMap(initialParams);
+ }
+
+ /** The mock paramMap observable */
+ readonly paramMap = this.subject.asObservable();
+
+ /** Set the paramMap observables's next value */
+ setParamMap(params?: Params): void {
+ this.subject.next(convertToParamMap(params));
+ }
+}
+
+ +
+ e2e/src/app.po.ts
+
+ Methods+ |
+
+
|
+
+ + + + getTitleText + + + + | +
+getTitleText()
+ |
+
+ Defined in e2e/src/app.po.ts:8
+ |
+
+
+
+ Returns :
+ Promise<string>
+
+ |
+
+ + + + navigateTo + + + + | +
+navigateTo()
+ |
+
+ Defined in e2e/src/app.po.ts:4
+ |
+
+
+
+ Returns :
+ Promise<unknown>
+
+ |
+
import { browser, by, element } from 'protractor';
+
+export class AppPage {
+ navigateTo(): Promise<unknown> {
+ return browser.get(browser.baseUrl) as Promise<unknown>;
+ }
+
+ getTitleText(): Promise<string> {
+ return element(by.css('app-root .content span')).getText() as Promise<string>;
+ }
+}
+
+ +
+ src/app/_models/transaction.ts
+
+ Properties+ |
+
+
|
+
+ + + + alg + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_models/transaction.ts:7
+ |
+
+ + + + blockFilter + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_models/transaction.ts:5
+ |
+
+ + + + blocktxFilter + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_models/transaction.ts:6
+ |
+
+ + + + filterRounds + + + | +
+ Type : number
+
+ |
+
+ Defined in src/app/_models/transaction.ts:8
+ |
+
+ + + + low + + + | +
+ Type : number
+
+ |
+
+ Defined in src/app/_models/transaction.ts:4
+ |
+
import {AccountDetails} from '@app/_models/account';
+
+class BlocksBloom {
+ low: number;
+ blockFilter: string;
+ blocktxFilter: string;
+ alg: string;
+ filterRounds: number;
+}
+
+class TxToken {
+ address: string;
+ name: string;
+ symbol: string;
+}
+
+class Tx {
+ block: number;
+ success: boolean;
+ timestamp: number;
+ txHash: string;
+ txIndex: number;
+}
+
+class Transaction {
+ from: string;
+ sender: AccountDetails;
+ to: string;
+ recipient: AccountDetails;
+ token: TxToken;
+ tx: Tx;
+ value: number;
+ type?: string;
+}
+
+class Conversion {
+ destinationToken: TxToken;
+ fromValue: number;
+ sourceToken: TxToken;
+ toValue: number;
+ trader: string;
+ user: AccountDetails;
+ tx: Tx;
+}
+
+export {
+ BlocksBloom,
+ TxToken,
+ Tx,
+ Transaction,
+ Conversion
+};
+
+ +
+ src/app/_models/transaction.ts
+
+ Properties+ |
+
+
|
+
+ + + + destinationToken + + + | +
+ Type : TxToken
+
+ |
+
+ Defined in src/app/_models/transaction.ts:37
+ |
+
+ + + + fromValue + + + | +
+ Type : number
+
+ |
+
+ Defined in src/app/_models/transaction.ts:38
+ |
+
+ + + + sourceToken + + + | +
+ Type : TxToken
+
+ |
+
+ Defined in src/app/_models/transaction.ts:39
+ |
+
+ + + + toValue + + + | +
+ Type : number
+
+ |
+
+ Defined in src/app/_models/transaction.ts:40
+ |
+
+ + + + trader + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_models/transaction.ts:41
+ |
+
+ + + + tx + + + | +
+ Type : Tx
+
+ |
+
+ Defined in src/app/_models/transaction.ts:43
+ |
+
+ + + + user + + + | +
+ Type : AccountDetails
+
+ |
+
+ Defined in src/app/_models/transaction.ts:42
+ |
+
import {AccountDetails} from '@app/_models/account';
+
+class BlocksBloom {
+ low: number;
+ blockFilter: string;
+ blocktxFilter: string;
+ alg: string;
+ filterRounds: number;
+}
+
+class TxToken {
+ address: string;
+ name: string;
+ symbol: string;
+}
+
+class Tx {
+ block: number;
+ success: boolean;
+ timestamp: number;
+ txHash: string;
+ txIndex: number;
+}
+
+class Transaction {
+ from: string;
+ sender: AccountDetails;
+ to: string;
+ recipient: AccountDetails;
+ token: TxToken;
+ tx: Tx;
+ value: number;
+ type?: string;
+}
+
+class Conversion {
+ destinationToken: TxToken;
+ fromValue: number;
+ sourceToken: TxToken;
+ toValue: number;
+ trader: string;
+ user: AccountDetails;
+ tx: Tx;
+}
+
+export {
+ BlocksBloom,
+ TxToken,
+ Tx,
+ Transaction,
+ Conversion
+};
+
+ +
+ src/app/_helpers/custom-error-state-matcher.ts
+
+
+ ErrorStateMatcher
+
+ Methods+ |
+
+
|
+
+ + + + isErrorState + + + + | +|||||||||
+isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null)
+ |
+ |||||||||
+ + | +|||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ boolean
+
+
+
+
+ |
+
import {ErrorStateMatcher} from '@angular/material/core';
+import {FormControl, FormGroupDirective, NgForm} from '@angular/forms';
+
+export class CustomErrorStateMatcher implements ErrorStateMatcher{
+ isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
+ const isSubmitted: boolean = form && form.submitted;
+ return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
+ }
+}
+
+ +
+ src/app/_helpers/custom.validator.ts
+
+ Methods+ |
+
+
|
+
+ + + + Static + passwordMatchValidator + + + + | +||||||
+
+ passwordMatchValidator(control: AbstractControl)
+ |
+ ||||||
+ Defined in src/app/_helpers/custom.validator.ts:4
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + Static + patternValidator + + + + | +|||||||||
+
+ patternValidator(regex: RegExp, error: ValidationErrors)
+ |
+ |||||||||
+ Defined in src/app/_helpers/custom.validator.ts:12
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ ValidationErrors | null
+
+
+
+
+ |
+
import {AbstractControl, ValidationErrors} from '@angular/forms';
+
+export class CustomValidator {
+ static passwordMatchValidator(control: AbstractControl): void {
+ const password: string = control.get('password').value;
+ const confirmPassword: string = control.get('confirmPassword').value;
+ if (password !== confirmPassword) {
+ control.get('confirmPassword').setErrors({ NoPasswordMatch: true });
+ }
+ }
+
+ static patternValidator(regex: RegExp, error: ValidationErrors): ValidationErrors | null {
+ return (control: AbstractControl): { [key: string]: any } => {
+ if (!control.value) {
+ return null;
+ }
+
+ const valid: boolean = regex.test(control.value);
+ return valid ? null : error;
+ };
+ }
+}
+
+ +
+ src/app/_helpers/global-error-handler.ts
+
+
+ Error
+
+ Properties+ |
+
+
|
+
+constructor(message: string, status: number)
+ |
+
+ Defined in src/app/_helpers/global-error-handler.ts:8
+ |
+
+ + | +
+ + + + Public + status + + + | +
+ Type : number
+
+ |
+
+ Defined in src/app/_helpers/global-error-handler.ts:8
+ |
+
import {ErrorHandler, Injectable} from '@angular/core';
+import {LoggingService} from '@app/_services/logging.service';
+import {HttpErrorResponse} from '@angular/common/http';
+import {Router} from '@angular/router';
+
+// A generalized http response error
+export class HttpError extends Error {
+ public status: number;
+ constructor(message: string, status: number) {
+ super(message);
+ this.status = status;
+ this.name = 'HttpError';
+ }
+}
+
+@Injectable()
+export class GlobalErrorHandler extends ErrorHandler {
+ private sentencesForWarningLogging: Array<string> = [];
+
+ constructor(
+ private loggingService: LoggingService,
+ private router: Router
+ ) {
+ super();
+ }
+
+ handleError(error: Error): void {
+ this.logError(error);
+ const message: string = error.message ? error.message : error.toString();
+
+ // if (error.status) {
+ // error = new Error(message);
+ // }
+
+ const errorTraceString: string = `Error message:\n${message}.\nStack trace: ${error.stack}`;
+
+ const isWarning: boolean = this.isWarning(errorTraceString);
+ if (isWarning) {
+ this.loggingService.sendWarnLevelMessage(errorTraceString, {error});
+ } else {
+ this.loggingService.sendErrorLevelMessage(errorTraceString, this, {error});
+ }
+
+ throw error;
+ }
+
+ logError(error: any): void {
+ const route: string = this.router.url;
+ if (error instanceof HttpErrorResponse) {
+ this.loggingService.sendErrorLevelMessage(
+ `There was an HTTP error on route ${route}.\n${error.message}.\nStatus code: ${(error as HttpErrorResponse).status}`,
+ this, {error});
+ } else if (error instanceof TypeError) {
+ this.loggingService.sendErrorLevelMessage(`There was a Type error on route ${route}.\n${error.message}`, this, {error});
+ } else if (error instanceof Error) {
+ this.loggingService.sendErrorLevelMessage(`There was a general error on route ${route}.\n${error.message}`, this, {error});
+ } else {
+ this.loggingService.sendErrorLevelMessage(`Nobody threw an error but something happened on route ${route}!`, this, {error});
+ }
+ }
+
+ private isWarning(errorTraceString: string): boolean {
+ let isWarning: boolean = true;
+ if (errorTraceString.includes('/src/app/')) {
+ isWarning = false;
+ }
+
+ this.sentencesForWarningLogging.forEach((whiteListSentence: string) => {
+ if (errorTraceString.includes(whiteListSentence)) {
+ isWarning = true;
+ }
+ });
+
+ return isWarning;
+ }
+}
+
+ +
+ src/app/_pgp/pgp-key-store.ts
+
+
+ MutableKeyStore
+
+ Methods+ |
+
+
|
+
+ + + + clearKeysInKeyring + + + + | +
+clearKeysInKeyring()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:142
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + getEncryptKeys + + + + | +
+getEncryptKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:66
+ |
+
+
+
+ Returns :
+ Array<any>
+
+ |
+
+ + + + getFingerprint + + + + | +
+getFingerprint()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:94
+ |
+
+
+
+ Returns :
+ string
+
+ |
+
+ + + + getKeyId + + + + | +||||||
+getKeyId(key: any)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:100
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ string
+
+
+
+
+ |
+
+ + + + getKeysForId + + + + | +||||||
+getKeysForId(keyId: string)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:109
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Array<any>
+
+
+
+
+ |
+
+ + + + getPrivateKey + + + + | +
+getPrivateKey()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:74
+ |
+
+
+
+ Returns :
+ any
+
+ |
+
+ + + + getPrivateKeyForId + + + + | +||||
+getPrivateKeyForId(keyId)
+ |
+ ||||
+ Defined in src/app/_pgp/pgp-key-store.ts:117
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + getPrivateKeyId + + + + | +
+getPrivateKeyId()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:104
+ |
+
+
+
+ Returns :
+ string
+
+ |
+
+ + + + getPrivateKeys + + + + | +
+getPrivateKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:70
+ |
+
+
+
+ Returns :
+ Array<any>
+
+ |
+
+ + + + getPublicKeyForId + + + + | +||||
+getPublicKeyForId(keyId)
+ |
+ ||||
+ Defined in src/app/_pgp/pgp-key-store.ts:113
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + getPublicKeyForSubkeyId + + + + | +||||
+getPublicKeyForSubkeyId(subkeyId)
+ |
+ ||||
+ Defined in src/app/_pgp/pgp-key-store.ts:121
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + getPublicKeys + + + + | +
+getPublicKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:54
+ |
+
+
+
+ Returns :
+ Array<any>
+
+ |
+
+ + + + getPublicKeysForAddress + + + + | +||||
+getPublicKeysForAddress(address)
+ |
+ ||||
+ Defined in src/app/_pgp/pgp-key-store.ts:125
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Array<any>
+
+
+
+
+ |
+
+ + + + getTrustedActiveKeys + + + + | +
+getTrustedActiveKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:62
+ |
+
+
+
+ Returns :
+ Array<any>
+
+ |
+
+ + + + getTrustedKeys + + + + | +
+getTrustedKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:58
+ |
+
+
+
+ Returns :
+ Array<any>
+
+ |
+
+ + + + Async + importKeyPair + + + + | +|||||||||
+
+ importKeyPair(publicKey: any, privateKey: any)
+ |
+ |||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:41
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + Async + importPrivateKey + + + + | +||||||
+
+ importPrivateKey(privateKey: any)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:50
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + importPublicKey + + + + | +||||||
+importPublicKey(publicKey: any)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:46
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + Async + isEncryptedPrivateKey + + + + | +||||||
+
+ isEncryptedPrivateKey(privateKey: any)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:84
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+
+ |
+
+ + + + Async + isValidKey + + + + | +||||
+
+ isValidKey(key)
+ |
+ ||||
+ Defined in src/app/_pgp/pgp-key-store.ts:78
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+
+ |
+
+ + + + Async + loadKeyring + + + + | +
+
+ loadKeyring()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:36
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + removeKeysForId + + + + | +||||
+removeKeysForId(keyId)
+ |
+ ||||
+ Defined in src/app/_pgp/pgp-key-store.ts:129
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Array<any>
+
+
+
+
+ |
+
+ + + + removePublicKey + + + + | +||||||
+removePublicKey(publicKey: any)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:137
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + removePublicKeyForId + + + + | +||||
+removePublicKeyForId(keyId)
+ |
+ ||||
+ Defined in src/app/_pgp/pgp-key-store.ts:133
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + Async + sign + + + + | +||||
+
+ sign(plainText)
+ |
+ ||||
+ Defined in src/app/_pgp/pgp-key-store.ts:146
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<any>
+
+
+
+
+ |
+
import { KeyStore } from 'cic-client-meta';
+// TODO should we put this on the mutable key store object
+import * as openpgp from 'openpgp';
+const keyring = new openpgp.Keyring();
+
+interface MutableKeyStore extends KeyStore {
+ loadKeyring(): void;
+ importKeyPair(publicKey: any, privateKey: any): Promise<void>;
+ importPublicKey(publicKey: any): void;
+ importPrivateKey(privateKey: any): Promise<void>;
+ getPublicKeys(): Array<any>;
+ getTrustedKeys(): Array<any>;
+ getTrustedActiveKeys(): Array<any>;
+ getEncryptKeys(): Array<any>;
+ getPrivateKeys(): Array<any>;
+ getPrivateKey(): any;
+ isValidKey(key: any): Promise<boolean>;
+ isEncryptedPrivateKey(privateKey: any): Promise<boolean>;
+ getFingerprint(): string;
+ getKeyId(key: any): string;
+ getPrivateKeyId(): string;
+ getKeysForId(keyId: string): Array<any>;
+ getPublicKeyForId(keyId: string): any;
+ getPrivateKeyForId(keyId: string): any;
+ getPublicKeyForSubkeyId(subkeyId: string): any;
+ getPublicKeysForAddress(address: string): Array<any>;
+ removeKeysForId(keyId: string): Array<any>;
+ removePublicKeyForId(keyId: string): any;
+ removePublicKey(publicKey: any): any;
+ clearKeysInKeyring(): void;
+ sign(plainText: string): Promise<any>;
+}
+
+class MutablePgpKeyStore implements MutableKeyStore{
+
+ async loadKeyring(): Promise<void> {
+ await keyring.load();
+ await keyring.store();
+ }
+
+ async importKeyPair(publicKey: any, privateKey: any): Promise<void> {
+ await keyring.publicKeys.importKey(publicKey);
+ await keyring.privateKeys.importKey(privateKey);
+ }
+
+ importPublicKey(publicKey: any): void {
+ keyring.publicKeys.importKey(publicKey);
+ }
+
+ async importPrivateKey(privateKey: any): Promise<void> {
+ await keyring.privateKeys.importKey(privateKey);
+ }
+
+ getPublicKeys(): Array<any> {
+ return keyring.publicKeys.keys;
+ }
+
+ getTrustedKeys(): Array<any> {
+ return keyring.publicKeys.keys;
+ }
+
+ getTrustedActiveKeys(): Array<any> {
+ return keyring.publicKeys.keys;
+ }
+
+ getEncryptKeys(): Array<any> {
+ return [];
+ }
+
+ getPrivateKeys(): Array<any> {
+ return keyring.privateKeys.keys;
+ }
+
+ getPrivateKey(): any {
+ return keyring.privateKeys && keyring.privateKeys.keys[0];
+ }
+
+ async isValidKey(key): Promise<boolean> {
+ // There is supposed to be an openpgp.readKey() method but I can't find it?
+ const _key = await openpgp.key.readArmored(key);
+ return !_key.err;
+ }
+
+ async isEncryptedPrivateKey(privateKey: any): Promise<boolean> {
+ const imported = await openpgp.key.readArmored(privateKey);
+ for (const key of imported.keys) {
+ if (key.isDecrypted()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ getFingerprint(): string {
+ // TODO Handle multiple keys
+ return keyring.privateKeys && keyring.privateKeys.keys[0] && keyring.privateKeys.keys[0].keyPacket &&
+ keyring.privateKeys.keys[0].keyPacket.fingerprint;
+ }
+
+ getKeyId(key: any): string {
+ return key.getKeyId().toHex();
+ }
+
+ getPrivateKeyId(): string {
+ // TODO is there a library that comes with angular for doing this?
+ return keyring.privateKeys && keyring.privateKeys.keys[0] && keyring.privateKeys.keys[0].getKeyId().toHex();
+ }
+
+ getKeysForId(keyId: string): Array<any> {
+ return keyring.getKeysForId(keyId);
+ }
+
+ getPublicKeyForId(keyId): any {
+ return keyring.publicKeys.getForId(keyId);
+ }
+
+ getPrivateKeyForId(keyId): any {
+ return keyring.privateKeys.getForId(keyId);
+ }
+
+ getPublicKeyForSubkeyId(subkeyId): any {
+ return keyring.publicKeys.getForId(subkeyId, true);
+ }
+
+ getPublicKeysForAddress(address): Array<any> {
+ return keyring.publicKeys.getForAddress(address);
+ }
+
+ removeKeysForId(keyId): Array<any> {
+ return keyring.removeKeysForId(keyId);
+ }
+
+ removePublicKeyForId(keyId): any {
+ return keyring.publicKeys.removeForId(keyId);
+ }
+
+ removePublicKey(publicKey: any): any {
+ const keyId = publicKey.getKeyId().toHex();
+ return keyring.publicKeys.removeForId(keyId);
+ }
+
+ clearKeysInKeyring(): void {
+ keyring.clear();
+ }
+
+ async sign(plainText): Promise<any> {
+ const privateKey = this.getPrivateKey();
+ if (!privateKey.isDecrypted()) {
+ const password = window.prompt('password');
+ await privateKey.decrypt(password);
+ }
+ const opts = {
+ message: openpgp.message.fromText(plainText),
+ privateKeys: [privateKey],
+ detached: true,
+ };
+ const signatureObject = await openpgp.sign(opts);
+ return signatureObject.signature;
+ }
+}
+
+export {
+ MutablePgpKeyStore,
+ MutableKeyStore
+};
+
+ +
+ src/app/_pgp/pgp-signer.ts
+
+
+ Signer
+
+ Properties+ |
+
+ + | +
+ Methods+ |
+
+
|
+
+constructor(keyStore: MutableKeyStore)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-signer.ts:35
+ |
+ ||||||
+
+ Parameters :
+
+
|
+
+ + + + algo + + + | +
+ Type : string
+
+ |
+
+ Default value : 'sha256'
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:29
+ |
+
+ + + + dgst + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:30
+ |
+
+ + + + engine + + + | +
+ Type : string
+
+ |
+
+ Default value : 'pgp'
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:28
+ |
+
+ + + + keyStore + + + | +
+ Type : MutableKeyStore
+
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:32
+ |
+
+ + + + loggingService + + + | +
+ Type : LoggingService
+
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:35
+ |
+
+ + + + onsign + + + | +
+ Type : function
+
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:33
+ |
+
+ + + + onverify + + + | +
+ Type : function
+
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:34
+ |
+
+ + + + signature + + + | +
+ Type : Signature
+
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:31
+ |
+
+ + + + Public + fingerprint + + + + | +
+
+ fingerprint()
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:43
+ |
+
+
+
+ Returns :
+ string
+
+ |
+
+ + + + Public + prepare + + + + | +||||||
+
+ prepare(material: Signable)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-signer.ts:47
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ boolean
+
+
+
+
+ |
+
+ + + + Public + Async + sign + + + + | +||||||
+
+ sign(digest: string)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-signer.ts:77
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + Public + verify + + + + | +|||||||||
+
+ verify(digest: string, signature: Signature)
+ |
+ |||||||||
+ Defined in src/app/_pgp/pgp-signer.ts:52
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
import {MutableKeyStore} from '@app/_pgp/pgp-key-store';
+import {LoggingService} from '@app/_services/logging.service';
+
+const openpgp = require('openpgp');
+
+interface Signable {
+ digest(): string;
+}
+
+type Signature = {
+ engine: string
+ algo: string
+ data: string
+ digest: string;
+};
+
+interface Signer {
+ onsign(signature: Signature): void;
+ onverify(flag: boolean): void;
+ fingerprint(): string;
+ prepare(material: Signable): boolean;
+ verify(digest: string, signature: Signature): void;
+ sign(digest: string): Promise<void>;
+}
+
+class PGPSigner implements Signer {
+
+ engine = 'pgp';
+ algo = 'sha256';
+ dgst: string;
+ signature: Signature;
+ keyStore: MutableKeyStore;
+ onsign: (signature: Signature) => void;
+ onverify: (flag: boolean) => void;
+ loggingService: LoggingService;
+
+ constructor(keyStore: MutableKeyStore) {
+ this.keyStore = keyStore;
+ this.onsign = (signature: Signature) => {};
+ this.onverify = (flag: boolean) => {};
+ }
+
+ public fingerprint(): string {
+ return this.keyStore.getFingerprint();
+ }
+
+ public prepare(material: Signable): boolean {
+ this.dgst = material.digest();
+ return true;
+ }
+
+ public verify(digest: string, signature: Signature): void {
+ openpgp.signature.readArmored(signature.data).then((sig) => {
+ const opts = {
+ message: openpgp.cleartext.fromText(digest),
+ publicKeys: this.keyStore.getTrustedKeys(),
+ signature: sig,
+ };
+ openpgp.verify(opts).then((v) => {
+ let i = 0;
+ for (i = 0; i < v.signatures.length; i++) {
+ const s = v.signatures[i];
+ if (s.valid) {
+ this.onverify(s);
+ return;
+ }
+ }
+ this.loggingService.sendErrorLevelMessage(`Checked ${i} signature(s) but none valid`, this, {error: '404 Not found!'});
+ this.onverify(false);
+ });
+ }).catch((e) => {
+ this.loggingService.sendErrorLevelMessage(e.message, this, {error: e});
+ this.onverify(false);
+ });
+ }
+
+ public async sign(digest: string): Promise<void> {
+ const m = openpgp.cleartext.fromText(digest);
+ const pk = this.keyStore.getPrivateKey();
+ if (!pk.isDecrypted()) {
+ const password = window.prompt('password');
+ await pk.decrypt(password);
+ }
+ const opts = {
+ message: m,
+ privateKeys: [pk],
+ detached: true,
+ };
+ openpgp.sign(opts).then((s) => {
+ this.signature = {
+ engine: this.engine,
+ algo: this.algo,
+ data: s.signature,
+ // TODO: fix for browser later
+ digest,
+ };
+ this.onsign(this.signature);
+ }).catch((e) => {
+ this.loggingService.sendErrorLevelMessage(e.message, this, {error: e});
+ this.onsign(undefined);
+ });
+ }
+}
+
+export {
+ Signable,
+ Signature,
+ Signer,
+ PGPSigner
+};
+
+ +
+ src/app/_models/settings.ts
+
+ Properties+ |
+
+
|
+
+constructor(scanFilter: any)
+ |
+ ||||||
+ Defined in src/app/_models/settings.ts:8
+ |
+ ||||||
+
+ Parameters :
+
+
|
+
+ + + + registry + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_models/settings.ts:7
+ |
+
+ + + + scanFilter + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_models/settings.ts:6
+ |
+
+ + + + txHelper + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_models/settings.ts:8
+ |
+
+ + + + w3 + + + | +
+ Type : W3
+
+ |
+
+ Default value : {
+ engine: undefined,
+ provider: undefined,
+ }
+ |
+
+ Defined in src/app/_models/settings.ts:2
+ |
+
class Settings {
+ w3: W3 = {
+ engine: undefined,
+ provider: undefined,
+ };
+ scanFilter: any;
+ registry: any;
+ txHelper: any;
+
+ constructor(scanFilter: any) {
+ this.scanFilter = scanFilter;
+ }
+}
+
+class W3 {
+ engine: any;
+ provider: any;
+}
+
+export {
+ Settings,
+ W3
+};
+
+ +
+ src/app/_eth/token-registry.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(contractAddress: string, signerAddress?: string)
+ |
+
+ Defined in src/app/_eth/token-registry.ts:10
+ |
+
+ + | +
+ + + + contract + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_eth/token-registry.ts:10
+ |
+
+ + + + contractAddress + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_eth/token-registry.ts:8
+ |
+
+ + + + signerAddress + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_eth/token-registry.ts:9
+ |
+
+ + + + Public + Async + addressOf + + + + | +||||||
+
+ addressOf(identifier: string)
+ |
+ ||||||
+ Defined in src/app/_eth/token-registry.ts:30
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<string>
+
+
+
+
+ |
+
+ + + + Public + Async + entry + + + + | +||||||
+
+ entry(serial: number)
+ |
+ ||||||
+ Defined in src/app/_eth/token-registry.ts:26
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<string>
+
+
+
+
+ |
+
+ + + + Public + Async + totalTokens + + + + | +
+
+ totalTokens()
+ |
+
+ Defined in src/app/_eth/token-registry.ts:22
+ |
+
+
+
+ Returns :
+ Promise<number>
+
+ |
+
import Web3 from 'web3';
+import {environment} from '@src/environments/environment';
+
+const abi: Array<any> = require('@src/assets/js/block-sync/data/TokenUniqueSymbolIndex.json');
+const web3: Web3 = new Web3(environment.web3Provider);
+
+export class TokenRegistry {
+ contractAddress: string;
+ signerAddress: string;
+ contract: any;
+
+ constructor(contractAddress: string, signerAddress?: string) {
+ this.contractAddress = contractAddress;
+ this.contract = new web3.eth.Contract(abi, this.contractAddress);
+ if (signerAddress) {
+ this.signerAddress = signerAddress;
+ } else {
+ this.signerAddress = web3.eth.accounts[0];
+ }
+ }
+
+ public async totalTokens(): Promise<number> {
+ return await this.contract.methods.entryCount().call();
+ }
+
+ public async entry(serial: number): Promise<string> {
+ return await this.contract.methods.entry(serial).call();
+ }
+
+ public async addressOf(identifier: string): Promise<string> {
+ const id: string = web3.eth.abi.encodeParameter('bytes32', web3.utils.toHex(identifier));
+ return await this.contract.methods.addressOf(id).call();
+ }
+}
+
+ +
+ src/testing/token-service-stub.ts
+
+ Methods+ |
+
+
|
+
+ + + + getBySymbol + + + + | +||||||
+getBySymbol(symbol: string)
+ |
+ ||||||
+ Defined in src/testing/token-service-stub.ts:2
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
export class TokenServiceStub {
+ getBySymbol(symbol: string): any {
+ return {
+ name: 'Reserve',
+ symbol: 'RSV'
+ };
+ }
+}
+
+ +
+ src/app/_models/transaction.ts
+
+ Properties+ |
+
+ + | +
+ + + + from + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_models/transaction.ts:26
+ |
+
+ + + + recipient + + + | +
+ Type : AccountDetails
+
+ |
+
+ Defined in src/app/_models/transaction.ts:29
+ |
+
+ + + + sender + + + | +
+ Type : AccountDetails
+
+ |
+
+ Defined in src/app/_models/transaction.ts:27
+ |
+
+ + + + to + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_models/transaction.ts:28
+ |
+
+ + + + token + + + | +
+ Type : TxToken
+
+ |
+
+ Defined in src/app/_models/transaction.ts:30
+ |
+
+ + + + tx + + + | +
+ Type : Tx
+
+ |
+
+ Defined in src/app/_models/transaction.ts:31
+ |
+
+ + + + Optional + type + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_models/transaction.ts:33
+ |
+
+ + + + value + + + | +
+ Type : number
+
+ |
+
+ Defined in src/app/_models/transaction.ts:32
+ |
+
import {AccountDetails} from '@app/_models/account';
+
+class BlocksBloom {
+ low: number;
+ blockFilter: string;
+ blocktxFilter: string;
+ alg: string;
+ filterRounds: number;
+}
+
+class TxToken {
+ address: string;
+ name: string;
+ symbol: string;
+}
+
+class Tx {
+ block: number;
+ success: boolean;
+ timestamp: number;
+ txHash: string;
+ txIndex: number;
+}
+
+class Transaction {
+ from: string;
+ sender: AccountDetails;
+ to: string;
+ recipient: AccountDetails;
+ token: TxToken;
+ tx: Tx;
+ value: number;
+ type?: string;
+}
+
+class Conversion {
+ destinationToken: TxToken;
+ fromValue: number;
+ sourceToken: TxToken;
+ toValue: number;
+ trader: string;
+ user: AccountDetails;
+ tx: Tx;
+}
+
+export {
+ BlocksBloom,
+ TxToken,
+ Tx,
+ Transaction,
+ Conversion
+};
+
+ +
+ src/testing/transaction-service-stub.ts
+
+ Methods+ |
+
+
|
+
+ + + + getAllTransactions + + + + | +|||||||||
+getAllTransactions(offset: number, limit: number)
+ |
+ |||||||||
+ Defined in src/testing/transaction-service-stub.ts:8
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + setConversion + + + + | +||||||
+setConversion(conversion: any)
+ |
+ ||||||
+ Defined in src/testing/transaction-service-stub.ts:6
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + setTransaction + + + + | +|||||||||
+setTransaction(transaction: any, cacheSize: number)
+ |
+ |||||||||
+ Defined in src/testing/transaction-service-stub.ts:4
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
import {Observable, of} from 'rxjs';
+
+export class TransactionServiceStub {
+ setTransaction(transaction: any, cacheSize: number): void {}
+
+ setConversion(conversion: any): void {}
+
+ getAllTransactions(offset: number, limit: number): Observable<any> {
+ return of('Hello World');
+ }
+}
+
+ +
+ src/app/_models/transaction.ts
+
+ Properties+ |
+
+ + | +
+ + + + block + + + | +
+ Type : number
+
+ |
+
+ Defined in src/app/_models/transaction.ts:18
+ |
+
+ + + + success + + + | +
+ Type : boolean
+
+ |
+
+ Defined in src/app/_models/transaction.ts:19
+ |
+
+ + + + timestamp + + + | +
+ Type : number
+
+ |
+
+ Defined in src/app/_models/transaction.ts:20
+ |
+
+ + + + txHash + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_models/transaction.ts:21
+ |
+
+ + + + txIndex + + + | +
+ Type : number
+
+ |
+
+ Defined in src/app/_models/transaction.ts:22
+ |
+
import {AccountDetails} from '@app/_models/account';
+
+class BlocksBloom {
+ low: number;
+ blockFilter: string;
+ blocktxFilter: string;
+ alg: string;
+ filterRounds: number;
+}
+
+class TxToken {
+ address: string;
+ name: string;
+ symbol: string;
+}
+
+class Tx {
+ block: number;
+ success: boolean;
+ timestamp: number;
+ txHash: string;
+ txIndex: number;
+}
+
+class Transaction {
+ from: string;
+ sender: AccountDetails;
+ to: string;
+ recipient: AccountDetails;
+ token: TxToken;
+ tx: Tx;
+ value: number;
+ type?: string;
+}
+
+class Conversion {
+ destinationToken: TxToken;
+ fromValue: number;
+ sourceToken: TxToken;
+ toValue: number;
+ trader: string;
+ user: AccountDetails;
+ tx: Tx;
+}
+
+export {
+ BlocksBloom,
+ TxToken,
+ Tx,
+ Transaction,
+ Conversion
+};
+
+ +
+ src/app/_models/transaction.ts
+
+ Properties+ |
+
+ + | +
+ + + + address + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_models/transaction.ts:12
+ |
+
+ + + + name + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_models/transaction.ts:13
+ |
+
+ + + + symbol + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_models/transaction.ts:14
+ |
+
import {AccountDetails} from '@app/_models/account';
+
+class BlocksBloom {
+ low: number;
+ blockFilter: string;
+ blocktxFilter: string;
+ alg: string;
+ filterRounds: number;
+}
+
+class TxToken {
+ address: string;
+ name: string;
+ symbol: string;
+}
+
+class Tx {
+ block: number;
+ success: boolean;
+ timestamp: number;
+ txHash: string;
+ txIndex: number;
+}
+
+class Transaction {
+ from: string;
+ sender: AccountDetails;
+ to: string;
+ recipient: AccountDetails;
+ token: TxToken;
+ tx: Tx;
+ value: number;
+ type?: string;
+}
+
+class Conversion {
+ destinationToken: TxToken;
+ fromValue: number;
+ sourceToken: TxToken;
+ toValue: number;
+ trader: string;
+ user: AccountDetails;
+ tx: Tx;
+}
+
+export {
+ BlocksBloom,
+ TxToken,
+ Tx,
+ Transaction,
+ Conversion
+};
+
+ +
+ src/testing/user-service-stub.ts
+
+ Properties+ |
+
+ + | +
+ Methods+ |
+
+
|
+
+ + + + actions + + + | +
+ Type : []
+
+ |
+
+ Default value : [
+ { id: 1, user: 'Tom', role: 'enroller', action: 'Disburse RSV 100', approval: false },
+ { id: 2, user: 'Christine', role: 'admin', action: 'Change user phone number', approval: true },
+ { id: 3, user: 'Will', role: 'superadmin', action: 'Reclaim RSV 1000', approval: true },
+ { id: 4, user: 'Vivian', role: 'enroller', action: 'Complete user profile', approval: true },
+ { id: 5, user: 'Jack', role: 'enroller', action: 'Reclaim RSV 200', approval: false },
+ { id: 6, user: 'Patience', role: 'enroller', action: 'Change user information', approval: false }
+ ]
+ |
+
+ Defined in src/testing/user-service-stub.ts:12
+ |
+
+ + + + users + + + | +
+ Type : []
+
+ |
+
+ Default value : [
+ {id: 1, name: 'John Doe', phone: '+25412345678', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'user', created: '08/16/2020', balance: '12987', failedPinAttempts: 1, status: 'approved', bio: 'Bodaboda', gender: 'male'},
+ {id: 2, name: 'Jane Buck', phone: '+25412341234', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'vendor', created: '04/02/2020', balance: '56281', failedPinAttempts: 0, status: 'approved', bio: 'Groceries', gender: 'female'},
+ {id: 3, name: 'Mc Donald', phone: '+25498765432', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'group', created: '11/16/2020', balance: '450', failedPinAttempts: 2, status: 'unapproved', bio: 'Food', gender: 'male'},
+ {id: 4, name: 'Hera Cles', phone: '+25498769876', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'user', created: '05/28/2020', balance: '5621', failedPinAttempts: 3, status: 'approved', bio: 'Shop', gender: 'female'},
+ {id: 5, name: 'Silver Fia', phone: '+25462518374', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'token agent', created: '10/10/2020', balance: '817', failedPinAttempts: 0, status: 'unapproved', bio: 'Electronics', gender: 'male'},
+ ]
+ |
+
+ Defined in src/testing/user-service-stub.ts:4
+ |
+
+ + + + approveAction + + + + | +||||||
+approveAction(id: number)
+ |
+ ||||||
+ Defined in src/testing/user-service-stub.ts:71
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + getActionById + + + + | +||||||
+getActionById(id: string)
+ |
+ ||||||
+ Defined in src/testing/user-service-stub.ts:61
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + getUser + + + + | +||||||
+getUser(userKey: string)
+ |
+ ||||||
+ Defined in src/testing/user-service-stub.ts:37
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getUserById + + + + | +||||||
+getUserById(id: string)
+ |
+ ||||||
+ Defined in src/testing/user-service-stub.ts:21
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
import {Observable, of} from 'rxjs';
+
+export class UserServiceStub {
+ users = [
+ {id: 1, name: 'John Doe', phone: '+25412345678', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'user', created: '08/16/2020', balance: '12987', failedPinAttempts: 1, status: 'approved', bio: 'Bodaboda', gender: 'male'},
+ {id: 2, name: 'Jane Buck', phone: '+25412341234', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'vendor', created: '04/02/2020', balance: '56281', failedPinAttempts: 0, status: 'approved', bio: 'Groceries', gender: 'female'},
+ {id: 3, name: 'Mc Donald', phone: '+25498765432', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'group', created: '11/16/2020', balance: '450', failedPinAttempts: 2, status: 'unapproved', bio: 'Food', gender: 'male'},
+ {id: 4, name: 'Hera Cles', phone: '+25498769876', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'user', created: '05/28/2020', balance: '5621', failedPinAttempts: 3, status: 'approved', bio: 'Shop', gender: 'female'},
+ {id: 5, name: 'Silver Fia', phone: '+25462518374', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'token agent', created: '10/10/2020', balance: '817', failedPinAttempts: 0, status: 'unapproved', bio: 'Electronics', gender: 'male'},
+ ];
+
+ actions = [
+ { id: 1, user: 'Tom', role: 'enroller', action: 'Disburse RSV 100', approval: false },
+ { id: 2, user: 'Christine', role: 'admin', action: 'Change user phone number', approval: true },
+ { id: 3, user: 'Will', role: 'superadmin', action: 'Reclaim RSV 1000', approval: true },
+ { id: 4, user: 'Vivian', role: 'enroller', action: 'Complete user profile', approval: true },
+ { id: 5, user: 'Jack', role: 'enroller', action: 'Reclaim RSV 200', approval: false },
+ { id: 6, user: 'Patience', role: 'enroller', action: 'Change user information', approval: false }
+ ];
+
+ getUserById(id: string): any {
+ return {
+ id: 1,
+ name: 'John Doe',
+ phone: '+25412345678',
+ address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+ type: 'user',
+ created: '08/16/2020',
+ balance: '12987',
+ failedPinAttempts: 1,
+ status: 'approved',
+ bio: 'Bodaboda',
+ gender: 'male'
+ };
+ }
+
+ getUser(userKey: string): Observable<any> {
+ console.log('Here');
+ return of({
+ dateRegistered: 1595537208,
+ key: {
+ ethereum: [
+ '0x51d3c8e2e421604e2b644117a362d589c5434739',
+ '0x9D7c284907acbd4a0cE2dDD0AA69147A921a573D'
+ ]
+ },
+ location: {
+ external: {},
+ latitude: '22.430670',
+ longitude: '151.002995'
+ },
+ selling: [
+ 'environment',
+ 'health',
+ 'transport'
+ ],
+ vcard: 'QkVHSU46VkNBUkQNClZFUlNJT046My4wDQpFTUFJTDphYXJuZXNlbkBob3RtYWlsLmNvbQ0KRk46S3VydMKgS3JhbmpjDQpOOktyYW5qYztLdXJ0Ozs7DQpURUw7VFlQPUNFTEw6NjkyNTAzMzQ5ODE5Ng0KRU5EOlZDQVJEDQo='
+ });
+ }
+
+ getActionById(id: string): any {
+ return {
+ id: 1,
+ user: 'Tom',
+ role: 'enroller',
+ action: 'Disburse RSV 100',
+ approval: false
+ };
+ }
+
+ approveAction(id: number): any {
+ return {
+ id: 1,
+ user: 'Tom',
+ role: 'enroller',
+ action: 'Disburse RSV 100',
+ approval: true
+ };
+ }
+}
+
+ +
+ src/app/_models/settings.ts
+
+ Properties+ |
+
+ + | +
+ + + + engine + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_models/settings.ts:16
+ |
+
+ + + + provider + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_models/settings.ts:17
+ |
+
class Settings {
+ w3: W3 = {
+ engine: undefined,
+ provider: undefined,
+ };
+ scanFilter: any;
+ registry: any;
+ txHelper: any;
+
+ constructor(scanFilter: any) {
+ this.scanFilter = scanFilter;
+ }
+}
+
+class W3 {
+ engine: any;
+ provider: any;
+}
+
+export {
+ Settings,
+ W3
+};
+
+ +
+ src/app/pages/accounts/account-details/account-details.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-account-details |
+
styleUrls | +./account-details.component.scss |
+
templateUrl | +./account-details.component.html |
+
+constructor(formBuilder: FormBuilder, locationService: LocationService, transactionService: TransactionService, userService: UserService, route: ActivatedRoute, router: Router, tokenService: TokenService, loggingService: LoggingService, blockSyncService: BlockSyncService, cdr: ChangeDetectorRef, snackBar: MatSnackBar)
+ |
+ ||||||||||||||||||||||||||||||||||||
+ + | +||||||||||||||||||||||||||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + copyAddress + + + + | +
+copyAddress()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + doTransactionFilter + + + + | +||||||
+doTransactionFilter(value: string)
+ |
+ ||||||
+ + | +||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + doUserFilter + + + + | +||||||
+doUserFilter(value: string)
+ |
+ ||||||
+ + | +||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + downloadCsv + + + + | +|||||||||
+downloadCsv(data: any, filename: string)
+ |
+ |||||||||
+ + | +|||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + filterAccounts + + + + | +
+filterAccounts()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + filterTransactions + + + + | +
+filterTransactions()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + resetPin + + + + | +
+resetPin()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + Async + saveInfo + + + + | +
+
+ saveInfo()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + viewAccount + + + + | +||||
+viewAccount(account)
+ |
+ ||||
+ + | +||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + viewTransaction + + + + | +||||
+viewTransaction(transaction)
+ |
+ ||||
+ + | +||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + account + + + | +
+ Type : AccountDetails
+
+ |
+
+ + | +
+ + + + accountAddress + + + | +
+ Type : string
+
+ |
+
+ + | +
+ + + + accountInfoForm + + + | +
+ Type : FormGroup
+
+ |
+
+ + | +
+ + + + accounts + + + | +
+ Type : Array<AccountDetails>
+
+ |
+
+ Default value : []
+ |
+
+ + | +
+ + + + accountStatus + + + | +
+ Type : any
+
+ |
+
+ + | +
+ + + + accountsType + + + | +
+ Type : string
+
+ |
+
+ Default value : 'all'
+ |
+
+ + | +
+ + + + accountTypes + + + | +
+ Type : Array<string>
+
+ |
+
+ + | +
+ + + + areaNames + + + | +
+ Type : Array<AreaName>
+
+ |
+
+ + | +
+ + + + areaTypes + + + | +
+ Type : Array<AreaType>
+
+ |
+
+ + | +
+ + + + bloxbergLink + + + | +
+ Type : string
+
+ |
+
+ + | +
+ + + + categories + + + | +
+ Type : Array<Category>
+
+ |
+
+ + | +
+ + + + genders + + + | +
+ Type : Array<string>
+
+ |
+
+ + | +
+ + + + matcher + + + | +
+ Type : CustomErrorStateMatcher
+
+ |
+
+ Default value : new CustomErrorStateMatcher()
+ |
+
+ + | +
+ + + + submitted + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ + | +
+ + + + transaction + + + | +
+ Type : any
+
+ |
+
+ + | +
+ + + + transactions + + + | +
+ Type : Array<Transaction>
+
+ |
+
+ + | +
+ + + + transactionsDataSource + + + | +
+ Type : MatTableDataSource<any>
+
+ |
+
+ + | +
+ + + + transactionsDefaultPageSize + + + | +
+ Type : number
+
+ |
+
+ Default value : 10
+ |
+
+ + | +
+ + + + transactionsDisplayedColumns + + + | +
+ Type : Array<string>
+
+ |
+
+ Default value : ['sender', 'recipient', 'value', 'created', 'type']
+ |
+
+ + | +
+ + + + transactionsPageSizeOptions + + + | +
+ Type : Array<number>
+
+ |
+
+ Default value : [10, 20, 50, 100]
+ |
+
+ + | +
+ + + + transactionsType + + + | +
+ Type : string
+
+ |
+
+ Default value : 'all'
+ |
+
+ + | +
+ + + + transactionsTypes + + + | +
+ Type : Array<string>
+
+ |
+
+ + | +
+ + + + transactionTablePaginator + + + | +
+ Type : MatPaginator
+
+ |
+
+ Decorators :
+ +
+ @ViewChild('TransactionTablePaginator', {static: true})
+ |
+
+ + | +
+ + + + transactionTableSort + + + | +
+ Type : MatSort
+
+ |
+
+ Decorators :
+ +
+ @ViewChild('TransactionTableSort', {static: true})
+ |
+
+ + | +
+ + + + userDataSource + + + | +
+ Type : MatTableDataSource<any>
+
+ |
+
+ + | +
+ + + + userDisplayedColumns + + + | +
+ Type : Array<string>
+
+ |
+
+ Default value : ['name', 'phone', 'created', 'balance', 'location']
+ |
+
+ + | +
+ + + + usersDefaultPageSize + + + | +
+ Type : number
+
+ |
+
+ Default value : 10
+ |
+
+ + | +
+ + + + usersPageSizeOptions + + + | +
+ Type : Array<number>
+
+ |
+
+ Default value : [10, 20, 50, 100]
+ |
+
+ + | +
+ + + + userTablePaginator + + + | +
+ Type : MatPaginator
+
+ |
+
+ Decorators :
+ +
+ @ViewChild('UserTablePaginator', {static: true})
+ |
+
+ + | +
+ + + + userTableSort + + + | +
+ Type : MatSort
+
+ |
+
+ Decorators :
+ +
+ @ViewChild('UserTableSort', {static: true})
+ |
+
+ + | +
+ + accountInfoFormStub + | +
+ getaccountInfoFormStub()
+ |
+
+ + | +
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core';
+import {MatTableDataSource} from '@angular/material/table';
+import {MatPaginator} from '@angular/material/paginator';
+import {MatSort} from '@angular/material/sort';
+import {BlockSyncService, LocationService, LoggingService, TokenService, TransactionService, UserService} from '@app/_services';
+import {ActivatedRoute, Params, Router} from '@angular/router';
+import {first} from 'rxjs/operators';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {copyToClipboard, CustomErrorStateMatcher, exportCsv} from '@app/_helpers';
+import {MatSnackBar} from '@angular/material/snack-bar';
+import {add0x, strip0x} from '@src/assets/js/ethtx/dist/hex';
+import {environment} from '@src/environments/environment';
+import {AccountDetails, AreaName, AreaType, Category, Transaction} from '@app/_models';
+
+@Component({
+ selector: 'app-account-details',
+ templateUrl: './account-details.component.html',
+ styleUrls: ['./account-details.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class AccountDetailsComponent implements OnInit {
+ transactionsDataSource: MatTableDataSource<any>;
+ transactionsDisplayedColumns: Array<string> = ['sender', 'recipient', 'value', 'created', 'type'];
+ transactionsDefaultPageSize: number = 10;
+ transactionsPageSizeOptions: Array<number> = [10, 20, 50, 100];
+ @ViewChild('TransactionTablePaginator', {static: true}) transactionTablePaginator: MatPaginator;
+ @ViewChild('TransactionTableSort', {static: true}) transactionTableSort: MatSort;
+
+ userDataSource: MatTableDataSource<any>;
+ userDisplayedColumns: Array<string> = ['name', 'phone', 'created', 'balance', 'location'];
+ usersDefaultPageSize: number = 10;
+ usersPageSizeOptions: Array<number> = [10, 20, 50, 100];
+ @ViewChild('UserTablePaginator', {static: true}) userTablePaginator: MatPaginator;
+ @ViewChild('UserTableSort', {static: true}) userTableSort: MatSort;
+
+ accountInfoForm: FormGroup;
+ account: AccountDetails;
+ accountAddress: string;
+ accountStatus: any;
+ accounts: Array<AccountDetails> = [];
+ accountsType: string = 'all';
+ categories: Array<Category>;
+ areaNames: Array<AreaName>;
+ areaTypes: Array<AreaType>;
+ transaction: any;
+ transactions: Array<Transaction>;
+ transactionsType: string = 'all';
+ accountTypes: Array<string>;
+ transactionsTypes: Array<string>;
+ genders: Array<string>;
+ matcher: CustomErrorStateMatcher = new CustomErrorStateMatcher();
+ submitted: boolean = false;
+ bloxbergLink: string;
+
+ constructor(
+ private formBuilder: FormBuilder,
+ private locationService: LocationService,
+ private transactionService: TransactionService,
+ private userService: UserService,
+ private route: ActivatedRoute,
+ private router: Router,
+ private tokenService: TokenService,
+ private loggingService: LoggingService,
+ private blockSyncService: BlockSyncService,
+ private cdr: ChangeDetectorRef,
+ private snackBar: MatSnackBar,
+ ) {
+ this.accountInfoForm = this.formBuilder.group({
+ name: ['', Validators.required],
+ phoneNumber: ['', Validators.required],
+ age: ['', Validators.required],
+ type: ['', Validators.required],
+ bio: ['', Validators.required],
+ gender: ['', Validators.required],
+ businessCategory: ['', Validators.required],
+ userLocation: ['', Validators.required],
+ location: ['', Validators.required],
+ locationType: ['', Validators.required],
+ });
+ this.route.paramMap.subscribe(async (params: Params) => {
+ this.accountAddress = add0x(params.get('id'));
+ this.bloxbergLink = 'https://blockexplorer.bloxberg.org/address/' + this.accountAddress + '/transactions';
+ (await this.userService.getAccountByAddress(this.accountAddress, 100)).subscribe(async res => {
+ if (res !== undefined) {
+ this.account = res;
+ this.cdr.detectChanges();
+ this.loggingService.sendInfoLevelMessage(this.account);
+ // this.userService.getAccountStatus(this.account.vcard?.tel[0].value).pipe(first())
+ // .subscribe(response => this.accountStatus = response);
+ this.accountInfoForm.patchValue({
+ name: this.account.vcard?.fn[0].value,
+ phoneNumber: this.account.vcard?.tel[0].value,
+ age: this.account.age,
+ type: this.account.type,
+ bio: this.account.products,
+ gender: this.account.gender,
+ businessCategory: this.account.category,
+ userLocation: this.account.location.area_name,
+ location: this.account.location.area,
+ locationType: this.account.location.area_type,
+ });
+ } else {
+ alert('Account not found!');
+ }
+ });
+ this.blockSyncService.blockSync(this.accountAddress);
+ });
+ this.userService.getCategories().pipe(first()).subscribe(res => this.categories = res);
+ this.locationService.getAreaNames().pipe(first()).subscribe(res => this.areaNames = res);
+ this.locationService.getAreaTypes().pipe(first()).subscribe(res => this.areaTypes = res);
+ this.userService.getAccountTypes().pipe(first()).subscribe(res => this.accountTypes = res);
+ this.userService.getTransactionTypes().pipe(first()).subscribe(res => this.transactionsTypes = res);
+ this.userService.getGenders().pipe(first()).subscribe(res => this.genders = res);
+ }
+
+ ngOnInit(): void {
+ this.userService.accountsSubject.subscribe(accounts => {
+ this.userDataSource = new MatTableDataSource<any>(accounts);
+ this.userDataSource.paginator = this.userTablePaginator;
+ this.userDataSource.sort = this.userTableSort;
+ this.accounts = accounts;
+ });
+
+ this.transactionService.transactionsSubject.subscribe(transactions => {
+ this.transactionsDataSource = new MatTableDataSource<any>(transactions);
+ this.transactionsDataSource.paginator = this.transactionTablePaginator;
+ this.transactionsDataSource.sort = this.transactionTableSort;
+ this.transactions = transactions;
+ });
+ }
+
+ doTransactionFilter(value: string): void {
+ this.transactionsDataSource.filter = value.trim().toLocaleLowerCase();
+ }
+
+ doUserFilter(value: string): void {
+ this.userDataSource.filter = value.trim().toLocaleLowerCase();
+ }
+
+ viewTransaction(transaction): void {
+ this.transaction = transaction;
+ }
+
+ viewAccount(account): void {
+ this.router.navigateByUrl(`/accounts/${strip0x(account.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`);
+ }
+
+ get accountInfoFormStub(): any { return this.accountInfoForm.controls; }
+
+ async saveInfo(): Promise<void> {
+ this.submitted = true;
+ if (this.accountInfoForm.invalid || !confirm('Change user\'s profile information?')) { return; }
+ const accountKey = await this.userService.changeAccountInfo(
+ this.accountAddress,
+ this.accountInfoFormStub.name.value,
+ this.accountInfoFormStub.phoneNumber.value,
+ this.accountInfoFormStub.age.value,
+ this.accountInfoFormStub.type.value,
+ this.accountInfoFormStub.bio.value,
+ this.accountInfoFormStub.gender.value,
+ this.accountInfoFormStub.businessCategory.value,
+ this.accountInfoFormStub.userLocation.value,
+ this.accountInfoFormStub.location.value,
+ this.accountInfoFormStub.locationType.value
+ );
+ this.submitted = false;
+ }
+
+ filterAccounts(): void {
+ if (this.accountsType === 'all') {
+ this.userService.accountsSubject.subscribe(accounts => {
+ this.userDataSource.data = accounts;
+ this.accounts = accounts;
+ });
+ } else {
+ this.userDataSource.data = this.accounts.filter(account => account.type === this.accountsType);
+ }
+ }
+
+ filterTransactions(): void {
+ if (this.transactionsType === 'all') {
+ this.transactionService.transactionsSubject.subscribe(transactions => {
+ this.transactionsDataSource.data = transactions;
+ this.transactions = transactions;
+ });
+ } else {
+ this.transactionsDataSource.data = this.transactions.filter(transaction => transaction.type === this.transactionsType);
+ }
+ }
+
+ resetPin(): void {
+ if (!confirm('Reset user\'s pin?')) { return; }
+ this.userService.resetPin(this.account.vcard.tel[0].value).pipe(first()).subscribe(res => {
+ this.loggingService.sendInfoLevelMessage(`Response: ${res}`);
+ });
+ }
+
+ downloadCsv(data: any, filename: string): void {
+ exportCsv(data, filename);
+ }
+
+ copyAddress(): void {
+ if (copyToClipboard(this.accountAddress)) {
+ this.snackBar.open(this.accountAddress + ' copied successfully!', 'Close', { duration: 3000 });
+ }
+ }
+}
+
+ <!-- Begin page -->
+<div class="wrapper">
+ <app-sidebar></app-sidebar>
+
+ <!-- ============================================================== -->
+ <!-- Start Page Content here -->
+ <!-- ============================================================== -->
+
+ <div id="content">
+ <app-topbar></app-topbar>
+ <!-- Start Content-->
+ <div class="container-fluid" appMenuSelection>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
+ <li class="breadcrumb-item"><a routerLink="/accounts">Accounts</a></li>
+ <li *ngIf="account" class="breadcrumb-item active" aria-current="page">{{account?.vcard?.fn[0].value}}</li>
+ </ol>
+ </nav>
+ <div *ngIf="!account" class="text-center">
+ <div class="spinner-grow text-primary m-1" role="status" style="width: 3rem; height: 3rem;">
+ <span class="sr-only">Loading...</span>
+ </div>
+ <div class="spinner-grow text-primary m-1" role="status" style="width: 3rem; height: 3rem;">
+ <span class="sr-only">Loading...</span>
+ </div>
+ <div class="spinner-grow text-primary m-1" role="status" style="width: 3rem; height: 3rem;">
+ <span class="sr-only">Loading...</span>
+ </div>
+ </div>
+ <div *ngIf="account" class="card mb-3">
+ <div class="row card-body">
+ <h3>
+ <strong> {{account?.vcard?.fn[0].value}} </strong>
+ </h3>
+ <span class="ml-auto"><strong>Balance:</strong> {{account?.balance | tokenRatio}} SRF</span>
+ <span class="ml-2"><strong>Created:</strong> {{account?.date_registered | date}}</span>
+ <span class="ml-2"><strong>Address:</strong>
+ <a href="{{bloxbergLink}}" target="_blank"> {{accountAddress}} </a>
+ <img src="assets/images/checklist.svg" class="ml-2" height="20rem" (click)="copyAddress()" alt="Copy">
+ </span>
+ </div>
+ </div>
+ <div *ngIf="account" class="card mt-3 mb-3">
+ <div class="card-body">
+ <form [formGroup]="accountInfoForm" (ngSubmit)="saveInfo()">
+ <div class="row form-inline">
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Name(s): *</mat-label>
+ <input matInput type="text" id="givenNames" placeholder="{{account?.vcard?.fn[0].value}}"
+ value="{{account?.vcard?.fn[0].value}}" formControlName="name" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && accountInfoFormStub.name.errors">Name is required.</mat-error>
+ </mat-form-field>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Phone Number: </mat-label>
+ <input matInput type="text" id="phoneNumber" placeholder="{{account?.vcard?.tel[0].value}}"
+ value="{{account?.vcard?.tel[0].value}}" formControlName="phoneNumber" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && accountInfoFormStub.phoneNumber.errors">Phone Number is required.</mat-error>
+ </mat-form-field>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Age: </mat-label>
+ <input matInput type="text" id="age" placeholder="{{account?.age}}"
+ value="{{account?.age}}" formControlName="age" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && accountInfoFormStub.age.errors">Age is required.</mat-error>
+ </mat-form-field>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label> ACCOUNT TYPE: </mat-label>
+ <mat-select id="accountType" [(value)]="account.type" formControlName="type"
+ [errorStateMatcher]="matcher">
+ <mat-option *ngFor="let accountType of accountTypes" [value]="accountType">
+ {{accountType | uppercase}}
+ </mat-option>
+ </mat-select>
+ <mat-error *ngIf="submitted && accountInfoFormStub.type.errors">Type is required.</mat-error>
+ </mat-form-field>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Bio: </mat-label>
+ <input matInput type="text" id="bio" placeholder="{{account?.products}}" value="{{account?.products}}"
+ formControlName="bio" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && accountInfoFormStub.bio.errors">Bio is required.</mat-error>
+ </mat-form-field>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label> GENDER: </mat-label>
+ <mat-select id="gender" [(value)]="account.gender" formControlName="gender"
+ [errorStateMatcher]="matcher">
+ <mat-option *ngFor="let gender of genders" [value]="gender">
+ {{gender | uppercase}}
+ </mat-option>
+ </mat-select>
+ <mat-error *ngIf="submitted && accountInfoFormStub.gender.errors">Gender is required.</mat-error>
+ </mat-form-field>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label> BUSINESS CATEGORY: </mat-label>
+ <mat-select id="businessCategory" [(value)]="account.category" formControlName="businessCategory"
+ [errorStateMatcher]="matcher">
+ <mat-option *ngFor="let category of categories" [value]="category">
+ {{category | titlecase}}
+ </mat-option>
+ </mat-select>
+ <mat-error *ngIf="submitted && accountInfoFormStub.businessCategory.errors">
+ Category is required.
+ </mat-error>
+ </mat-form-field>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>User Location: </mat-label>
+ <input matInput type="text" id="userLocation" placeholder="{{account?.location.area_name}}"
+ value="{{account?.location.area_name}}" formControlName="userLocation"
+ [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && accountInfoFormStub.userLocation.errors">
+ User Location is required.
+ </mat-error>
+ </mat-form-field>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label> LOCATION: </mat-label>
+ <mat-select id="location" [(value)]="account.location.area" formControlName="location"
+ [errorStateMatcher]="matcher">
+ <mat-option *ngFor="let area of areaNames" [value]="area">
+ {{area | uppercase}}
+ </mat-option>
+ </mat-select>
+ <mat-error *ngIf="submitted && accountInfoFormStub.location.errors">Location is required.</mat-error>
+ </mat-form-field>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label> LOCATION TYPE: </mat-label>
+ <mat-select id="locationType" [(value)]="account.location.area_type" formControlName="locationType"
+ [errorStateMatcher]="matcher">
+ <mat-option *ngFor="let type of areaTypes" [value]="type">
+ {{type | uppercase}}
+ </mat-option>
+ </mat-select>
+ <mat-error *ngIf="submitted && accountInfoFormStub.locationType.errors">Location Type is required.</mat-error>
+ </mat-form-field>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary mb-3">
+ Add User KYC
+ </button>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-success mb-3"
+ (click)="resetPin()">
+ Reset Pin
+ </button>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <button mat-raised-button color="warn" type="button" class="btn btn-outline-danger mb-3">
+ Delete User
+ </button>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <button mat-raised-button color="primary" type="submit" class="btn btn-outline-primary">
+ SAVE DETAILS
+ </button>
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+
+ <div class="card mb-3">
+ <mat-card-title class="card-header">
+ USER
+ </mat-card-title>
+ <div class="card-body">
+ <div class="table-responsive">
+ <table class="table table-striped table-bordered table-hover">
+ <caption> 1 user </caption>
+ <thead class="thead-dark">
+ <tr>
+ <th scope="col">NAME</th>
+ <th scope="col">BALANCE</th>
+ <th scope="col">CREATED</th>
+ <th scope="col">STATUS</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>{{account?.vcard?.fn[0].value}}</td>
+ <td>{{account?.balance | tokenRatio}}</td>
+ <td>{{account?.date_registered | date}}</td>
+ <td>
+ <span *ngIf="accountStatus === 'active'" class="badge badge-success badge-pill">
+ {{accountStatus}}
+ </span>
+ <span *ngIf="accountStatus === 'blocked'" class="badge badge-danger badge-pill">
+ {{accountStatus}}
+ </span>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+
+ <mat-tab-group *ngIf="account" dynamicHeight mat-align-tabs="start">
+ <mat-tab label="Transactions">
+ <app-transaction-details [transaction]="transaction"></app-transaction-details>
+ <div class="card mt-1">
+ <div class="card-header">
+ <div class="row">
+ <mat-form-field appearance="outline">
+ <mat-label> TRANSACTION TYPE </mat-label>
+ <mat-select id="transferSelect" [(value)]="transactionsType" (selectionChange)="filterTransactions()">
+ <mat-option value="all">ALL TRANSFERS</mat-option>
+ <mat-option *ngFor="let transactionType of transactionsTypes" [value]="transactionType">
+ {{transactionType | uppercase}}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary ml-auto mr-2" (click)="downloadCsv(transactions, 'transactions')"> EXPORT </button>
+ </div>
+ </div>
+
+ <div class="card-body">
+ <mat-form-field appearance="outline">
+ <mat-label> Filter </mat-label>
+ <input matInput type="text" (keyup)="doTransactionFilter($event.target.value)" placeholder="Filter">
+ <mat-icon matSuffix>search</mat-icon>
+ </mat-form-field>
+
+ <mat-table class="mat-elevation-z10" [dataSource]="transactionsDataSource" matSort matSortActive="created"
+ #TransactionTableSort="matSort" matSortDirection="asc" matSortDisableClear>
+
+ <ng-container matColumnDef="sender">
+ <th mat-header-cell *matHeaderCellDef mat-sort-header> Sender </th>
+ <td mat-cell *matCellDef="let transaction"> {{transaction?.sender?.vcard.fn[0].value}} </td>
+ </ng-container>
+
+ <ng-container matColumnDef="recipient">
+ <th mat-header-cell *matHeaderCellDef mat-sort-header> Recipient </th>
+ <td mat-cell *matCellDef="let transaction"> {{transaction?.recipient?.vcard.fn[0].value}} </td>
+ </ng-container>
+
+ <ng-container matColumnDef="value">
+ <th mat-header-cell *matHeaderCellDef mat-sort-header> Value </th>
+ <td mat-cell *matCellDef="let transaction">
+ <span *ngIf="transaction.type == 'transaction'">{{transaction?.value | tokenRatio}}</span>
+ <span *ngIf="transaction.type == 'conversion'">{{transaction?.toValue | tokenRatio}}</span>
+ </td>
+ </ng-container>
+
+ <ng-container matColumnDef="created">
+ <th mat-header-cell *matHeaderCellDef mat-sort-header> Created </th>
+ <td mat-cell *matCellDef="let transaction"> {{transaction?.tx.timestamp | date}} </td>
+ </ng-container>
+
+ <ng-container matColumnDef="type">
+ <th mat-header-cell *matHeaderCellDef mat-sort-header> TYPE </th>
+ <td mat-cell *matCellDef="let transaction">
+ <span class="badge badge-success badge-pill"> {{transaction?.type}} </span>
+ </td>
+ </ng-container>
+
+ <mat-header-row *matHeaderRowDef="transactionsDisplayedColumns"></mat-header-row>
+ <mat-row *matRowDef="let transaction; columns: transactionsDisplayedColumns" matRipple
+ (click)="viewTransaction(transaction)"></mat-row>
+ </mat-table>
+
+ <mat-paginator #TransactionTablePaginator="matPaginator" [pageSize]="transactionsDefaultPageSize"
+ [pageSizeOptions]="transactionsPageSizeOptions" showFirstLastButtons></mat-paginator>
+
+ </div>
+ </div>
+ </mat-tab>
+
+ <mat-tab label="Users">
+ <div class="card mt-1">
+ <mat-card-title class="card-header">
+ Accounts
+ </mat-card-title>
+ <div class="card-body">
+ <div class="row card-header">
+ <mat-form-field appearance="outline">
+ <mat-label> ACCOUNT TYPE </mat-label>
+ <mat-select id="typeSelect" [(value)]="accountsType" (selectionChange)="filterAccounts()">
+ <mat-option value="all">ALL</mat-option>
+ <mat-option *ngFor="let accountType of accountTypes" [value]="accountType">
+ {{accountType | uppercase}}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary ml-auto mr-2" (click)="downloadCsv(accounts, 'accounts')"> EXPORT </button>
+ </div>
+
+ <mat-form-field appearance="outline">
+ <mat-label> Filter </mat-label>
+ <input matInput type="text" (keyup)="doUserFilter($event.target.value)" placeholder="Filter">
+ <mat-icon matSuffix>search</mat-icon>
+ </mat-form-field>
+
+ <mat-table class="mat-elevation-z10" [dataSource]="userDataSource" matSort #UserTableSort="matSort"
+ matSortActive="created" matSortDirection="desc" matSortDisableClear>
+
+ <ng-container matColumnDef="name">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> NAME </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user?.vcard.fn[0].value}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="phone">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> PHONE NUMBER </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user?.vcard.tel[0].value}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="created">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> CREATED </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user?.date_registered | date}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="balance">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> BALANCE </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user?.balance}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="location">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> LOCATION </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user?.location.area_name}} </mat-cell>
+ </ng-container>
+
+ <mat-header-row *matHeaderRowDef=userDisplayedColumns></mat-header-row>
+ <mat-row *matRowDef="let account; columns: userDisplayedColumns" (click)="viewAccount(account)"
+ matRipple></mat-row>
+ </mat-table>
+
+ <mat-paginator #UserTablePaginator="matPaginator" [pageSize]="usersDefaultPageSize"
+ [pageSizeOptions]="usersPageSizeOptions" showFirstLastButtons></mat-paginator>
+ </div>
+ </div>
+ </mat-tab>
+ </mat-tab-group>
+ </div>
+ <app-footer appMenuSelection></app-footer>
+ </div>
+ <!-- ============================================================== -->
+ <!-- End Page content -->
+ <!-- ============================================================== -->
+</div>
+
+
+ ./account-details.component.scss
+
+ +
+ src/app/pages/accounts/account-search/account-search.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-account-search |
+
styleUrls | +./account-search.component.scss |
+
templateUrl | +./account-search.component.html |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+ Accessors+ |
+
+
|
+
+constructor(formBuilder: FormBuilder, userService: UserService, router: Router)
+ |
+ ||||||||||||
+ + | +||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + Async + onAddressSearch + + + + | +
+
+ onAddressSearch()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + onNameSearch + + + + | +
+onNameSearch()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + Async + onPhoneSearch + + + + | +
+
+ onPhoneSearch()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + addressSearchForm + + + | +
+ Type : FormGroup
+
+ |
+
+ + | +
+ + + + addressSearchLoading + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ + | +
+ + + + addressSearchSubmitted + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ + | +
+ + + + matcher + + + | +
+ Type : CustomErrorStateMatcher
+
+ |
+
+ Default value : new CustomErrorStateMatcher()
+ |
+
+ + | +
+ + + + nameSearchForm + + + | +
+ Type : FormGroup
+
+ |
+
+ + | +
+ + + + nameSearchLoading + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ + | +
+ + + + nameSearchSubmitted + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ + | +
+ + + + phoneSearchForm + + + | +
+ Type : FormGroup
+
+ |
+
+ + | +
+ + + + phoneSearchLoading + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ + | +
+ + + + phoneSearchSubmitted + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ + | +
+ + nameSearchFormStub + | +
+ getnameSearchFormStub()
+ |
+
+ + | +
+ + phoneSearchFormStub + | +
+ getphoneSearchFormStub()
+ |
+
+ + | +
+ + addressSearchFormStub + | +
+ getaddressSearchFormStub()
+ |
+
+ + | +
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {CustomErrorStateMatcher} from '@app/_helpers';
+import {UserService} from '@app/_services';
+import {Router} from '@angular/router';
+import {strip0x} from '@src/assets/js/ethtx/dist/hex';
+import {environment} from '@src/environments/environment';
+
+@Component({
+ selector: 'app-account-search',
+ templateUrl: './account-search.component.html',
+ styleUrls: ['./account-search.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class AccountSearchComponent implements OnInit {
+ nameSearchForm: FormGroup;
+ nameSearchSubmitted: boolean = false;
+ nameSearchLoading: boolean = false;
+ phoneSearchForm: FormGroup;
+ phoneSearchSubmitted: boolean = false;
+ phoneSearchLoading: boolean = false;
+ addressSearchForm: FormGroup;
+ addressSearchSubmitted: boolean = false;
+ addressSearchLoading: boolean = false;
+ matcher: CustomErrorStateMatcher = new CustomErrorStateMatcher();
+
+ constructor(
+ private formBuilder: FormBuilder,
+ private userService: UserService,
+ private router: Router,
+ ) { }
+
+ ngOnInit(): void {
+ this.nameSearchForm = this.formBuilder.group({
+ name: ['', Validators.required],
+ });
+ this.phoneSearchForm = this.formBuilder.group({
+ phoneNumber: ['', Validators.required],
+ });
+ this.addressSearchForm = this.formBuilder.group({
+ address: ['', Validators.required],
+ });
+ }
+
+ get nameSearchFormStub(): any { return this.nameSearchForm.controls; }
+ get phoneSearchFormStub(): any { return this.phoneSearchForm.controls; }
+ get addressSearchFormStub(): any { return this.addressSearchForm.controls; }
+
+ onNameSearch(): void {
+ this.nameSearchSubmitted = true;
+ if (this.nameSearchForm.invalid) { return; }
+ this.nameSearchLoading = true;
+ this.userService.searchAccountByName(this.nameSearchFormStub.name.value);
+ this.nameSearchLoading = false;
+ }
+
+ async onPhoneSearch(): Promise<void> {
+ this.phoneSearchSubmitted = true;
+ if (this.phoneSearchForm.invalid) { return; }
+ this.phoneSearchLoading = true;
+ (await this.userService.getAccountByPhone(this.phoneSearchFormStub.phoneNumber.value, 100)).subscribe(async res => {
+ if (res !== undefined) {
+ await this.router.navigateByUrl(`/accounts/${strip0x(res.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`);
+ } else {
+ alert('Account not found!');
+ }
+ });
+ this.phoneSearchLoading = false;
+ }
+
+ async onAddressSearch(): Promise<void> {
+ this.addressSearchSubmitted = true;
+ if (this.addressSearchForm.invalid) { return; }
+ this.addressSearchLoading = true;
+ (await this.userService.getAccountByAddress(this.addressSearchFormStub.address.value, 100)).subscribe(async res => {
+ if (res !== undefined) {
+ await this.router.navigateByUrl(`/accounts/${strip0x(res.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`);
+ } else {
+ alert('Account not found!');
+ }
+ });
+ this.addressSearchLoading = false;
+ }
+}
+
+ <!-- Begin page -->
+<div class="wrapper">
+ <app-sidebar></app-sidebar>
+
+ <!-- ============================================================== -->
+ <!-- Start Page Content here -->
+ <!-- ============================================================== -->
+
+ <div id="content">
+ <app-topbar></app-topbar>
+ <!-- Start Content-->
+ <div class="container-fluid" appMenuSelection>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
+ <li class="breadcrumb-item"><a routerLink="/accounts">Accounts</a></li>
+ <li class="breadcrumb-item active" aria-current="page">Search</li>
+ </ol>
+ </nav>
+ <div class="card">
+ <mat-card-title class="card-header">
+ Accounts
+ </mat-card-title>
+ <div class="card-body">
+ <mat-tab-group>
+ <mat-tab label="Phone Number">
+ <form [formGroup]="phoneSearchForm" (ngSubmit)="onPhoneSearch()">
+ <mat-form-field appearance="outline">
+ <mat-label> Search </mat-label>
+ <input matInput type="text" placeholder="Search by phone number" formControlName="phoneNumber" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="phoneSearchSubmitted && phoneSearchFormStub.phoneNumber.errors">Phone Number is required.</mat-error>
+ <mat-icon matSuffix>phone</mat-icon>
+ <mat-hint>Phone Number</mat-hint>
+ </mat-form-field>
+ <button mat-raised-button color="primary" type="submit" class="btn btn-outline-primary ml-3"> SEARCH </button>
+ </form>
+ </mat-tab>
+ <mat-tab label="Account Address">
+ <form [formGroup]="addressSearchForm" (ngSubmit)="onAddressSearch()">
+ <mat-form-field appearance="outline">
+ <mat-label> Search </mat-label>
+ <input matInput type="text" placeholder="Search by account address" formControlName="address" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="addressSearchSubmitted && addressSearchFormStub.address.errors">Account Address is required.</mat-error>
+ <mat-icon matSuffix>view_in_ar</mat-icon>
+ <mat-hint>Account Address</mat-hint>
+ </mat-form-field>
+ <button mat-raised-button color="primary" type="submit" class="btn btn-outline-primary ml-3"> SEARCH </button>
+ </form>
+ </mat-tab>
+ </mat-tab-group>
+ </div>
+ </div>
+ </div>
+ <app-footer appMenuSelection></app-footer>
+ </div>
+ <!-- ============================================================== -->
+ <!-- End Page content -->
+ <!-- ============================================================== -->
+</div>
+
+
+ ./account-search.component.scss
+
+ +
+ src/app/pages/accounts/accounts.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-accounts |
+
styleUrls | +./accounts.component.scss |
+
templateUrl | +./accounts.component.html |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(userService: UserService, loggingService: LoggingService, router: Router)
+ |
+ ||||||||||||
+ + | +||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + doFilter + + + + | +||||||
+doFilter(value: string)
+ |
+ ||||||
+ + | +||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + downloadCsv + + + + | +
+downloadCsv()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + filterAccounts + + + + | +
+filterAccounts()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + refreshPaginator + + + + | +
+refreshPaginator()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + accounts + + + | +
+ Type : Array<AccountDetails>
+
+ |
+
+ Default value : []
+ |
+
+ + | +
+ + + + accountsType + + + | +
+ Type : string
+
+ |
+
+ Default value : 'all'
+ |
+
+ + | +
+ + + + accountTypes + + + | +
+ Type : Array<string>
+
+ |
+
+ + | +
+ + + + dataSource + + + | +
+ Type : MatTableDataSource<any>
+
+ |
+
+ + | +
+ + + + defaultPageSize + + + | +
+ Type : number
+
+ |
+
+ Default value : 10
+ |
+
+ + | +
+ + + + displayedColumns + + + | +
+ Type : Array<string>
+
+ |
+
+ Default value : ['name', 'phone', 'created', 'balance', 'location']
+ |
+
+ + | +
+ + + + pageSizeOptions + + + | +
+ Type : Array<number>
+
+ |
+
+ Default value : [10, 20, 50, 100]
+ |
+
+ + | +
+ + + + paginator + + + | +
+ Type : MatPaginator
+
+ |
+
+ Decorators :
+ +
+ @ViewChild(MatPaginator)
+ |
+
+ + | +
+ + + + sort + + + | +
+ Type : MatSort
+
+ |
+
+ Decorators :
+ +
+ @ViewChild(MatSort)
+ |
+
+ + | +
import {ChangeDetectionStrategy, Component, OnInit, ViewChild} from '@angular/core';
+import {MatTableDataSource} from '@angular/material/table';
+import {MatPaginator} from '@angular/material/paginator';
+import {MatSort} from '@angular/material/sort';
+import {LoggingService, UserService} from '@app/_services';
+import {Router} from '@angular/router';
+import {exportCsv} from '@app/_helpers';
+import {strip0x} from '@src/assets/js/ethtx/dist/hex';
+import {first} from 'rxjs/operators';
+import {environment} from '@src/environments/environment';
+import {AccountDetails} from '@app/_models';
+
+@Component({
+ selector: 'app-accounts',
+ templateUrl: './accounts.component.html',
+ styleUrls: ['./accounts.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class AccountsComponent implements OnInit {
+ dataSource: MatTableDataSource<any>;
+ accounts: Array<AccountDetails> = [];
+ displayedColumns: Array<string> = ['name', 'phone', 'created', 'balance', 'location'];
+ defaultPageSize: number = 10;
+ pageSizeOptions: Array<number> = [10, 20, 50, 100];
+ accountsType: string = 'all';
+ accountTypes: Array<string>;
+
+ @ViewChild(MatPaginator) paginator: MatPaginator;
+ @ViewChild(MatSort) sort: MatSort;
+
+ constructor(
+ private userService: UserService,
+ private loggingService: LoggingService,
+ private router: Router
+ )
+ {
+ (async () => {
+ try {
+ // TODO it feels like this should be in the onInit handler
+ await this.userService.loadAccounts(100);
+ } catch (error) {
+ this.loggingService.sendErrorLevelMessage('Failed to load accounts', this, {error});
+ }
+ })();
+ this.userService.getAccountTypes().pipe(first()).subscribe(res => this.accountTypes = res);
+ }
+
+ ngOnInit(): void {
+ this.userService.accountsSubject.subscribe(accounts => {
+ this.dataSource = new MatTableDataSource<any>(accounts);
+ this.dataSource.paginator = this.paginator;
+ this.dataSource.sort = this.sort;
+ this.accounts = accounts;
+ });
+ }
+
+ doFilter(value: string): void {
+ this.dataSource.filter = value.trim().toLocaleLowerCase();
+ }
+
+ async viewAccount(account): Promise<void> {
+ await this.router.navigateByUrl(`/accounts/${strip0x(account.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`);
+ }
+
+ filterAccounts(): void {
+ if (this.accountsType === 'all') {
+ this.userService.accountsSubject.subscribe(accounts => {
+ this.dataSource.data = accounts;
+ this.accounts = accounts;
+ });
+ } else {
+ this.dataSource.data = this.accounts.filter(account => account.type === this.accountsType);
+ }
+ }
+
+ refreshPaginator(): void {
+ if (!this.dataSource.paginator) {
+ this.dataSource.paginator = this.paginator;
+ }
+
+ this.paginator._changePageSize(this.paginator.pageSize);
+ }
+
+ downloadCsv(): void {
+ exportCsv(this.accounts, 'accounts');
+ }
+}
+
+ <!-- Begin page -->
+<div class="wrapper">
+ <app-sidebar></app-sidebar>
+
+ <!-- ============================================================== -->
+ <!-- Start Page Content here -->
+ <!-- ============================================================== -->
+
+ <div id="content">
+ <app-topbar></app-topbar>
+ <!-- Start Content-->
+ <div class="container-fluid" appMenuSelection>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
+ <li class="breadcrumb-item active" aria-current="page">Accounts</li>
+ </ol>
+ </nav>
+ <div class="card">
+ <mat-card-title class="card-header">
+ Accounts
+ </mat-card-title>
+ <div class="card-body">
+ <div class="row card-header">
+ <mat-form-field appearance="outline">
+ <mat-label> ACCOUNT TYPE </mat-label>
+ <mat-select id="typeSelect" [(value)]="accountsType" (selectionChange)="filterAccounts()">
+ <mat-option value="all">ALL</mat-option>
+ <mat-option *ngFor="let accountType of accountTypes" [value]="accountType">
+ {{accountType | uppercase}}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary ml-auto mr-2" routerLink="/accounts/search"> SEARCH </button>
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary mr-2" (click)="downloadCsv()"> EXPORT </button>
+ </div>
+
+ <mat-form-field appearance="outline">
+ <mat-label> Filter </mat-label>
+ <input matInput type="text" (keyup)="doFilter($event.target.value)" placeholder="Filter">
+ <mat-icon matSuffix>search</mat-icon>
+ </mat-form-field>
+
+ <mat-table class="mat-elevation-z10" [dataSource]="dataSource" matSort matSortActive="created"
+ matSortDirection="desc" matSortDisableClear>
+
+ <ng-container matColumnDef="name">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> NAME </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user?.vcard.fn[0].value}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="phone">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> PHONE NUMBER </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user?.vcard.tel[0].value}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="created">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> CREATED </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user?.date_registered | date}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="balance">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> BALANCE </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user?.balance | tokenRatio}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="location">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> LOCATION </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user?.location.area_name}} </mat-cell>
+ </ng-container>
+
+ <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
+ <mat-row *matRowDef="let account; columns: displayedColumns" (click)="viewAccount(account)" matRipple></mat-row>
+ </mat-table>
+
+ <mat-paginator [pageSize]="defaultPageSize" [pageSizeOptions]="pageSizeOptions" showFirstLastButtons></mat-paginator>
+ </div>
+ </div>
+ </div>
+ <app-footer appMenuSelection></app-footer>
+ </div>
+ <!-- ============================================================== -->
+ <!-- End Page content -->
+ <!-- ============================================================== -->
+</div>
+
+
+ ./accounts.component.scss
+
+ +
+ src/app/pages/admin/admin.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-admin |
+
styleUrls | +./admin.component.scss |
+
templateUrl | +./admin.component.html |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(userService: UserService, loggingService: LoggingService)
+ |
+ |||||||||
+ Defined in src/app/pages/admin/admin.component.ts:31
+ |
+ |||||||||
+
+ Parameters :
+
+
|
+
+ + + + approvalStatus + + + + | +||||||
+approvalStatus(status: boolean)
+ |
+ ||||||
+ Defined in src/app/pages/admin/admin.component.ts:53
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ string
+
+
+
+
+ |
+
+ + + + approveAction + + + + | +||||||
+approveAction(action: any)
+ |
+ ||||||
+ Defined in src/app/pages/admin/admin.component.ts:57
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + disapproveAction + + + + | +||||||
+disapproveAction(action: any)
+ |
+ ||||||
+ Defined in src/app/pages/admin/admin.component.ts:63
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + doFilter + + + + | +||||||
+doFilter(value: string)
+ |
+ ||||||
+ Defined in src/app/pages/admin/admin.component.ts:49
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + downloadCsv + + + + | +
+downloadCsv()
+ |
+
+ Defined in src/app/pages/admin/admin.component.ts:73
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + expandCollapse + + + + | +||||
+expandCollapse(row)
+ |
+ ||||
+ Defined in src/app/pages/admin/admin.component.ts:69
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ Defined in src/app/pages/admin/admin.component.ts:46
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + action + + + | +
+ Type : Action
+
+ |
+
+ Defined in src/app/pages/admin/admin.component.ts:27
+ |
+
+ + + + actions + + + | +
+ Type : Array<Action>
+
+ |
+
+ Defined in src/app/pages/admin/admin.component.ts:28
+ |
+
+ + + + dataSource + + + | +
+ Type : MatTableDataSource<any>
+
+ |
+
+ Defined in src/app/pages/admin/admin.component.ts:25
+ |
+
+ + + + displayedColumns + + + | +
+ Type : Array<string>
+
+ |
+
+ Default value : ['expand', 'user', 'role', 'action', 'status', 'approve']
+ |
+
+ Defined in src/app/pages/admin/admin.component.ts:26
+ |
+
+ + + + paginator + + + | +
+ Type : MatPaginator
+
+ |
+
+ Decorators :
+ +
+ @ViewChild(MatPaginator)
+ |
+
+ Defined in src/app/pages/admin/admin.component.ts:30
+ |
+
+ + + + sort + + + | +
+ Type : MatSort
+
+ |
+
+ Decorators :
+ +
+ @ViewChild(MatSort)
+ |
+
+ Defined in src/app/pages/admin/admin.component.ts:31
+ |
+
import {ChangeDetectionStrategy, Component, OnInit, ViewChild} from '@angular/core';
+import {MatTableDataSource} from '@angular/material/table';
+import {MatPaginator} from '@angular/material/paginator';
+import {MatSort} from '@angular/material/sort';
+import {LoggingService, UserService} from '@app/_services';
+import {animate, state, style, transition, trigger} from '@angular/animations';
+import {first} from 'rxjs/operators';
+import {exportCsv} from '@app/_helpers';
+import {Action} from '../../_models';
+
+@Component({
+ selector: 'app-admin',
+ templateUrl: './admin.component.html',
+ styleUrls: ['./admin.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ animations: [
+ trigger('detailExpand', [
+ state('collapsed', style({height: '0px', minHeight: 0, visibility: 'hidden'})),
+ state('expanded', style({height: '*', visibility: 'visible'})),
+ transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
+ ])
+ ]
+})
+export class AdminComponent implements OnInit {
+ dataSource: MatTableDataSource<any>;
+ displayedColumns: Array<string> = ['expand', 'user', 'role', 'action', 'status', 'approve'];
+ action: Action;
+ actions: Array<Action>;
+
+ @ViewChild(MatPaginator) paginator: MatPaginator;
+ @ViewChild(MatSort) sort: MatSort;
+
+ constructor(
+ private userService: UserService,
+ private loggingService: LoggingService
+ ) {
+ this.userService.getActions();
+ this.userService.actionsSubject.subscribe(actions => {
+ this.dataSource = new MatTableDataSource<any>(actions);
+ this.dataSource.paginator = this.paginator;
+ this.dataSource.sort = this.sort;
+ this.actions = actions;
+ });
+ }
+
+ ngOnInit(): void {
+ }
+
+ doFilter(value: string): void {
+ this.dataSource.filter = value.trim().toLocaleLowerCase();
+ }
+
+ approvalStatus(status: boolean): string {
+ return status ? 'Approved' : 'Unapproved';
+ }
+
+ approveAction(action: any): void {
+ if (!confirm('Approve action?')) { return; }
+ this.userService.approveAction(action.id).pipe(first()).subscribe(res => this.loggingService.sendInfoLevelMessage(res));
+ this.userService.getActions();
+ }
+
+ disapproveAction(action: any): void {
+ if (!confirm('Disapprove action?')) { return; }
+ this.userService.revokeAction(action.id).pipe(first()).subscribe(res => this.loggingService.sendInfoLevelMessage(res));
+ this.userService.getActions();
+ }
+
+ expandCollapse(row): void {
+ row.isExpanded = !row.isExpanded;
+ }
+
+ downloadCsv(): void {
+ exportCsv(this.actions, 'actions');
+ }
+}
+
+ <!-- Begin page -->
+<div class="wrapper">
+ <app-sidebar></app-sidebar>
+
+ <!-- ============================================================== -->
+ <!-- Start Page Content here -->
+ <!-- ============================================================== -->
+
+ <div id="content">
+ <app-topbar></app-topbar>
+ <!-- Start Content-->
+ <div class="container-fluid" appMenuSelection>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
+ <li class="breadcrumb-item active" aria-current="page">Admin</li>
+ </ol>
+ </nav>
+ <div class="card">
+ <mat-card-title class="card-header">
+ <div class="row">
+ Actions
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary ml-auto mr-2" (click)="downloadCsv()"> EXPORT </button>
+ </div>
+ </mat-card-title>
+ <div class="card-body">
+
+ <mat-form-field appearance="outline">
+ <mat-label> Filter </mat-label>
+ <input matInput type="text" (keyup)="doFilter($event.target.value)" placeholder="Filter">
+ <mat-icon matSuffix>search</mat-icon>
+ </mat-form-field>
+
+ <mat-table class="mat-elevation-z10" [dataSource]="dataSource" multiTemplateDataRows>
+
+ <!-- Expand Column -->
+ <ng-container matColumnDef="expand">
+ <mat-header-cell *matHeaderCellDef> Expand </mat-header-cell>
+ <mat-cell *matCellDef="let element" (click)="expandCollapse(element)">
+ <span *ngIf="!element.isExpanded" class="signs"> + </span>
+ <span *ngIf="element.isExpanded" class="signs"> - </span>
+ </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="user">
+ <mat-header-cell *matHeaderCellDef> NAME </mat-header-cell>
+ <mat-cell *matCellDef="let action"> {{action.user}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="role">
+ <mat-header-cell *matHeaderCellDef> ROLE </mat-header-cell>
+ <mat-cell *matCellDef="let action"> {{action.role}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="action">
+ <mat-header-cell *matHeaderCellDef> ACTION </mat-header-cell>
+ <mat-cell *matCellDef="let action"> {{action.action}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="status">
+ <mat-header-cell *matHeaderCellDef> STATUS </mat-header-cell>
+ <mat-cell *matCellDef="let action">
+ <span *ngIf="action.approval == true" class="badge badge-success badge-pill"> {{approvalStatus(action.approval)}} </span>
+ <span *ngIf="action.approval == false" class="badge badge-danger badge-pill"> {{approvalStatus(action.approval)}} </span>
+ </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="approve">
+ <mat-header-cell *matHeaderCellDef> APPROVE </mat-header-cell>
+ <mat-cell *matCellDef="let action">
+ <button mat-raised-button color="primary" *ngIf="!action.approval" class="btn btn-outline-success" (click)="approveAction(action)"> Approve </button>
+ <button mat-raised-button color="warn" *ngIf="action.approval" class="btn btn-outline-danger" (click)="disapproveAction(action)"> Disapprove </button>
+ </mat-cell>
+ </ng-container>
+
+ <!-- Expanded Content Column - The detail row is made up of this one column -->
+ <ng-container matColumnDef="expandedDetail">
+ <mat-cell *matCellDef="let action">
+ <div>
+ <span><strong>Staff Name:</strong> {{action.user}}</span><br>
+ <span><strong>Role:</strong> {{action.role}}</span><br>
+ <span><strong>Action Details:</strong> {{action.action}}</span><br>
+ <span><strong>Approval Status:</strong> {{action.approval}}</span><br>
+ </div>
+ </mat-cell>
+ </ng-container>
+
+ <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
+ <mat-row *matRowDef="let row; columns: displayedColumns;" matRipple class="element-row"
+ [class.expanded]="row.isExpanded"></mat-row>
+ <mat-row *matRowDef="let row; columns: ['expandedDetail'];"
+ [@detailExpand]="row.isExpanded == true ? 'expanded': 'collapsed'" style="overflow: hidden"></mat-row>
+ </mat-table>
+
+ <mat-paginator [pageSize]="10" [pageSizeOptions]="[10, 20, 50, 100]" showFirstLastButtons></mat-paginator>
+ </div>
+ </div>
+ </div>
+ <app-footer appMenuSelection></app-footer>
+ </div>
+ <!-- ============================================================== -->
+ <!-- End Page content -->
+ <!-- ============================================================== -->
+</div>
+
+
+
+ ./admin.component.scss
+
button {
+ width: 6rem;
+}
+
+ +
+ src/app/app.component.ts
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-root |
+
styleUrls | +./app.component.scss |
+
templateUrl | +./app.component.html |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+ HostListeners+ |
+
+
|
+
+constructor(authService: AuthService, transactionService: TransactionService, loggingService: LoggingService, errorDialogService: ErrorDialogService)
+ |
+ |||||||||||||||
+ Defined in src/app/app.component.ts:15
+ |
+ |||||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + window:cic_convert + + + + | +
+ Arguments : '$event'
+ |
+
+window:cic_convert(event: CustomEvent)
+ |
+
+ Defined in src/app/app.component.ts:74
+ |
+
+ + + + window:cic_transfer + + + + | +
+ Arguments : '$event'
+ |
+
+window:cic_transfer(event: CustomEvent)
+ |
+
+ Defined in src/app/app.component.ts:68
+ |
+
+ + + + onResize + + + + | +||||
+onResize(e)
+ |
+ ||||
+ Defined in src/app/app.component.ts:43
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + mediaQuery + + + | +
+ Type : MediaQueryList
+
+ |
+
+ Default value : window.matchMedia('(max-width: 768px)')
+ |
+
+ Defined in src/app/app.component.ts:15
+ |
+
+ + + + readyState + + + | +
+ Type : number
+
+ |
+
+ Default value : 0
+ |
+
+ Defined in src/app/app.component.ts:14
+ |
+
+ + + + readyStateTarget + + + | +
+ Type : number
+
+ |
+
+ Default value : 3
+ |
+
+ Defined in src/app/app.component.ts:13
+ |
+
+ + + + title + + + | +
+ Type : string
+
+ |
+
+ Default value : 'CICADA'
+ |
+
+ Defined in src/app/app.component.ts:12
+ |
+
import {ChangeDetectionStrategy, Component, HostListener} from '@angular/core';
+import {AuthService, ErrorDialogService, LoggingService, TransactionService} from '@app/_services';
+import {catchError} from 'rxjs/operators';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: './app.component.html',
+ styleUrls: ['./app.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class AppComponent {
+ title = 'CICADA';
+ readyStateTarget: number = 3;
+ readyState: number = 0;
+ mediaQuery: MediaQueryList = window.matchMedia('(max-width: 768px)');
+
+ constructor(
+ private authService: AuthService,
+ private transactionService: TransactionService,
+ private loggingService: LoggingService,
+ private errorDialogService: ErrorDialogService
+ ) {
+ (async () => {
+ try {
+ await this.authService.init();
+ // this.authService.getPublicKeys()
+ // .pipe(catchError(async (error) => {
+ // this.loggingService.sendErrorLevelMessage('Unable to load trusted public keys.', this, {error});
+ // this.errorDialogService.openDialog({message: 'Trusted keys endpoint can\'t be reached. Please try again later.'});
+ // })).subscribe(this.authService.mutableKeyStore.importPublicKey);
+ const publicKeys = await this.authService.getPublicKeys();
+ await this.authService.mutableKeyStore.importPublicKey(publicKeys);
+ } catch (error) {
+ this.errorDialogService.openDialog({message: 'Trusted keys endpoint can\'t be reached. Please try again later.'});
+ // TODO do something to halt user progress...show a sad cicada page 🦗?
+ }
+ })();
+ this.mediaQuery.addListener(this.onResize);
+ this.onResize(this.mediaQuery);
+ }
+
+ // Load resize
+ onResize(e): void {
+ const sidebar: HTMLElement = document.getElementById('sidebar');
+ const content: HTMLElement = document.getElementById('content');
+ const sidebarCollapse: HTMLElement = document.getElementById('sidebarCollapse');
+ if (sidebarCollapse?.classList.contains('active')) {
+ sidebarCollapse?.classList.remove('active');
+ }
+ if (e.matches) {
+ if (!sidebar?.classList.contains('active')) {
+ sidebar?.classList.add('active');
+ }
+ if (!content?.classList.contains('active')) {
+ content?.classList.add('active');
+ }
+ } else {
+ if (sidebar?.classList.contains('active')) {
+ sidebar?.classList.remove('active');
+ }
+ if (content?.classList.contains('active')) {
+ content?.classList.remove('active');
+ }
+ }
+ }
+
+ @HostListener('window:cic_transfer', ['$event'])
+ async cicTransfer(event: CustomEvent): Promise<void> {
+ const transaction: any = event.detail.tx;
+ await this.transactionService.setTransaction(transaction, 100);
+ }
+
+ @HostListener('window:cic_convert', ['$event'])
+ async cicConvert(event: CustomEvent): Promise<void> {
+ const conversion: any = event.detail.tx;
+ await this.transactionService.setConversion(conversion, 100);
+ }
+}
+
+ <router-outlet (activate)="onResize(mediaQuery)"></router-outlet>
+
+
+ ./app.component.scss
+
+ +
+ src/app/auth/auth.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-auth |
+
styleUrls | +./auth.component.scss |
+
templateUrl | +./auth.component.html |
+
+ Properties+ |
+
+ + | +
+ Methods+ |
+
+
|
+
+ Accessors+ |
+
+
|
+
+constructor(authService: AuthService, formBuilder: FormBuilder, router: Router)
+ |
+ ||||||||||||
+ Defined in src/app/auth/auth.component.ts:17
+ |
+ ||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + login + + + + | +
+login()
+ |
+
+ Defined in src/app/auth/auth.component.ts:48
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ Defined in src/app/auth/auth.component.ts:25
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + Async + onSubmit + + + + | +
+
+ onSubmit()
+ |
+
+ Defined in src/app/auth/auth.component.ts:38
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + switchWindows + + + + | +
+switchWindows()
+ |
+
+ Defined in src/app/auth/auth.component.ts:59
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + toggleDisplay + + + + | +||||||
+toggleDisplay(element: any)
+ |
+ ||||||
+ Defined in src/app/auth/auth.component.ts:67
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + keyForm + + + | +
+ Type : FormGroup
+
+ |
+
+ Defined in src/app/auth/auth.component.ts:14
+ |
+
+ + + + loading + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ Defined in src/app/auth/auth.component.ts:16
+ |
+
+ + + + matcher + + + | +
+ Type : CustomErrorStateMatcher
+
+ |
+
+ Default value : new CustomErrorStateMatcher()
+ |
+
+ Defined in src/app/auth/auth.component.ts:17
+ |
+
+ + + + submitted + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ Defined in src/app/auth/auth.component.ts:15
+ |
+
+ + keyFormStub + | +
+ getkeyFormStub()
+ |
+
+ Defined in src/app/auth/auth.component.ts:36
+ |
+
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {CustomErrorStateMatcher} from '@app/_helpers';
+import {AuthService} from '@app/_services';
+import {Router} from '@angular/router';
+
+@Component({
+ selector: 'app-auth',
+ templateUrl: './auth.component.html',
+ styleUrls: ['./auth.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class AuthComponent implements OnInit {
+ keyForm: FormGroup;
+ submitted: boolean = false;
+ loading: boolean = false;
+ matcher: CustomErrorStateMatcher = new CustomErrorStateMatcher();
+
+ constructor(
+ private authService: AuthService,
+ private formBuilder: FormBuilder,
+ private router: Router
+ ) { }
+
+ async ngOnInit(): Promise<void> {
+ this.keyForm = this.formBuilder.group({
+ key: ['', Validators.required],
+ });
+ await this.authService.init();
+ // if (this.authService.privateKey !== undefined) {
+ // const setKey = await this.authService.setKey(this.authService.privateKey);
+ // }
+ // }
+ }
+
+ get keyFormStub(): any { return this.keyForm.controls; }
+
+ async onSubmit(): Promise<void> {
+ this.submitted = true;
+
+ if (this.keyForm.invalid) { return; }
+
+ this.loading = true;
+ await this.authService.setKey(this.keyFormStub.key.value);
+ this.loading = false;
+ }
+
+ login(): void {
+ // TODO check if we have privatekey
+ // Send us to home if we have a private key
+ // talk to meta somehow
+ // in the error interceptor if 401/403 handle it
+ // if 200 go /home
+ if (this.authService.getPrivateKey()) {
+ this.router.navigate(['/home']);
+ }
+ }
+
+ switchWindows(): void {
+ this.authService.sessionToken = undefined;
+ const divOne: HTMLElement = document.getElementById('one');
+ const divTwo: HTMLElement = document.getElementById('two');
+ this.toggleDisplay(divOne);
+ this.toggleDisplay(divTwo);
+ }
+
+ toggleDisplay(element: any): void {
+ const style: string = window.getComputedStyle(element).display;
+ if (style === 'block') {
+ element.style.display = 'none';
+ } else {
+ element.style.display = 'block';
+ }
+ }
+}
+
+ <div class="container">
+ <div class="row justify-content-center mt-5 mb-5">
+ <div class="col-lg-6 col-md-8 col-sm-10">
+ <div class="card">
+ <mat-card-title class="card-header pt-4 pb-4 text-center bg-dark">
+ <a routerLink="/">
+ <h1 class="text-white">CICADA</h1>
+ </a>
+ </mat-card-title>
+ <div id="one" style="display: block" class="card-body p-4">
+
+ <div class="text-center w-75 m-auto">
+ <h4 class="text-dark-50 text-center font-weight-bold">Add Private Key</h4>
+ </div>
+
+ <form [formGroup]="keyForm" (ngSubmit)="onSubmit()">
+
+ <mat-form-field appearance="outline" class="full-width">
+ <mat-label>Private Key</mat-label>
+ <textarea matInput style="height: 30rem" formControlName="key" placeholder="Enter your private key..."
+ [errorStateMatcher]="matcher"></textarea>
+ <div *ngIf="submitted && keyFormStub.key.errors" class="invalid-feedback">
+ <mat-error *ngIf="keyFormStub.key.errors.required">Private Key is required.</mat-error>
+ </div>
+ </mat-form-field>
+
+ <button mat-raised-button matRipple color="primary" type="submit" [disabled]="loading">
+ <span *ngIf="loading" class="spinner-border spinner-border-sm mr-1"></span>
+ Add Key
+ </button>
+
+ </form>
+ </div>
+ <div id="two" style="display: none" class="card-body p-4 align-items-center">
+
+ <div class="text-center w-75 m-auto">
+ <h4 id="state" class="text-dark-50 text-center font-weight-bold"></h4>
+ <button mat-raised-button matRipple color="primary" type="submit" (click)="login()"> Login </button>
+ </div>
+
+ <div class="row mt-3">
+ <div class="col-12 text-center">
+ <p class="text-muted">Change private key? <a (click)="switchWindows()" class="text-muted ml-1"><b>Enter private key</b></a></p>
+ </div> <!-- end col-->
+ </div>
+ <!-- end row -->
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+
+ ./auth.component.scss
+
+ +
+ src/app/pages/accounts/create-account/create-account.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-create-account |
+
styleUrls | +./create-account.component.scss |
+
templateUrl | +./create-account.component.html |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+ + | +
+ Accessors+ |
+
+
|
+
+constructor(formBuilder: FormBuilder, locationService: LocationService, userService: UserService)
+ |
+ ||||||||||||
+ + | +||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + onSubmit + + + + | +
+onSubmit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + accountTypes + + + | +
+ Type : Array<string>
+
+ |
+
+ + | +
+ + + + areaNames + + + | +
+ Type : Array<AreaName>
+
+ |
+
+ + | +
+ + + + categories + + + | +
+ Type : Array<Category>
+
+ |
+
+ + | +
+ + + + createForm + + + | +
+ Type : FormGroup
+
+ |
+
+ + | +
+ + + + genders + + + | +
+ Type : Array<string>
+
+ |
+
+ + | +
+ + + + matcher + + + | +
+ Type : CustomErrorStateMatcher
+
+ |
+
+ Default value : new CustomErrorStateMatcher()
+ |
+
+ + | +
+ + + + submitted + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ + | +
+ + createFormStub + | +
+ getcreateFormStub()
+ |
+
+ + | +
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {LocationService, UserService} from '@app/_services';
+import {CustomErrorStateMatcher} from '@app/_helpers';
+import {first} from 'rxjs/operators';
+import {AreaName, Category} from '@app/_models';
+
+@Component({
+ selector: 'app-create-account',
+ templateUrl: './create-account.component.html',
+ styleUrls: ['./create-account.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class CreateAccountComponent implements OnInit {
+ createForm: FormGroup;
+ matcher: CustomErrorStateMatcher = new CustomErrorStateMatcher();
+ submitted: boolean = false;
+ categories: Array<Category>;
+ areaNames: Array<AreaName>;
+ accountTypes: Array<string>;
+ genders: Array<string>;
+
+ constructor(
+ private formBuilder: FormBuilder,
+ private locationService: LocationService,
+ private userService: UserService
+ ) { }
+
+ ngOnInit(): void {
+ this.createForm = this.formBuilder.group({
+ accountType: ['', Validators.required],
+ idNumber: ['', Validators.required],
+ phoneNumber: ['', Validators.required],
+ givenName: ['', Validators.required],
+ surname: ['', Validators.required],
+ directoryEntry: ['', Validators.required],
+ location: ['', Validators.required],
+ gender: ['', Validators.required],
+ referrer: ['', Validators.required],
+ businessCategory: ['', Validators.required]
+ });
+ this.userService.getCategories().pipe(first()).subscribe(res => this.categories = res);
+ this.locationService.getAreaNames().pipe(first()).subscribe(res => this.areaNames = res);
+ this.userService.getAccountTypes().pipe(first()).subscribe(res => this.accountTypes = res);
+ this.userService.getGenders().pipe(first()).subscribe(res => this.genders = res);
+ }
+
+ get createFormStub(): any { return this.createForm.controls; }
+
+ onSubmit(): void {
+ this.submitted = true;
+ if (this.createForm.invalid || !confirm('Create account?')) { return; }
+ this.submitted = false;
+ }
+}
+
+ <!-- Begin page -->
+<div class="wrapper">
+ <app-sidebar></app-sidebar>
+
+ <!-- ============================================================== -->
+ <!-- Start Page Content here -->
+ <!-- ============================================================== -->
+
+ <div id="content">
+ <app-topbar></app-topbar>
+ <!-- Start Content-->
+ <div class="container-fluid" appMenuSelection>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
+ <li class="breadcrumb-item"><a routerLink="/accounts">Accounts</a></li>
+ <li class="breadcrumb-item active" aria-current="page">Create Account</li>
+ </ol>
+ </nav>
+ <div class="card">
+ <mat-card-title class="card-header text-center">
+ CREATE A USER ACCOUNT
+ </mat-card-title>
+ <div class="card-body">
+ <form class="row form-inline" [formGroup]="createForm" (ngSubmit)="onSubmit()">
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Account Type: </mat-label>
+ <mat-select id="accountType" formControlName="accountType" [errorStateMatcher]="matcher">
+ <mat-option *ngFor="let accountType of accountTypes" [value]="accountType">
+ {{accountType | uppercase}}
+ </mat-option>
+ </mat-select>
+ <mat-error *ngIf="submitted && createFormStub.accountType.errors">Account type is required.</mat-error>
+ </mat-form-field><br>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>ID Number: </mat-label>
+ <input matInput type="text" id="idNumber" placeholder="ID Number" formControlName="idNumber" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && createFormStub.idNumber.errors">ID Number is required.</mat-error>
+ </mat-form-field>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Phone Number: </mat-label>
+ <input matInput type="text" id="phoneNumber" placeholder="Phone Number" formControlName="phoneNumber" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && createFormStub.phoneNumber.errors">Phone Number is required.</mat-error>
+ </mat-form-field><br>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Given Name(s):* </mat-label>
+ <input matInput type="text" id="givenNames" placeholder="Given Names" formControlName="givenName" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && createFormStub.givenName.errors">Given Names are required.</mat-error>
+ </mat-form-field><br>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Family/Surname: </mat-label>
+ <input matInput type="text" id="surname" placeholder="Surname" formControlName="surname" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && createFormStub.surname.errors">Surname is required.</mat-error>
+ </mat-form-field><br>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Directory Entry: </mat-label>
+ <input matInput type="text" id="directoryEntry" placeholder="Directory Entry" formControlName="directoryEntry" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && createFormStub.directoryEntry.errors">Directory Entry is required.</mat-error>
+ </mat-form-field><br>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Location: </mat-label>
+ <mat-select id="location" formControlName="location" [errorStateMatcher]="matcher">
+ <mat-option *ngFor="let area of areaNames" [value]="area">
+ {{area | uppercase}}
+ </mat-option>
+ </mat-select>
+ <mat-error *ngIf="submitted && createFormStub.location.errors">Location is required.</mat-error>
+ </mat-form-field><br>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Gender: </mat-label>
+ <mat-select id="gender" formControlName="gender" [errorStateMatcher]="matcher">
+ <mat-option *ngFor="let gender of genders" [value]="gender">
+ {{gender | uppercase}}
+ </mat-option>
+ </mat-select>
+ <mat-error *ngIf="submitted && createFormStub.gender.errors">Gender is required.</mat-error>
+ </mat-form-field><br>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Referrer Phone Number: </mat-label>
+ <input matInput type="text" id="referredBy" placeholder="Reffered By" formControlName="referrer" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && createFormStub.referrer.errors">Referrer is required.</mat-error>
+ </mat-form-field><br>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Business Category: </mat-label>
+ <mat-select id="businessCategory" formControlName="businessCategory" [errorStateMatcher]="matcher">
+ <mat-option *ngFor="let category of categories" [value]="category">
+ {{category | titlecase}}
+ </mat-option>
+ </mat-select>
+ <mat-error *ngIf="submitted && createFormStub.businessCategory.errors">Business Category is required.</mat-error>
+ </mat-form-field>
+ </div>
+
+ <button mat-raised-button color="primary" type="submit" class="btn btn-outline-primary ml-3" (click)="onSubmit()">Submit</button>
+ </form>
+ </div>
+ </div>
+ </div>
+ <app-footer appMenuSelection></app-footer>
+ </div>
+ <!-- ============================================================== -->
+ <!-- End Page content -->
+ <!-- ============================================================== -->
+</div>
+
+
+ ./create-account.component.scss
+
+ +
+ src/app/shared/error-dialog/error-dialog.component.ts
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-error-dialog |
+
styleUrls | +./error-dialog.component.scss |
+
templateUrl | +./error-dialog.component.html |
+
+ Properties+ |
+
+
|
+
+constructor(data: any)
+ |
+ ||||||
+ + | +||||||
+
+ Parameters :
+
+
|
+
+ + + + Public + data + + + | +
+ Type : any
+
+ |
+
+ Decorators :
+ +
+ @Inject(MAT_DIALOG_DATA)
+ |
+
+ + | +
import {Component, ChangeDetectionStrategy, Inject} from '@angular/core';
+import {MAT_DIALOG_DATA} from '@angular/material/dialog';
+
+@Component({
+ selector: 'app-error-dialog',
+ templateUrl: './error-dialog.component.html',
+ styleUrls: ['./error-dialog.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ErrorDialogComponent {
+
+ constructor(@Inject(MAT_DIALOG_DATA) public data: any) { }
+
+}
+
+ <div>
+ <div>
+ <p>
+ Message: {{ data.message }}
+ </p>
+ <p *ngIf="data.status">
+ Status: {{ data?.status }}
+ </p>
+ </div>
+</div>
+
+
+ ./error-dialog.component.scss
+
+ +
+ src/app/shared/footer/footer.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-footer |
+
styleUrls | +./footer.component.scss |
+
templateUrl | +./footer.component.html |
+
+ Methods+ |
+
+
|
+
+constructor()
+ |
+
+ Defined in src/app/shared/footer/footer.component.ts:9
+ |
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ Defined in src/app/shared/footer/footer.component.ts:13
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
+
+@Component({
+ selector: 'app-footer',
+ templateUrl: './footer.component.html',
+ styleUrls: ['./footer.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FooterComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit(): void {
+ }
+
+}
+
+ <!-- Footer Start -->
+<footer class="footer">
+ 2020 © Grassroots Economics
+</footer>
+<!-- end Footer -->
+
+
+ ./footer.component.scss
+
+ +
+ src/testing/shared-module-stub.ts
+
selector | +app-footer |
+
import {Component} from '@angular/core';
+
+@Component({selector: 'app-sidebar', template: ''})
+export class SidebarStubComponent {}
+
+@Component({selector: 'app-topbar', template: ''})
+export class TopbarStubComponent {}
+
+@Component({selector: 'app-footer', template: ''})
+export class FooterStubComponent {}
+
+ +
+ src/app/pages/settings/organization/organization.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-organization |
+
styleUrls | +./organization.component.scss |
+
templateUrl | +./organization.component.html |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+ + | +
+ Accessors+ |
+
+
|
+
+constructor(formBuilder: FormBuilder)
+ |
+ ||||||
+ + | +||||||
+
+ Parameters :
+
+
|
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + onSubmit + + + + | +
+onSubmit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + matcher + + + | +
+ Type : CustomErrorStateMatcher
+
+ |
+
+ Default value : new CustomErrorStateMatcher()
+ |
+
+ + | +
+ + + + organizationForm + + + | +
+ Type : FormGroup
+
+ |
+
+ + | +
+ + + + submitted + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ + | +
+ + organizationFormStub + | +
+ getorganizationFormStub()
+ |
+
+ + | +
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+import {CustomErrorStateMatcher} from '@app/_helpers';
+
+@Component({
+ selector: 'app-organization',
+ templateUrl: './organization.component.html',
+ styleUrls: ['./organization.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class OrganizationComponent implements OnInit {
+ organizationForm: FormGroup;
+ submitted: boolean = false;
+ matcher: CustomErrorStateMatcher = new CustomErrorStateMatcher();
+
+ constructor(
+ private formBuilder: FormBuilder
+ ) { }
+
+ ngOnInit(): void {
+ this.organizationForm = this.formBuilder.group({
+ disbursement: ['', Validators.required],
+ transfer: '',
+ countryCode: ['', Validators.required]
+ });
+ }
+
+ get organizationFormStub(): any { return this.organizationForm.controls; }
+
+ onSubmit(): void {
+ this.submitted = true;
+ if (this.organizationForm.invalid || !confirm('Set organization information?')) { return; }
+ this.submitted = false;
+ }
+}
+
+ <!-- Begin page -->
+<div class="wrapper">
+ <app-sidebar></app-sidebar>
+
+ <!-- ============================================================== -->
+ <!-- Start Page Content here -->
+ <!-- ============================================================== -->
+
+ <div id="content">
+ <app-topbar></app-topbar>
+ <!-- Start Content-->
+ <div class="container-fluid" appMenuSelection>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
+ <li class="breadcrumb-item"><a routerLink="/settings">Settings</a></li>
+ <li class="breadcrumb-item active" aria-current="page">Organization Settings</li>
+ </ol>
+ </nav>
+ <div class="col-md-6 center-body">
+ <div class="card">
+ <mat-card-title class="card-header text-center">
+ DEFAULT ORGANISATION SETTINGS
+ </mat-card-title>
+ <div class="card-body">
+ <form [formGroup]="organizationForm" (ngSubmit)="onSubmit()">
+ <mat-form-field appearance="outline">
+ <mat-label>Default Disbursement *</mat-label>
+ <input matInput type="text" id="amount" placeholder="Amount" formControlName="disbursement"
+ [errorStateMatcher]="matcher">
+ <span matSuffix>RCU</span>
+ <mat-error *ngIf="submitted && organizationFormStub.disbursement.errors">
+ Default Disbursement is required.
+ </mat-error>
+ </mat-form-field>
+ <div class="form-group form-check">
+ <mat-checkbox id="transferCard" formControlName="transfer">Require Transfer Card *</mat-checkbox>
+ </div>
+ <mat-form-field appearance="outline">
+ <mat-label>Default Country Code *</mat-label>
+ <mat-select id="countryCode" formControlName="countryCode" [errorStateMatcher]="matcher">
+ <mat-option value="KE">KE Kenya</mat-option>
+ <mat-option value="US">US United States</mat-option>
+ <mat-option value="ETH">ETH Ethiopia</mat-option>
+ <mat-option value="GER">GER Germany</mat-option>
+ <mat-option value="UG">UG Uganda</mat-option>
+ </mat-select>
+ <mat-error *ngIf="submitted && organizationFormStub.countryCode.errors">
+ Country Code is required.
+ </mat-error>
+ </mat-form-field><br>
+ <button mat-raised-button color="primary" type="submit" class="btn btn-primary">Submit</button>
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+ <app-footer appMenuSelection></app-footer>
+ </div>
+ <!-- ============================================================== -->
+ <!-- End Page content -->
+ <!-- ============================================================== -->
+</div>
+
+
+ ./organization.component.scss
+
+ +
+ src/app/pages/pages.component.ts
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-pages |
+
styleUrls | +./pages.component.scss |
+
templateUrl | +./pages.component.html |
+
+ Properties+ |
+
+
|
+
+constructor()
+ |
+
+ Defined in src/app/pages/pages.component.ts:10
+ |
+
+ + + + url + + + | +
+ Type : string
+
+ |
+
+ Default value : 'https://dashboard.sarafu.network/'
+ |
+
+ Defined in src/app/pages/pages.component.ts:10
+ |
+
import {ChangeDetectionStrategy, Component} from '@angular/core';
+
+@Component({
+ selector: 'app-pages',
+ templateUrl: './pages.component.html',
+ styleUrls: ['./pages.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class PagesComponent {
+ url: string = 'https://dashboard.sarafu.network/';
+
+ constructor() { }
+}
+
+ <!-- Begin page -->
+<div class="wrapper">
+ <app-sidebar></app-sidebar>
+
+ <!-- ============================================================== -->
+ <!-- Start Page Content here -->
+ <!-- ============================================================== -->
+
+ <div id="content">
+ <app-topbar></app-topbar>
+ <!-- Start Content-->
+ <div class="container-fluid" appMenuSelection>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item active" aria-current="page">Home</li>
+ </ol>
+ </nav>
+ <div class="embed-responsive embed-responsive-16by9">
+ <iframe class="embed-responsive-item" [src]="url | safe" allow="fullscreen" loading="lazy"
+ title="Community inclusion currencies dashboard" referrerpolicy="no-referrer">
+ <p>
+ <a href="{{url}}"> Your browser does not support iframes. </a>
+ </p>
+ </iframe>
+ </div>
+ </div>
+ <app-footer appMenuSelection></app-footer>
+ </div>
+ <!-- ============================================================== -->
+ <!-- End Page content -->
+ <!-- ============================================================== -->
+</div>
+
+
+ ./pages.component.scss
+
+ +
+ src/app/pages/settings/settings.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-settings |
+
styleUrls | +./settings.component.scss |
+
templateUrl | +./settings.component.html |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(authService: AuthService)
+ |
+ ||||||
+ + | +||||||
+
+ Parameters :
+
+
|
+
+ + + + doFilter + + + + | +||||||
+doFilter(value: string)
+ |
+ ||||||
+ + | +||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + downloadCsv + + + + | +
+downloadCsv()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + logout + + + + | +
+logout()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + dataSource + + + | +
+ Type : MatTableDataSource<any>
+
+ |
+
+ + | +
+ + + + date + + + | +
+ Type : string
+
+ |
+
+ + | +
+ + + + displayedColumns + + + | +
+ Type : Array<string>
+
+ |
+
+ Default value : ['name', 'email', 'userId']
+ |
+
+ + | +
+ + + + paginator + + + | +
+ Type : MatPaginator
+
+ |
+
+ Decorators :
+ +
+ @ViewChild(MatPaginator)
+ |
+
+ + | +
+ + + + sort + + + | +
+ Type : MatSort
+
+ |
+
+ Decorators :
+ +
+ @ViewChild(MatSort)
+ |
+
+ + | +
+ + + + trustedUsers + + + | +
+ Type : Array<Staff>
+
+ |
+
+ + | +
import {ChangeDetectionStrategy, Component, OnInit, ViewChild} from '@angular/core';
+import {MatTableDataSource} from '@angular/material/table';
+import {MatPaginator} from '@angular/material/paginator';
+import {MatSort} from '@angular/material/sort';
+import {AuthService} from '@app/_services';
+import {Staff} from '@app/_models/staff';
+import {exportCsv} from '@app/_helpers';
+
+@Component({
+ selector: 'app-settings',
+ templateUrl: './settings.component.html',
+ styleUrls: ['./settings.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class SettingsComponent implements OnInit {
+ date: string;
+ dataSource: MatTableDataSource<any>;
+ displayedColumns: Array<string> = ['name', 'email', 'userId'];
+ trustedUsers: Array<Staff>;
+
+ @ViewChild(MatPaginator) paginator: MatPaginator;
+ @ViewChild(MatSort) sort: MatSort;
+
+ constructor(
+ private authService: AuthService
+ ) { }
+
+ ngOnInit(): void {
+ const d = new Date();
+ this.date = `${d.getDate()}/${d.getMonth()}/${d.getFullYear()}`;
+ this.trustedUsers = this.authService.getTrustedUsers();
+ this.dataSource = new MatTableDataSource<any>(this.trustedUsers);
+ this.dataSource.paginator = this.paginator;
+ this.dataSource.sort = this.sort;
+ }
+
+ doFilter(value: string): void {
+ this.dataSource.filter = value.trim().toLocaleLowerCase();
+ }
+
+ downloadCsv(): void {
+ exportCsv(this.trustedUsers, 'users');
+ }
+
+ logout(): void {
+ this.authService.logout();
+ }
+}
+
+ <!-- Begin page -->
+<div class="wrapper">
+ <app-sidebar></app-sidebar>
+
+ <!-- ============================================================== -->
+ <!-- Start Page Content here -->
+ <!-- ============================================================== -->
+
+ <div id="content">
+ <app-topbar></app-topbar>
+ <!-- Start Content-->
+ <div class="container-fluid" appMenuSelection>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
+ <li class="breadcrumb-item active" aria-current="page">Settings</li>
+ </ol>
+ </nav>
+ <div class="row">
+ <div class="col-md-6 mb-2">
+ <div class="card">
+ <mat-card-title class="card-header text-center">
+ SETTINGS
+ </mat-card-title>
+ <div class="card-body">
+ <h4>Kobo Toolbox Credentials</h4>
+ <span><strong>Username: </strong> admin_reserve </span><br>
+ <span><strong>Password: </strong> ******** </span>
+ </div>
+ <hr>
+ <div class="card-body">
+ <h4>Organization Settings</h4>
+ <a routerLink="/settings/organization"><i>Update your organization settings</i></a>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-6 mb-2">
+ <div class="card">
+ <mat-card-title class="card-header text-center">
+ ACCOUNT MANAGEMENT
+ </mat-card-title>
+ <div class="card-body">
+ <h4>Change Password</h4>
+ <a routerLink="/settings"><i>Change your account password</i></a>
+ </div>
+ <hr>
+ <div class="card-body">
+ <h4>Two-step authentication</h4>
+ <a routerLink="/settings"><i>Secure your account with two step verification</i></a>
+ </div>
+ <hr>
+ <div class="card-body">
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary" (click)="logout()"> LOGOUT ADMIN </button>
+ </div>
+ </div>
+ </div>
+ <div class="col-12">
+ <div class="card mb-3">
+ <mat-card-title class="card-header">
+ <div class="row">
+ TRUSTED USERS
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary ml-auto mr-2" (click)="downloadCsv()"> EXPORT </button>
+ </div>
+ </mat-card-title>
+ <div class="card-body">
+ <mat-form-field appearance="outline">
+ <mat-label> Filter </mat-label>
+ <input matInput type="text" (keyup)="doFilter($event.target.value)" placeholder="Filter">
+ <mat-icon matSuffix>search</mat-icon>
+ </mat-form-field>
+ <mat-table class="mat-elevation-z10" [dataSource]="dataSource" matSort matSortActive="name"
+ matSortDirection="asc" matSortDisableClear>
+
+ <ng-container matColumnDef="name">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> NAME </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user.name}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="email">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> EMAIL </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user.email}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="userId">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> USER ID </mat-header-cell>
+ <mat-cell *matCellDef="let user"> {{user.userid}} </mat-cell>
+ </ng-container>
+
+ <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
+ <mat-row *matRowDef="let user; columns: displayedColumns"></mat-row>
+ </mat-table>
+
+ <mat-paginator [pageSize]="5" [pageSizeOptions]="[5, 10, 20, 50, 100]" showFirstLastButtons></mat-paginator>
+
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <app-footer appMenuSelection></app-footer>
+ </div>
+ <!-- ============================================================== -->
+ <!-- End Page content -->
+ <!-- ============================================================== -->
+</div>
+
+
+ ./settings.component.scss
+
+ +
+ src/app/shared/sidebar/sidebar.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-sidebar |
+
styleUrls | +./sidebar.component.scss |
+
templateUrl | +./sidebar.component.html |
+
+ Methods+ |
+
+
|
+
+constructor()
+ |
+
+ + | +
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
+
+@Component({
+ selector: 'app-sidebar',
+ templateUrl: './sidebar.component.html',
+ styleUrls: ['./sidebar.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class SidebarComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit(): void {
+ }
+
+}
+
+ <!-- ========== Left Sidebar Start ========== -->
+<div id="sidebar">
+ <nav>
+
+ <div class="sidebar-header">
+ <h3>
+ <img class="full-width" src="assets/images/CIC-Logo-white.png" alt="CIC Admin Dashboard">
+ </h3>
+ <strong>CICADA</strong>
+ </div>
+
+ <ul class="list-unstyled components">
+ <li>
+ <a routerLink="/home" routerLinkActive="active" appMenuSelection>
+ <i class="fa fa-home"></i>
+ <span> Dashboard </span>
+ </a>
+ </li>
+ <li>
+ <a routerLink="/accounts" routerLinkActive="active" appMenuSelection>
+ <i class="fa fa-users"></i>
+ <span> Accounts </span>
+ </a>
+ </li>
+ <li>
+ <a routerLink="/tx" routerLinkActive="active" appMenuSelection>
+ <i class="fa fa-exchange"></i>
+ <span> Transactions </span>
+ </a>
+ </li>
+ <li>
+ <a routerLink="/tokens" routerLinkActive="active" appMenuSelection>
+ <i class="fa fa-money"></i>
+ <span> Tokens </span>
+ </a>
+ </li>
+ <li>
+ <a routerLink="/settings" routerLinkActive="active" appMenuSelection>
+ <i class="fa fa-cog"></i>
+ <span> Settings </span>
+ </a>
+ </li>
+ <li>
+ <a routerLink="/admin" routerLinkActive="active" appMenuSelection>
+ <i class="fa fa-user-secret"></i>
+ <span> Admin </span>
+ </a>
+ </li>
+ </ul>
+ </nav>
+</div>
+<!-- Left Sidebar End -->
+
+
+ ./sidebar.component.scss
+
+ +
+ src/testing/shared-module-stub.ts
+
selector | +app-sidebar |
+
import {Component} from '@angular/core';
+
+@Component({selector: 'app-sidebar', template: ''})
+export class SidebarStubComponent {}
+
+@Component({selector: 'app-topbar', template: ''})
+export class TopbarStubComponent {}
+
+@Component({selector: 'app-footer', template: ''})
+export class FooterStubComponent {}
+
+ +
+ src/app/pages/tokens/token-details/token-details.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-token-details |
+
styleUrls | +./token-details.component.scss |
+
templateUrl | +./token-details.component.html |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(route: ActivatedRoute, tokenService: TokenService)
+ |
+ |||||||||
+ + | +|||||||||
+
+ Parameters :
+
+
|
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + token + + + | +
+ Type : Token
+
+ |
+
+ + | +
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
+import {ActivatedRoute, Params} from '@angular/router';
+import {TokenService} from '@app/_services';
+import {first} from 'rxjs/operators';
+import {Token} from '../../../_models';
+
+@Component({
+ selector: 'app-token-details',
+ templateUrl: './token-details.component.html',
+ styleUrls: ['./token-details.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TokenDetailsComponent implements OnInit {
+ token: Token;
+
+ constructor(
+ private route: ActivatedRoute,
+ private tokenService: TokenService
+ ) {
+ this.route.paramMap.subscribe((params: Params) => {
+ this.tokenService.getTokenBySymbol(params.get('id')).pipe(first()).subscribe(res => {
+ this.token = res;
+ });
+ });
+ }
+
+ ngOnInit(): void {
+ }
+
+}
+
+ <!-- Begin page -->
+<div class="wrapper">
+ <app-sidebar></app-sidebar>
+
+ <!-- ============================================================== -->
+ <!-- Start Page Content here -->
+ <!-- ============================================================== -->
+
+ <div id="content">
+ <app-topbar></app-topbar>
+ <!-- Start Content-->
+ <div class="container-fluid text-center" appMenuSelection>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
+ <li class="breadcrumb-item"><a routerLink="/tokens">Tokens</a></li>
+ <li class="breadcrumb-item active" aria-current="page">{{token.name}}</li>
+ </ol>
+ </nav>
+ <div class="col-md-6 center-body">
+ <div class="card">
+ <mat-card-title class="card-header">
+ Token
+ </mat-card-title>
+ <div class="card-body">
+ <div>
+ <span><strong>Name:</strong> {{token.name}}</span>
+ </div>
+ <div>
+ <span><strong>Symbol:</strong> {{token.symbol}}</span>
+ </div>
+ <div>
+ <span><strong>Address:</strong> {{token.address}}</span>
+ </div>
+ <div>
+ <span><strong>Details:</strong> A community inclusive currency for trading among lower to middle income societies.</span>
+ </div>
+ <div>
+ <span><strong>Supply:</strong> {{token.supply | tokenRatio}}</span>
+ </div><br>
+ <div>
+ <h2>Reserve</h2>
+ <div>
+ <span><strong>Weight:</strong> {{token.reserveRatio}}</span>
+ </div>
+ <div>
+ <span><strong>Owner:</strong> {{token.owner}}</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <app-footer appMenuSelection></app-footer>
+ </div>
+ <!-- ============================================================== -->
+ <!-- End Page content -->
+ <!-- ============================================================== -->
+</div>
+
+
+
+ ./token-details.component.scss
+
+ +
+ src/app/pages/tokens/tokens.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-tokens |
+
styleUrls | +./tokens.component.scss |
+
templateUrl | +./tokens.component.html |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(tokenService: TokenService, loggingService: LoggingService, router: Router)
+ |
+ ||||||||||||
+ Defined in src/app/pages/tokens/tokens.component.ts:22
+ |
+ ||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + doFilter + + + + | +||||||
+doFilter(value: string)
+ |
+ ||||||
+ Defined in src/app/pages/tokens/tokens.component.ts:41
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + downloadCsv + + + + | +
+downloadCsv()
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:49
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:30
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + Async + viewToken + + + + | +||||
+
+ viewToken(token)
+ |
+ ||||
+ Defined in src/app/pages/tokens/tokens.component.ts:45
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + columnsToDisplay + + + | +
+ Type : Array<string>
+
+ |
+
+ Default value : ['name', 'symbol', 'address', 'supply']
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:19
+ |
+
+ + + + dataSource + + + | +
+ Type : MatTableDataSource<any>
+
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:18
+ |
+
+ + + + paginator + + + | +
+ Type : MatPaginator
+
+ |
+
+ Decorators :
+ +
+ @ViewChild(MatPaginator)
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:20
+ |
+
+ + + + sort + + + | +
+ Type : MatSort
+
+ |
+
+ Decorators :
+ +
+ @ViewChild(MatSort)
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:21
+ |
+
+ + + + tokens + + + | +
+ Type : Array<Promise<string>>
+
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:22
+ |
+
import {ChangeDetectionStrategy, Component, OnInit, ViewChild} from '@angular/core';
+import {MatPaginator} from '@angular/material/paginator';
+import {MatSort} from '@angular/material/sort';
+import {LoggingService, TokenService} from '@app/_services';
+import {MatTableDataSource} from '@angular/material/table';
+import {Router} from '@angular/router';
+import {exportCsv} from '@app/_helpers';
+import {TokenRegistry} from '../../_eth';
+import {Token} from '../../_models';
+
+@Component({
+ selector: 'app-tokens',
+ templateUrl: './tokens.component.html',
+ styleUrls: ['./tokens.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TokensComponent implements OnInit {
+ dataSource: MatTableDataSource<any>;
+ columnsToDisplay: Array<string> = ['name', 'symbol', 'address', 'supply'];
+ @ViewChild(MatPaginator) paginator: MatPaginator;
+ @ViewChild(MatSort) sort: MatSort;
+ tokens: Array<Promise<string>>;
+
+ constructor(
+ private tokenService: TokenService,
+ private loggingService: LoggingService,
+ private router: Router
+ ) { }
+
+ async ngOnInit(): Promise<void> {
+ this.tokenService.LoadEvent.subscribe(async () => {
+ this.tokens = await this.tokenService.getTokens();
+ });
+ this.tokens = await this.tokenService.getTokens();
+ this.loggingService.sendInfoLevelMessage(this.tokens);
+ this.dataSource = new MatTableDataSource(this.tokens);
+ this.dataSource.paginator = this.paginator;
+ this.dataSource.sort = this.sort;
+ }
+
+ doFilter(value: string): void {
+ this.dataSource.filter = value.trim().toLocaleLowerCase();
+ }
+
+ async viewToken(token): Promise<void> {
+ await this.router.navigateByUrl(`/tokens/${token.symbol}`);
+ }
+
+ downloadCsv(): void {
+ exportCsv(this.tokens, 'tokens');
+ }
+}
+
+ <!-- Begin page -->
+<div class="wrapper">
+ <app-sidebar></app-sidebar>
+
+ <!-- ============================================================== -->
+ <!-- Start Page Content here -->
+ <!-- ============================================================== -->
+
+ <div id="content">
+ <app-topbar></app-topbar>
+ <!-- Start Content-->
+ <div class="container-fluid" appMenuSelection>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
+ <li class="breadcrumb-item active" aria-current="page">Tokens</li>
+ </ol>
+ </nav>
+ <div class="card">
+ <mat-card-title class="card-header">
+ <div class="row">
+ Tokens
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary ml-auto mr-2" (click)="downloadCsv()"> EXPORT </button>
+ </div>
+ </mat-card-title>
+ <div class="card-body">
+ <mat-form-field appearance="outline">
+ <mat-label> Filter </mat-label>
+ <input matInput type="text" (keyup)="doFilter($event.target.value)" placeholder="Filter">
+ <mat-icon matSuffix>search</mat-icon>
+ </mat-form-field>
+
+ <mat-table class="mat-elevation-z10 table-responsive" [dataSource]="dataSource" matSort matSortDirection="asc" matSortDisableClear>
+ <ng-container matColumnDef="name">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
+ <mat-cell *matCellDef="let token"> {{token.name}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="symbol">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> Symbol </mat-header-cell>
+ <mat-cell *matCellDef="let token"> {{token.symbol}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="address">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> Address </mat-header-cell>
+ <mat-cell *matCellDef="let token"> {{token.address}} </mat-cell>
+ </ng-container>
+
+ <ng-container matColumnDef="supply">
+ <mat-header-cell *matHeaderCellDef mat-sort-header> Supply </mat-header-cell>
+ <mat-cell *matCellDef="let token"> {{token.supply | tokenRatio}} </mat-cell>
+ </ng-container>
+
+ <mat-header-row *matHeaderRowDef="columnsToDisplay"></mat-header-row>
+ <mat-row *matRowDef="let token; columns: columnsToDisplay" (click)="viewToken(token)" matRipple></mat-row>
+ </mat-table>
+
+ <mat-paginator [pageSize]="5" [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons></mat-paginator>
+ </div>
+ </div>
+ </div>
+ <app-footer appMenuSelection></app-footer>
+ </div>
+ <!-- ============================================================== -->
+ <!-- End Page content -->
+ <!-- ============================================================== -->
+</div>
+
+
+
+ ./tokens.component.scss
+
+ +
+ src/app/shared/topbar/topbar.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-topbar |
+
styleUrls | +./topbar.component.scss |
+
templateUrl | +./topbar.component.html |
+
+ Methods+ |
+
+
|
+
+constructor()
+ |
+
+ Defined in src/app/shared/topbar/topbar.component.ts:9
+ |
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ Defined in src/app/shared/topbar/topbar.component.ts:13
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
+
+@Component({
+ selector: 'app-topbar',
+ templateUrl: './topbar.component.html',
+ styleUrls: ['./topbar.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TopbarComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit(): void {
+ }
+
+}
+
+ <!-- Topbar Start -->
+<nav class="navbar navbar-expand-lg navbar-light bg-light sticky-top">
+ <div class="container-fluid">
+ <button type="button" id="sidebarCollapse" class="navbar-btn menutoggle" aria-label="Sidebar toggle" appMenuToggle>
+ <span></span>
+ <span></span>
+ <span></span>
+ </button>
+ </div>
+</nav>
+<!-- end Topbar -->
+
+
+ ./topbar.component.scss
+
+ +
+ src/testing/shared-module-stub.ts
+
selector | +app-topbar |
+
import {Component} from '@angular/core';
+
+@Component({selector: 'app-sidebar', template: ''})
+export class SidebarStubComponent {}
+
+@Component({selector: 'app-topbar', template: ''})
+export class TopbarStubComponent {}
+
+@Component({selector: 'app-footer', template: ''})
+export class FooterStubComponent {}
+
+ +
+ src/app/pages/transactions/transaction-details/transaction-details.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-transaction-details |
+
styleUrls | +./transaction-details.component.scss |
+
templateUrl | +./transaction-details.component.html |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+ Inputs+ |
+
+
|
+
+constructor(router: Router, transactionService: TransactionService, snackBar: MatSnackBar)
+ |
+ ||||||||||||
+ + | +||||||||||||
+
+ Parameters :
+
+
|
+
+ + transaction + | +|
+ + | +
+ + + + copyAddress + + + + | +||||||
+copyAddress(address: string)
+ |
+ ||||||
+ + | +||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + Async + reverseTransaction + + + + | +
+
+ reverseTransaction()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + Async + viewRecipient + + + + | +
+
+ viewRecipient()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + Async + viewSender + + + + | +
+
+ viewSender()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + Async + viewTrader + + + + | +
+
+ viewTrader()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + recipientBloxbergLink + + + | +
+ Type : string
+
+ |
+
+ + | +
+ + + + senderBloxbergLink + + + | +
+ Type : string
+
+ |
+
+ + | +
+ + + + traderBloxbergLink + + + | +
+ Type : string
+
+ |
+
+ + | +
import {ChangeDetectionStrategy, Component, Input, OnInit} from '@angular/core';
+import {Router} from '@angular/router';
+import {TransactionService} from '@app/_services';
+import {copyToClipboard} from '@app/_helpers';
+import {MatSnackBar} from '@angular/material/snack-bar';
+import {strip0x} from '@src/assets/js/ethtx/dist/hex';
+
+@Component({
+ selector: 'app-transaction-details',
+ templateUrl: './transaction-details.component.html',
+ styleUrls: ['./transaction-details.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TransactionDetailsComponent implements OnInit {
+ @Input() transaction;
+ senderBloxbergLink: string;
+ recipientBloxbergLink: string;
+ traderBloxbergLink: string;
+
+ constructor(
+ private router: Router,
+ private transactionService: TransactionService,
+ private snackBar: MatSnackBar,
+ ) { }
+
+ ngOnInit(): void {
+ if (this.transaction?.type === 'conversion') {
+ this.traderBloxbergLink = 'https://blockexplorer.bloxberg.org/address/' + this.transaction?.trader + '/transactions';
+ } else {
+ this.senderBloxbergLink = 'https://blockexplorer.bloxberg.org/address/' + this.transaction?.from + '/transactions';
+ this.recipientBloxbergLink = 'https://blockexplorer.bloxberg.org/address/' + this.transaction?.to + '/transactions';
+ }
+ }
+
+ async viewSender(): Promise<void> {
+ await this.router.navigateByUrl(`/accounts/${strip0x(this.transaction.from)}`);
+ }
+
+ async viewRecipient(): Promise<void> {
+ await this.router.navigateByUrl(`/accounts/${strip0x(this.transaction.to)}`);
+ }
+
+ async viewTrader(): Promise<void> {
+ await this.router.navigateByUrl(`/accounts/${strip0x(this.transaction.trader)}`);
+ }
+
+ async reverseTransaction(): Promise<void> {
+ await this.transactionService.transferRequest(
+ this.transaction.token.address,
+ this.transaction.to,
+ this.transaction.from,
+ this.transaction.value
+ );
+ }
+
+ copyAddress(address: string): void {
+ if (copyToClipboard(address)) {
+ this.snackBar.open(address + ' copied successfully!', 'Close', { duration: 3000 });
+ }
+ }
+}
+
+ <div *ngIf="transaction | async" class="mb-3 mt-1">
+ <div class="card text-center">
+ <mat-card-title class="card-header">
+ <div class="row">
+ TRANSACTION DETAILS
+ <button mat-raised-button type="button" class="btn btn-outline-secondary ml-auto mr-2" (click)="transaction = null"> CLOSE </button>
+ </div>
+ </mat-card-title>
+ <div *ngIf="transaction.type == 'transaction'" class="card-body">
+ <div class="row">
+ <div class="col-md-6">
+ <h4>Exchange: </h4>
+ <ul class="list-group list-group-flush">
+ <li class="list-group-item">
+ <span>Sender: {{transaction.sender?.vcard.fn[0].value}}</span><br><br>
+ <span>
+ Sender Address:
+ <a href="{{senderBloxbergLink}}" target="_blank"> {{transaction.from}} </a>
+ <img src="assets/images/checklist.svg" class="ml-2" height="20rem" (click)="copyAddress(transaction.from)" alt="Copy">
+ </span><br><br>
+ <button mat-raised-button color="primary" class="btn btn-outline-info" (click)="viewSender()">View Sender</button>
+ </li>
+ <li class="list-group-item">
+ <span>Recipient: {{transaction.recipient?.vcard.fn[0].value}}</span><br><br>
+ <span>
+ Recipient Address:
+ <a href="{{recipientBloxbergLink}}" target="_blank"> {{transaction.to}} </a>
+ <img src="assets/images/checklist.svg" class="ml-2" height="20rem" (click)="copyAddress(transaction.to)" alt="Copy">
+ </span><br><br>
+ <button mat-raised-button color="primary" class="btn btn-outline-info" (click)="viewRecipient()">View Recipient</button>
+ </li>
+ <li class="list-group-item">
+ <span>Amount: SRF {{transaction.value | tokenRatio}}</span>
+ </li>
+ </ul>
+ <h4 class="mt-2">Token: </h4>
+ <ul class="list-group list-group-flush">
+ <li class="list-group-item">
+ <span>
+ Address:
+ {{transaction.token._address}}
+ <img src="assets/images/checklist.svg" class="ml-2" height="20rem" (click)="copyAddress(transaction.token._address)" alt="Copy">
+ </span>
+ </li>
+ <li class="list-group-item">
+ <span>Name: Sarafu Token</span>
+ </li>
+ <li class="list-group-item">
+ <span>Symbol: SRF</span>
+ </li>
+ </ul>
+ </div>
+ <div class="col-md-6">
+ <h4>Transaction: </h4>
+ <ul class="list-group list-group-flush">
+ <li class="list-group-item">
+ <span>Block: {{transaction.tx.block}}</span>
+ </li>
+ <li class="list-group-item">
+ <span>Index: {{transaction.tx.txIndex}}</span>
+ </li>
+ <li class="list-group-item">
+ <span>Hash: {{transaction.tx.txHash}}</span>
+ </li>
+ <li class="list-group-item">
+ <span>Success: {{transaction.tx.success}}</span>
+ </li>
+ <li class="list-group-item">
+ <span>Timestamp: {{transaction.tx.timestamp | date}}</span>
+ </li>
+ </ul><br>
+ <div class="mb-3">
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-success">Resend SMS</button>
+ </div>
+ <div>
+ <button mat-raised-button color="warn" type="button" class="btn btn-outline-danger">Reverse Transaction</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div *ngIf="transaction.type == 'conversion'" class="card-body">
+ <h3>Exchange: </h3>
+ <ul class="list-group list-group-flush">
+ <li class="list-group-item">
+ <span><strong>Trader: {{transaction.sender?.vcard.fn[0].value}}</strong></span>
+ </li>
+ <li class="list-group-item">
+ <span>
+ Trader Address:
+ <a href="{{traderBloxbergLink}}" target="_blank"> {{transaction.trader}} </a>
+ <img src="assets/images/checklist.svg" class="ml-2" height="20rem" (click)="copyAddress(transaction.trader)" alt="Copy">
+ </span>
+ </li>
+ </ul>
+ <button mat-raised-button color="primary" class="btn btn-outline-info" (click)="viewTrader()">View Trader</button>
+ <br><br>
+ <div class="row">
+ <div class="col-md-6">
+ <h4>Source Token: </h4>
+ <ul class="list-group list-group-flush">
+ <li class="list-group-item">
+ <span>
+ Address:
+ {{transaction.sourceToken.address}}
+ <img src="assets/images/checklist.svg" class="ml-2" height="20rem" (click)="copyAddress(transaction.sourceToken.address)" alt="Copy">
+ </span>
+ </li>
+ <li class="list-group-item">
+ <span>Name: {{transaction.sourceToken.name}}</span>
+ </li>
+ <li class="list-group-item">
+ <span>Symbol: {{transaction.sourceToken.symbol}}</span>
+ </li>
+ <li class="list-group-item">
+ <span>Amount: {{transaction.sourceToken.symbol + ' ' + transaction.fromValue}}</span>
+ </li>
+ </ul>
+ </div>
+ <div class="col-md-6">
+ <h4>Destination Token: </h4>
+ <ul class="list-group list-group-flush">
+ <li class="list-group-item">
+ <span>
+ Address:
+ {{transaction.destinationToken.address}}
+ <img src="assets/images/checklist.svg" class="ml-2" height="20rem" (click)="copyAddress(transaction.destinationToken.address)" alt="Copy">
+ </span>
+ </li>
+ <li class="list-group-item">
+ <span>Name: {{transaction.destinationToken.name}}</span>
+ </li>
+ <li class="list-group-item">
+ <span>Symbol: {{transaction.destinationToken.symbol}}</span>
+ </li>
+ <li class="list-group-item">
+ <span>Amount: {{transaction.destinationToken.symbol + ' ' + transaction.toValue}}</span>
+ </li>
+ </ul>
+ </div>
+ <div class="col-md-6">
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-success">Resend SMS</button>
+ </div>
+ <div class="col-md-6">
+ <button mat-raised-button color="warn" type="button" class="btn btn-outline-danger ml-2" (click)="reverseTransaction()">Reverse Transaction</button>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+
+ ./transaction-details.component.scss
+
+ +
+ src/app/pages/transactions/transactions.component.ts
+
+
+ OnInit
+ AfterViewInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-transactions |
+
styleUrls | +./transactions.component.scss |
+
templateUrl | +./transactions.component.html |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(blockSyncService: BlockSyncService, transactionService: TransactionService, userService: UserService)
+ |
+ ||||||||||||
+ + | +||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + doFilter + + + + | +|||||||||
+doFilter(value: string, dataSource)
+ |
+ |||||||||
+ + | +|||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + downloadCsv + + + + | +
+downloadCsv()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + filterTransactions + + + + | +
+filterTransactions()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + ngAfterViewInit + + + + | +
+ngAfterViewInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + viewTransaction + + + + | +||||
+viewTransaction(transaction)
+ |
+ ||||
+ + | +||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + defaultPageSize + + + | +
+ Type : number
+
+ |
+
+ Default value : 10
+ |
+
+ + | +
+ + + + pageSizeOptions + + + | +
+ Type : Array<number>
+
+ |
+
+ Default value : [10, 20, 50, 100]
+ |
+
+ + | +
+ + + + paginator + + + | +
+ Type : MatPaginator
+
+ |
+
+ Decorators :
+ +
+ @ViewChild(MatPaginator)
+ |
+
+ + | +
+ + + + sort + + + | +
+ Type : MatSort
+
+ |
+
+ Decorators :
+ +
+ @ViewChild(MatSort)
+ |
+
+ + | +
+ + + + transaction + + + | +
+ Type : Transaction
+
+ |
+
+ + | +
+ + + + transactionDataSource + + + | +
+ Type : MatTableDataSource<any>
+
+ |
+
+ + | +
+ + + + transactionDisplayedColumns + + + | +
+ Type : Array<string>
+
+ |
+
+ Default value : ['sender', 'recipient', 'value', 'created', 'type']
+ |
+
+ + | +
+ + + + transactions + + + | +
+ Type : Array<Transaction>
+
+ |
+
+ + | +
+ + + + transactionsType + + + | +
+ Type : string
+
+ |
+
+ Default value : 'all'
+ |
+
+ + | +
+ + + + transactionsTypes + + + | +
+ Type : Array<string>
+
+ |
+
+ + | +
import {AfterViewInit, ChangeDetectionStrategy, Component, OnInit, ViewChild} from '@angular/core';
+import {BlockSyncService, TransactionService, UserService} from '@app/_services';
+import {MatTableDataSource} from '@angular/material/table';
+import {MatPaginator} from '@angular/material/paginator';
+import {MatSort} from '@angular/material/sort';
+import {exportCsv} from '@app/_helpers';
+import {first} from 'rxjs/operators';
+import {Transaction} from '@app/_models';
+
+@Component({
+ selector: 'app-transactions',
+ templateUrl: './transactions.component.html',
+ styleUrls: ['./transactions.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TransactionsComponent implements OnInit, AfterViewInit {
+ transactionDataSource: MatTableDataSource<any>;
+ transactionDisplayedColumns: Array<string> = ['sender', 'recipient', 'value', 'created', 'type'];
+ defaultPageSize: number = 10;
+ pageSizeOptions: Array<number> = [10, 20, 50, 100];
+ transactions: Array<Transaction>;
+ transaction: Transaction;
+ transactionsType: string = 'all';
+ transactionsTypes: Array<string>;
+
+ @ViewChild(MatPaginator) paginator: MatPaginator;
+ @ViewChild(MatSort) sort: MatSort;
+
+ constructor(
+ private blockSyncService: BlockSyncService,
+ private transactionService: TransactionService,
+ private userService: UserService
+ ) {
+ this.blockSyncService.blockSync();
+ }
+
+ ngOnInit(): void {
+ this.transactionService.transactionsSubject.subscribe(transactions => {
+ this.transactionDataSource = new MatTableDataSource<any>(transactions);
+ this.transactionDataSource.paginator = this.paginator;
+ this.transactionDataSource.sort = this.sort;
+ this.transactions = transactions;
+ });
+ this.userService.getTransactionTypes().pipe(first()).subscribe(res => this.transactionsTypes = res);
+ }
+
+ viewTransaction(transaction): void {
+ this.transaction = transaction;
+ }
+
+ doFilter(value: string, dataSource): void {
+ dataSource.filter = value.trim().toLocaleLowerCase();
+ }
+
+ filterTransactions(): void {
+ if (this.transactionsType === 'all') {
+ this.transactionService.transactionsSubject.subscribe(transactions => {
+ this.transactionDataSource.data = transactions;
+ this.transactions = transactions;
+ });
+ } else {
+ this.transactionDataSource.data = this.transactions.filter(transaction => transaction.type === this.transactionsType);
+ }
+ }
+
+ ngAfterViewInit(): void {
+ this.transactionDataSource.paginator = this.paginator;
+ this.transactionDataSource.sort = this.sort;
+ }
+
+ downloadCsv(): void {
+ exportCsv(this.transactions, 'transactions');
+ }
+}
+
+ <!-- Begin page -->
+<div class="wrapper">
+ <app-sidebar></app-sidebar>
+
+ <!-- ============================================================== -->
+ <!-- Start Page Content here -->
+ <!-- ============================================================== -->
+
+ <div id="content">
+ <app-topbar></app-topbar>
+ <!-- Start Content-->
+ <div class="container-fluid" appMenuSelection>
+ <nav aria-label="breadcrumb">
+ <ol class="breadcrumb">
+ <li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
+ <li class="breadcrumb-item active" aria-current="page">Transactions</li>
+ </ol>
+ </nav>
+ <div class="card">
+ <mat-card-title class="card-header">
+ Transfers
+ </mat-card-title>
+ <div class="card-body">
+
+ <app-transaction-details [transaction]="transaction"></app-transaction-details>
+
+ <div class="row card-header">
+ <mat-form-field appearance="outline">
+ <mat-label> TRANSFER TYPE </mat-label>
+ <mat-select id="typeSelect" [(value)]="transactionsType" (selectionChange)="filterTransactions()">
+ <mat-option value="all">ALL TRANSFERS</mat-option>
+ <mat-option *ngFor="let transactionType of transactionsTypes" [value]="transactionType">
+ {{transactionType | uppercase}}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary ml-auto mr-2" (click)="downloadCsv()"> EXPORT </button>
+ </div>
+
+ <mat-form-field appearance="outline">
+ <mat-label> Filter </mat-label>
+ <input matInput type="text" (keyup)="doFilter($event.target.value, transactionDataSource)" placeholder="Filter">
+ <mat-icon matSuffix>search</mat-icon>
+ </mat-form-field>
+
+ <table mat-table class="mat-elevation-z10" [dataSource]="transactionDataSource" matSort matSortActive="created"
+ matSortDirection="desc" matSortDisableClear>
+
+ <ng-container matColumnDef="sender">
+ <th mat-header-cell *matHeaderCellDef mat-sort-header> Sender </th>
+ <td mat-cell *matCellDef="let transaction"> {{transaction?.sender?.vcard.fn[0].value}} </td>
+ </ng-container>
+
+ <ng-container matColumnDef="recipient">
+ <th mat-header-cell *matHeaderCellDef mat-sort-header> Recipient </th>
+ <td mat-cell *matCellDef="let transaction"> {{transaction?.recipient?.vcard.fn[0].value}} </td>
+ </ng-container>
+
+ <ng-container matColumnDef="value">
+ <th mat-header-cell *matHeaderCellDef mat-sort-header> Value </th>
+ <td mat-cell *matCellDef="let transaction">
+ <span *ngIf="transaction.type == 'transaction'">{{transaction?.value | tokenRatio}}</span>
+ <span *ngIf="transaction.type == 'conversion'">{{transaction?.toValue | tokenRatio}}</span>
+ </td>
+ </ng-container>
+
+ <ng-container matColumnDef="created">
+ <th mat-header-cell *matHeaderCellDef mat-sort-header> Created </th>
+ <td mat-cell *matCellDef="let transaction"> {{transaction?.tx.timestamp | date}} </td>
+ </ng-container>
+
+ <ng-container matColumnDef="type">
+ <th mat-header-cell *matHeaderCellDef mat-sort-header> TYPE </th>
+ <td mat-cell *matCellDef="let transaction">
+ <span class="badge badge-success badge-pill"> {{transaction?.type}} </span>
+ </td>
+ </ng-container>
+
+ <tr mat-header-row *matHeaderRowDef="transactionDisplayedColumns"></tr>
+ <tr mat-row *matRowDef="let transaction; columns: transactionDisplayedColumns;" (click)="viewTransaction(transaction)"></tr>
+ </table>
+
+ <mat-paginator [pageSize]="defaultPageSize" [pageSizeOptions]="pageSizeOptions" showFirstLastButtons></mat-paginator>
+
+ </div>
+ </div>
+ </div>
+ <app-footer appMenuSelection></app-footer>
+ </div>
+ <!-- ============================================================== -->
+ <!-- End Page content -->
+ <!-- ============================================================== -->
+</div>
+
+
+
+
+ ./transactions.component.scss
+
+ File | +Type | +Identifier | +Statements | +
---|---|---|---|
+ + src/app/_eth/accountIndex.ts + | +class | +AccountIndex | ++ 0 % + (0/9) + | +
+ + src/app/_eth/accountIndex.ts + | +variable | +abi | ++ 0 % + (0/1) + | +
+ + src/app/_eth/accountIndex.ts + | +variable | +web3 | ++ 0 % + (0/1) + | +
+ + src/app/_eth/token-registry.ts + | +class | +TokenRegistry | ++ 0 % + (0/8) + | +
+ + src/app/_eth/token-registry.ts + | +variable | +abi | ++ 0 % + (0/1) + | +
+ + src/app/_eth/token-registry.ts + | +variable | +web3 | ++ 0 % + (0/1) + | +
+ + src/app/_guards/auth.guard.ts + | +guard | +AuthGuard | ++ 0 % + (0/3) + | +
+ + src/app/_guards/role.guard.ts + | +guard | +RoleGuard | ++ 0 % + (0/3) + | +
+ + src/app/_helpers/array-sum.ts + | +function | +arraySum | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/clipboard-copy.ts + | +function | +copyToClipboard | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/custom-error-state-matcher.ts + | +class | +CustomErrorStateMatcher | ++ 0 % + (0/2) + | +
+ + src/app/_helpers/custom.validator.ts + | +class | +CustomValidator | ++ 0 % + (0/3) + | +
+ + src/app/_helpers/export-csv.ts + | +function | +exportCsv | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/export-csv.ts + | +function | +removeSpecialChar | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/global-error-handler.ts + | +class | +HttpError | ++ 0 % + (0/3) + | +
+ + src/app/_helpers/global-error-handler.ts + | +injectable | +GlobalErrorHandler | ++ 0 % + (0/6) + | +
+ + src/app/_helpers/http-getter.ts + | +function | +HttpGetter | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +interceptor | +MockBackendInterceptor | ++ 0 % + (0/2) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +accountTypes | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +actions | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +areaNames | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +areaTypes | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +categories | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +genders | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +MockBackendProvider | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +tokens | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +transactionTypes | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/read-csv.ts + | +function | +parseData | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/read-csv.ts + | +function | +readCsv | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/read-csv.ts + | +variable | +objCsv | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/schema-validation.ts + | +function | +personValidation | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/schema-validation.ts + | +function | +vcardValidation | ++ 0 % + (0/1) + | +
+ + src/app/_interceptors/error.interceptor.ts + | +interceptor | +ErrorInterceptor | ++ 0 % + (0/3) + | +
+ + src/app/_interceptors/http-config.interceptor.ts + | +interceptor | +HttpConfigInterceptor | ++ 0 % + (0/3) + | +
+ + src/app/_interceptors/logging.interceptor.ts + | +interceptor | +LoggingInterceptor | ++ 0 % + (0/3) + | +
+ + src/app/_models/account.ts + | +interface | +AccountDetails | ++ 0 % + (0/11) + | +
+ + src/app/_models/account.ts + | +interface | +Meta | ++ 0 % + (0/4) + | +
+ + src/app/_models/account.ts + | +interface | +MetaResponse | ++ 0 % + (0/3) + | +
+ + src/app/_models/account.ts + | +interface | +Signature | ++ 0 % + (0/5) + | +
+ + src/app/_models/account.ts + | +variable | +defaultAccount | ++ 0 % + (0/1) + | +
+ + src/app/_models/mappings.ts + | +interface | +Action | ++ 0 % + (0/6) + | +
+ + src/app/_models/mappings.ts + | +interface | +AreaName | ++ 0 % + (0/3) + | +
+ + src/app/_models/mappings.ts + | +interface | +AreaType | ++ 0 % + (0/3) + | +
+ + src/app/_models/mappings.ts + | +interface | +Category | ++ 0 % + (0/3) + | +
+ + src/app/_models/settings.ts + | +class | +Settings | ++ 0 % + (0/6) + | +
+ + src/app/_models/settings.ts + | +class | +W3 | ++ 0 % + (0/3) + | +
+ + src/app/_models/staff.ts + | +interface | +Staff | ++ 0 % + (0/6) + | +
+ + src/app/_models/token.ts + | +interface | +Token | ++ 0 % + (0/9) + | +
+ + src/app/_models/transaction.ts + | +class | +BlocksBloom | ++ 0 % + (0/6) + | +
+ + src/app/_models/transaction.ts + | +class | +Conversion | ++ 0 % + (0/8) + | +
+ + src/app/_models/transaction.ts + | +class | +Transaction | ++ 0 % + (0/9) + | +
+ + src/app/_models/transaction.ts + | +class | +Tx | ++ 0 % + (0/6) + | +
+ + src/app/_models/transaction.ts + | +class | +TxToken | ++ 0 % + (0/4) + | +
+ + src/app/_pgp/pgp-key-store.ts + | +class | +MutablePgpKeyStore | ++ 0 % + (0/26) + | +
+ + src/app/_pgp/pgp-key-store.ts + | +interface | +MutableKeyStore | ++ 0 % + (0/26) + | +
+ + src/app/_pgp/pgp-key-store.ts + | +variable | +keyring | ++ 0 % + (0/1) + | +
+ + src/app/_pgp/pgp-signer.ts + | +class | +PGPSigner | ++ 0 % + (0/14) + | +
+ + src/app/_pgp/pgp-signer.ts + | +interface | +Signable | ++ 0 % + (0/2) + | +
+ + src/app/_pgp/pgp-signer.ts + | +interface | +Signer | ++ 0 % + (0/7) + | +
+ + src/app/_pgp/pgp-signer.ts + | +variable | +openpgp | ++ 0 % + (0/1) + | +
+ + src/app/_services/auth.service.ts + | +injectable | +AuthService | ++ 0 % + (0/18) + | +
+ + src/app/_services/block-sync.service.ts + | +injectable | +BlockSyncService | ++ 0 % + (0/10) + | +
+ + src/app/_services/error-dialog.service.ts + | +injectable | +ErrorDialogService | ++ 0 % + (0/5) + | +
+ + src/app/_services/location.service.ts + | +injectable | +LocationService | ++ 0 % + (0/6) + | +
+ + src/app/_services/logging.service.ts + | +injectable | +LoggingService | ++ 0 % + (0/11) + | +
+ + src/app/_services/registry.service.ts + | +injectable | +RegistryService | ++ 0 % + (0/7) + | +
+ + src/app/_services/token.service.ts + | +injectable | +TokenService | ++ 0 % + (0/8) + | +
+ + src/app/_services/transaction.service.ts + | +injectable | +TransactionService | ++ 0 % + (0/16) + | +
+ + src/app/_services/transaction.service.ts + | +variable | +vCard | ++ 0 % + (0/1) + | +
+ + src/app/_services/user.service.ts + | +injectable | +UserService | ++ 0 % + (0/33) + | +
+ + src/app/_services/user.service.ts + | +variable | +vCard | ++ 0 % + (0/1) + | +
+ + src/app/app.component.ts + | +component | +AppComponent | ++ 0 % + (0/9) + | +
+ + src/app/auth/_directives/password-toggle.directive.ts + | +directive | +PasswordToggleDirective | ++ 0 % + (0/5) + | +
+ + src/app/auth/auth.component.ts + | +component | +AuthComponent | ++ 0 % + (0/11) + | +
+ + src/app/pages/accounts/account-details/account-details.component.ts + | +component | +AccountDetailsComponent | ++ 0 % + (0/43) + | +
+ + src/app/pages/accounts/account-search/account-search.component.ts + | +component | +AccountSearchComponent | ++ 0 % + (0/16) + | +
+ + src/app/pages/accounts/accounts.component.ts + | +component | +AccountsComponent | ++ 0 % + (0/17) + | +
+ + src/app/pages/accounts/create-account/create-account.component.ts + | +component | +CreateAccountComponent | ++ 0 % + (0/11) + | +
+ + src/app/pages/admin/admin.component.ts + | +component | +AdminComponent | ++ 0 % + (0/15) + | +
+ + src/app/pages/pages.component.ts + | +component | +PagesComponent | ++ 0 % + (0/3) + | +
+ + src/app/pages/settings/organization/organization.component.ts + | +component | +OrganizationComponent | ++ 0 % + (0/7) + | +
+ + src/app/pages/settings/settings.component.ts + | +component | +SettingsComponent | ++ 0 % + (0/12) + | +
+ + src/app/pages/tokens/token-details/token-details.component.ts + | +component | +TokenDetailsComponent | ++ 0 % + (0/4) + | +
+ + src/app/pages/tokens/tokens.component.ts + | +component | +TokensComponent | ++ 0 % + (0/11) + | +
+ + src/app/pages/transactions/transaction-details/transaction-details.component.ts + | +component | +TransactionDetailsComponent | ++ 0 % + (0/12) + | +
+ + src/app/pages/transactions/transactions.component.ts + | +component | +TransactionsComponent | ++ 0 % + (0/18) + | +
+ + src/app/shared/_directives/menu-selection.directive.ts + | +directive | +MenuSelectionDirective | ++ 0 % + (0/3) + | +
+ + src/app/shared/_directives/menu-toggle.directive.ts + | +directive | +MenuToggleDirective | ++ 0 % + (0/3) + | +
+ + src/app/shared/_pipes/safe.pipe.ts + | +pipe | +SafePipe | ++ 0 % + (0/1) + | +
+ + src/app/shared/_pipes/token-ratio.pipe.ts + | +pipe | +TokenRatioPipe | ++ 0 % + (0/1) + | +
+ + src/app/shared/error-dialog/error-dialog.component.ts + | +component | +ErrorDialogComponent | ++ 0 % + (0/3) + | +
+ + src/app/shared/footer/footer.component.ts + | +component | +FooterComponent | ++ 0 % + (0/3) + | +
+ + src/app/shared/sidebar/sidebar.component.ts + | +component | +SidebarComponent | ++ 0 % + (0/3) + | +
+ + src/app/shared/topbar/topbar.component.ts + | +component | +TopbarComponent | ++ 0 % + (0/3) + | +
+ + src/environments/environment.dev.ts + | +variable | +environment | ++ 0 % + (0/1) + | +
+ + src/environments/environment.prod.ts + | +variable | +environment | ++ 0 % + (0/1) + | +
+ + src/environments/environment.ts + | +variable | +environment | ++ 0 % + (0/1) + | +
+ + src/testing/activated-route-stub.ts + | +class | +ActivatedRouteStub | ++ 60 % + (3/5) + | +
+ + src/testing/router-link-directive-stub.ts + | +directive | +RouterLinkDirectiveStub | ++ 0 % + (0/4) + | +
+ + src/testing/shared-module-stub.ts + | +component | +FooterStubComponent | ++ 0 % + (0/1) + | +
+ + src/testing/shared-module-stub.ts + | +component | +SidebarStubComponent | ++ 0 % + (0/1) + | +
+ + src/testing/shared-module-stub.ts + | +component | +TopbarStubComponent | ++ 0 % + (0/1) + | +
+ + src/testing/token-service-stub.ts + | +class | +TokenServiceStub | ++ 0 % + (0/2) + | +
+ + src/testing/transaction-service-stub.ts + | +class | +TransactionServiceStub | ++ 0 % + (0/4) + | +
+ + src/testing/user-service-stub.ts + | +class | +UserServiceStub | ++ 0 % + (0/7) + | +
+
+ src/app/shared/_directives/menu-selection.directive.ts
+
Selector | +[appMenuSelection] |
+
+ Methods+ |
+
+
|
+
+constructor(elementRef: ElementRef, renderer: Renderer2)
+ |
+ |||||||||
+ + | +|||||||||
+
+ Parameters :
+
+
|
+
+ + + + onMenuSelect + + + + | +
+onMenuSelect()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
import {Directive, ElementRef, Renderer2} from '@angular/core';
+
+@Directive({
+ selector: '[appMenuSelection]'
+})
+export class MenuSelectionDirective {
+
+ constructor(
+ private elementRef: ElementRef,
+ private renderer: Renderer2
+ ) {
+ this.renderer.listen(this.elementRef.nativeElement, 'click', () => {
+ const mediaQuery = window.matchMedia('(max-width: 768px)');
+ if (mediaQuery.matches) {
+ this.onMenuSelect();
+ }
+ });
+ }
+
+ onMenuSelect(): void {
+ const sidebar: HTMLElement = document.getElementById('sidebar');
+ if (!sidebar?.classList.contains('active')) {
+ sidebar?.classList.add('active');
+ }
+ const content: HTMLElement = document.getElementById('content');
+ if (!content?.classList.contains('active')) {
+ content?.classList.add('active');
+ }
+ const sidebarCollapse: HTMLElement = document.getElementById('sidebarCollapse');
+ if (sidebarCollapse?.classList.contains('active')) {
+ sidebarCollapse?.classList.remove('active');
+ }
+ }
+}
+
+ +
+ src/app/shared/_directives/menu-toggle.directive.ts
+
Selector | +[appMenuToggle] |
+
+ Methods+ |
+
+
|
+
+constructor(elementRef: ElementRef, renderer: Renderer2)
+ |
+ |||||||||
+ + | +|||||||||
+
+ Parameters :
+
+
|
+
+ + + + onMenuToggle + + + + | +
+onMenuToggle()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
import {Directive, ElementRef, Renderer2} from '@angular/core';
+
+@Directive({
+ selector: '[appMenuToggle]'
+})
+export class MenuToggleDirective {
+
+ constructor(
+ private elementRef: ElementRef,
+ private renderer: Renderer2
+ ) {
+ this.renderer.listen(this.elementRef.nativeElement, 'click', () => {
+ this.onMenuToggle();
+ });
+ }
+
+ // Menu Trigger
+ onMenuToggle(): void {
+ const sidebar: HTMLElement = document.getElementById('sidebar');
+ sidebar?.classList.toggle('active');
+ const content: HTMLElement = document.getElementById('content');
+ content?.classList.toggle('active');
+ const sidebarCollapse: HTMLElement = document.getElementById('sidebarCollapse');
+ sidebarCollapse?.classList.toggle('active');
+ }
+}
+
+ +
+ src/app/auth/_directives/password-toggle.directive.ts
+
Selector | +[appPasswordToggle] |
+
+ Methods+ |
+
+
|
+
+ Inputs+ |
+
+ + | +
+constructor(elementRef: ElementRef, renderer: Renderer2)
+ |
+ |||||||||
+ + | +|||||||||
+
+ Parameters :
+
+
|
+
+ + iconId + | +|
+ Type : string
+
+ |
+ |
+ + | +
+ + id + | +|
+ Type : string
+
+ |
+ |
+ + | +
+ + + + togglePasswordVisibility + + + + | +
+togglePasswordVisibility()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
import {Directive, ElementRef, Input, Renderer2} from '@angular/core';
+
+@Directive({
+ selector: '[appPasswordToggle]'
+})
+export class PasswordToggleDirective {
+ @Input()
+ id: string;
+
+ @Input()
+ iconId: string;
+
+ constructor(
+ private elementRef: ElementRef,
+ private renderer: Renderer2,
+ ) {
+ this.renderer.listen(this.elementRef.nativeElement, 'click', () => {
+ this.togglePasswordVisibility();
+ });
+ }
+
+ togglePasswordVisibility(): void {
+ const password: HTMLElement = document.getElementById(this.id);
+ const icon: HTMLElement = document.getElementById(this.iconId);
+ // @ts-ignore
+ if (password.type === 'password') {
+ // @ts-ignore
+ password.type = 'text';
+ icon.classList.remove('fa-eye');
+ icon.classList.add('fa-eye-slash');
+ } else {
+ // @ts-ignore
+ password.type = 'password';
+ icon.classList.remove('fa-eye-slash');
+ icon.classList.add('fa-eye');
+ }
+ }
+}
+
+ +
+ src/testing/router-link-directive-stub.ts
+
Selector | +[routerLink] |
+
+ Properties+ |
+
+
|
+
+ Inputs+ |
+
+
|
+
+ HostListeners+ |
+
+
|
+
+ + routerLink + | +|
+ Type : any
+
+ |
+ |
+ Defined in src/testing/router-link-directive-stub.ts:7
+ |
+
+ + + + click + + + + | +
+click()
+ |
+
+ Defined in src/testing/router-link-directive-stub.ts:11
+ |
+
+ + + + navigatedTo + + + | +
+ Type : any
+
+ |
+
+ Default value : null
+ |
+
+ Defined in src/testing/router-link-directive-stub.ts:8
+ |
+
import {Directive, HostListener, Input} from '@angular/core';
+
+@Directive({
+ selector: '[routerLink]'
+})
+export class RouterLinkDirectiveStub {
+ @Input('routerLink') linkParams: any;
+ navigatedTo: any = null;
+
+ @HostListener('click')
+ onClick(): void {
+ this.navigatedTo = this.linkParams;
+ }
+}
+
+ +
+ src/app/_guards/auth.guard.ts
+
+ Methods+ |
+
+
|
+
+constructor(router: Router)
+ |
+ ||||||
+ Defined in src/app/_guards/auth.guard.ts:8
+ |
+ ||||||
+
+ Parameters :
+
+
|
+
+ + + + canActivate + + + + | +|||||||||
+canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
+ |
+ |||||||||
+ Defined in src/app/_guards/auth.guard.ts:12
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable | Promise | boolean | UrlTree
+
+
+
+
+ |
+
import { Injectable } from '@angular/core';
+import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router} from '@angular/router';
+import { Observable } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class AuthGuard implements CanActivate {
+
+ constructor(private router: Router) {}
+
+ canActivate(
+ route: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
+ if (localStorage.getItem(btoa('CICADA_PRIVATE_KEY'))) {
+ return true;
+ }
+ this.router.navigate(['/auth']);
+ return false;
+ }
+
+}
+
+ +
+ src/app/_guards/role.guard.ts
+
+ Methods+ |
+
+
|
+
+constructor(router: Router)
+ |
+ ||||||
+ Defined in src/app/_guards/role.guard.ts:8
+ |
+ ||||||
+
+ Parameters :
+
+
|
+
+ + + + canActivate + + + + | +|||||||||
+canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
+ |
+ |||||||||
+ Defined in src/app/_guards/role.guard.ts:12
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable | Promise | boolean | UrlTree
+
+
+
+
+ |
+
import { Injectable } from '@angular/core';
+import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router} from '@angular/router';
+import { Observable } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class RoleGuard implements CanActivate {
+
+ constructor(private router: Router) {}
+
+ canActivate(
+ route: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
+ const currentUser = JSON.parse(localStorage.getItem(atob('CICADA_USER')));
+ if (currentUser) {
+ if (route.data.roles && route.data.roles.indexOf(currentUser.role) === -1) {
+ this.router.navigate(['/']);
+ return false;
+ }
+ return true;
+ }
+
+ this.router.navigate(['/auth'], { queryParams: { returnUrl: state.url }});
+ return false;
+ }
+
+}
+
+ An angular admin web client for managing users and transactions in the CIC network.
+This project was generated with Angular CLI version 10.2.0.
+Run npm install -g @angular/cli
to install the angular CLI.
Run npm run start:dev
for a dev server. Navigate to http://localhost:4200/
. The app will automatically reload if you change any of the source files.
Run ng generate component component-name
to generate a new component. You can also use ng generate directive|pipe|service|class|guard|interface|enum|module
.
Run ng generate module module-name --route module-name --module app.module
to generate a new module on route /module-name
in the app module.
set you environment variables - set these via environment variables as found in set-env.ts +// TODO create a .env file so people don't have to set these one-by-one
+Run npm run build:dev
to build the project. The build artifacts will be stored in the dist/
directory. Use the build:prod
script for a production build.
Run npm run test:dev
to execute the unit tests via Karma.
Run ng e2e
to execute the end-to-end tests via Protractor.
Environment variables are contained in the .env
file. See .env.example
for a template.
Default environment variables are set in the set-env.ts
file.
+Once loaded they will be populated in the directory src/environments/
.
+It contains environment variables for development on environment.ts
and production on environment.prod.ts
.
To get more help on the Angular CLI use ng help
or go check out the Angular CLI Overview and Command Reference page.
+
+ src/app/_services/auth.service.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(httpClient: HttpClient, loggingService: LoggingService, errorDialogService: ErrorDialogService)
+ |
+ ||||||||||||
+ Defined in src/app/_services/auth.service.ts:17
+ |
+ ||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + getChallenge + + + + | +
+getChallenge()
+ |
+
+ Defined in src/app/_services/auth.service.ts:84
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + getPrivateKey + + + + | +
+getPrivateKey()
+ |
+
+ Defined in src/app/_services/auth.service.ts:200
+ |
+
+
+
+ Returns :
+ any
+
+ |
+
+ + + + Async + getPublicKeys + + + + | +
+
+ getPublicKeys()
+ |
+
+ Defined in src/app/_services/auth.service.ts:189
+ |
+
+
+
+ Returns :
+ Promise<any>
+
+ |
+
+ + + + getTrustedUsers + + + + | +
+getTrustedUsers()
+ |
+
+ Defined in src/app/_services/auth.service.ts:183
+ |
+
+
+
+ Returns :
+ any
+
+ |
+
+ + + + getWithToken + + + + | +
+getWithToken()
+ |
+
+ Defined in src/app/_services/auth.service.ts:42
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + Async + init + + + + | +
+
+ init()
+ |
+
+ Defined in src/app/_services/auth.service.ts:27
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + login + + + + | +
+login()
+ |
+
+ Defined in src/app/_services/auth.service.ts:99
+ |
+
+
+
+ Returns :
+ boolean
+
+ |
+
+ + + + Async + loginResponse + + + + | +||||||
+
+ loginResponse(o: literal type)
+ |
+ ||||||
+ Defined in src/app/_services/auth.service.ts:118
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<any>
+
+
+
+
+ |
+
+ + + + loginView + + + + | +
+loginView()
+ |
+
+ Defined in src/app/_services/auth.service.ts:147
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + logout + + + + | +
+logout()
+ |
+
+ Defined in src/app/_services/auth.service.ts:177
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + sendResponse + + + + | +||||||
+sendResponse(hobaResponseEncoded: any)
+ |
+ ||||||
+ Defined in src/app/_services/auth.service.ts:61
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+
+ |
+
+ + + + Async + setKey + + + + | +||||
+
+ setKey(privateKeyArmored)
+ |
+ ||||
+ Defined in src/app/_services/auth.service.ts:153
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+
+ |
+
+ + + + setState + + + + | +||||
+setState(s)
+ |
+ ||||
+ Defined in src/app/_services/auth.service.ts:38
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + mutableKeyStore + + + | +
+ Type : MutableKeyStore
+
+ |
+
+ Defined in src/app/_services/auth.service.ts:17
+ |
+
+ + + + sessionLoginCount + + + | +
+ Type : number
+
+ |
+
+ Default value : 0
+ |
+
+ Defined in src/app/_services/auth.service.ts:16
+ |
+
+ + + + sessionToken + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_services/auth.service.ts:15
+ |
+
import {Injectable} from '@angular/core';
+import {hobaParseChallengeHeader} from '@src/assets/js/hoba.js';
+import {signChallenge} from '@src/assets/js/hoba-pgp.js';
+import {environment} from '@src/environments/environment';
+import {LoggingService} from '@app/_services/logging.service';
+import {MutableKeyStore, MutablePgpKeyStore} from '@app/_pgp';
+import {ErrorDialogService} from '@app/_services/error-dialog.service';
+import {HttpClient} from '@angular/common/http';
+import {HttpError} from '@app/_helpers/global-error-handler';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class AuthService {
+ sessionToken: any;
+ sessionLoginCount: number = 0;
+ mutableKeyStore: MutableKeyStore;
+
+ constructor(
+ private httpClient: HttpClient,
+ private loggingService: LoggingService,
+ private errorDialogService: ErrorDialogService
+ ) {
+ this.mutableKeyStore = new MutablePgpKeyStore();
+ }
+
+ async init(): Promise<void> {
+ await this.mutableKeyStore.loadKeyring();
+ // TODO setting these together should be atomic
+ if (sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN'))) {
+ this.sessionToken = sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN'));
+ }
+ if (localStorage.getItem(btoa('CICADA_PRIVATE_KEY'))) {
+ await this.mutableKeyStore.importPrivateKey(localStorage.getItem(btoa('CICADA_PRIVATE_KEY')));
+ }
+ }
+
+ setState(s): void {
+ document.getElementById('state').innerHTML = s;
+ }
+
+ getWithToken(): void {
+ const xhr: XMLHttpRequest = new XMLHttpRequest();
+ xhr.responseType = 'text';
+ xhr.open('GET', environment.cicMetaUrl + window.location.search.substring(1));
+ xhr.setRequestHeader('Authorization', 'Bearer ' + this.sessionToken);
+ xhr.setRequestHeader('Content-Type', 'application/json');
+ xhr.setRequestHeader('x-cic-automerge', 'none');
+ xhr.addEventListener('load', (e) => {
+ if (xhr.status === 401) {
+ throw new Error('login rejected');
+ }
+ this.sessionLoginCount++;
+ this.setState('Click button to log in');
+ return;
+ });
+ xhr.send();
+ }
+
+ // TODO rename to send signed challenge and set session. Also separate these responsibilities
+ sendResponse(hobaResponseEncoded: any): Promise<boolean> {
+ return new Promise((resolve, reject) => {
+ const xhr: XMLHttpRequest = new XMLHttpRequest();
+ xhr.responseType = 'text';
+ xhr.open('GET', environment.cicMetaUrl + window.location.search.substring(1));
+ xhr.setRequestHeader('Authorization', 'HOBA ' + hobaResponseEncoded);
+ xhr.setRequestHeader('Content-Type', 'application/json');
+ xhr.setRequestHeader('x-cic-automerge', 'none');
+ xhr.addEventListener('load', (e) => {
+ if (xhr.status !== 200) {
+ const error = new HttpError(xhr.statusText, xhr.status);
+ return reject(error);
+ }
+ this.sessionToken = xhr.getResponseHeader('Token');
+ sessionStorage.setItem(btoa('CICADA_SESSION_TOKEN'), this.sessionToken);
+ this.sessionLoginCount++;
+ this.setState('Click button to log in');
+ return resolve(true);
+ });
+ xhr.send();
+ });
+ }
+
+ getChallenge(): void {
+ const xhr: XMLHttpRequest = new XMLHttpRequest();
+ xhr.responseType = 'arraybuffer';
+ xhr.open('GET', environment.cicMetaUrl + window.location.search.substring(1));
+ xhr.onload = async (e) => {
+ if (xhr.status === 401) {
+ const authHeader = xhr.getResponseHeader('WWW-Authenticate');
+ const o = hobaParseChallengeHeader(authHeader);
+ this.loginResponse(o);
+ }
+ };
+ xhr.send();
+ }
+
+
+ login(): boolean {
+ if (this.sessionToken !== undefined) {
+ try {
+ this.getWithToken();
+ return true;
+ } catch (e) {
+ this.loggingService.sendErrorLevelMessage('Login token failed', this, {error: e});
+ }
+ } else {
+ try {
+ this.getChallenge();
+ } catch (e) {
+ this.loggingService.sendErrorLevelMessage('Login challenge failed', this, {error: e});
+ }
+ }
+ return false;
+ }
+
+
+ async loginResponse(o: { challenge: string, realm: any }): Promise<any> {
+ return new Promise(async (resolve, reject) => {
+ try {
+ const r = await signChallenge(o.challenge,
+ o.realm,
+ environment.cicMetaUrl,
+ this.mutableKeyStore);
+ const sessionTokenResult: boolean = await this.sendResponse(r);
+ } catch (error) {
+ if (error instanceof HttpError) {
+ if (error.status === 403) {
+ this.errorDialogService.openDialog({ message: 'You are not authorized to use this system' });
+ }
+ if (error.status === 401) {
+ this.errorDialogService.openDialog({
+ message: 'Unable to authenticate with the service. ' +
+ 'Please speak with the staff at Grassroots ' +
+ 'Economics for requesting access ' +
+ 'staff@grassrootseconomics.net.'
+ });
+ }
+ }
+ // TODO define this error
+ this.errorDialogService.openDialog({message: 'Incorrect key passphrase.'});
+ resolve(false);
+ }
+ });
+ }
+
+ loginView(): void {
+ document.getElementById('one').style.display = 'none';
+ document.getElementById('two').style.display = 'block';
+ this.setState('Click button to log in with PGP key ' + this.mutableKeyStore.getPrivateKeyId());
+ }
+
+ async setKey(privateKeyArmored): Promise<boolean> {
+ try {
+ const isValidKeyCheck = await this.mutableKeyStore.isValidKey(privateKeyArmored);
+ if (!isValidKeyCheck) {
+ throw Error('The private key is invalid');
+ }
+ // TODO leaving this out for now.
+ // const isEncryptedKeyCheck = await this.mutableKeyStore.isEncryptedPrivateKey(privateKeyArmored);
+ // if (!isEncryptedKeyCheck) {
+ // throw Error('The private key doesn\'t have a password!');
+ // }
+ const key = await this.mutableKeyStore.importPrivateKey(privateKeyArmored);
+ localStorage.setItem(btoa('CICADA_PRIVATE_KEY'), privateKeyArmored);
+ } catch (err) {
+ this.loggingService.sendErrorLevelMessage(`Failed to set key: ${err.message || err.statusText}`, this, {error: err});
+ this.errorDialogService.openDialog({
+ message: `Failed to set key: ${err.message || err.statusText}`,
+ });
+ return false;
+ }
+ this.loginView();
+ return true;
+ }
+
+ logout(): void {
+ sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN'));
+ this.sessionToken = undefined;
+ window.location.reload(true);
+ }
+
+ getTrustedUsers(): any {
+ const trustedUsers: Array<any> = [];
+ this.mutableKeyStore.getPublicKeys().forEach(key => trustedUsers.push(key.users[0].userId));
+ return trustedUsers;
+ }
+
+ async getPublicKeys(): Promise<any> {
+ return await fetch(environment.publicKeysUrl)
+ .then(res => {
+ if (!res.ok) {
+ // TODO does angular recommend an error interface?
+ throw Error(`${res.statusText} - ${res.status}`);
+ }
+ return res.text();
+ });
+ }
+
+ getPrivateKey(): any {
+ return this.mutableKeyStore.getPrivateKey();
+ }
+}
+
+ +
+ src/app/_services/block-sync.service.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(transactionService: TransactionService, loggingService: LoggingService, registryService: RegistryService)
+ |
+ ||||||||||||
+ Defined in src/app/_services/block-sync.service.ts:15
+ |
+ ||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + blockSync + + + + | +||||||||||||||||
+blockSync(address: string, offset: number, limit: number)
+ |
+ ||||||||||||||||
+ Defined in src/app/_services/block-sync.service.ts:23
+ |
+ ||||||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + fetcher + + + + | +|||||||||
+fetcher(settings: Settings, transactionsInfo: any)
+ |
+ |||||||||
+ Defined in src/app/_services/block-sync.service.ts:101
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + newConversionEvent + + + + | +||||||
+newConversionEvent(tx: any)
+ |
+ ||||||
+ Defined in src/app/_services/block-sync.service.ts:76
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + newTransferEvent + + + + | +||||||
+newTransferEvent(tx: any)
+ |
+ ||||||
+ Defined in src/app/_services/block-sync.service.ts:68
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + readyStateProcessor + + + + | +||||||||||||||||||
+readyStateProcessor(settings: Settings, bit: number, address: string, offset: number, limit: number)
+ |
+ ||||||||||||||||||
+ Defined in src/app/_services/block-sync.service.ts:46
+ |
+ ||||||||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + Async + scan + + + + | +|||||||||||||||||||||
+
+ scan(settings: Settings, lo: number, hi: number, bloomBlockBytes: Uint8Array, bloomBlocktxBytes: Uint8Array, bloomRounds: any)
+ |
+ |||||||||||||||||||||
+ Defined in src/app/_services/block-sync.service.ts:84
+ |
+ |||||||||||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + readyState + + + | +
+ Type : number
+
+ |
+
+ Default value : 0
+ |
+
+ Defined in src/app/_services/block-sync.service.ts:15
+ |
+
+ + + + readyStateTarget + + + | +
+ Type : number
+
+ |
+
+ Default value : 2
+ |
+
+ Defined in src/app/_services/block-sync.service.ts:14
+ |
+
import {Injectable} from '@angular/core';
+import {Settings} from '@app/_models';
+import {TransactionHelper} from 'cic-client';
+import {first} from 'rxjs/operators';
+import {TransactionService} from '@app/_services/transaction.service';
+import {environment} from '@src/environments/environment';
+import {LoggingService} from '@app/_services/logging.service';
+import {RegistryService} from '@app/_services/registry.service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class BlockSyncService {
+ readyStateTarget: number = 2;
+ readyState: number = 0;
+
+ constructor(
+ private transactionService: TransactionService,
+ private loggingService: LoggingService,
+ private registryService: RegistryService,
+ ) { }
+
+ blockSync(address: string = null, offset: number = 0, limit: number = 100): void {
+ this.transactionService.resetTransactionsList();
+ const settings: Settings = new Settings(this.scan);
+ const readyStateElements: { network: number } = { network: 2 };
+ settings.w3.provider = environment.web3Provider;
+ settings.w3.engine = this.registryService.getWeb3();
+ settings.registry = this.registryService.getRegistry();
+ settings.txHelper = new TransactionHelper(settings.w3.engine, settings.registry);
+
+ settings.txHelper.ontransfer = async (transaction: any): Promise<void> => {
+ window.dispatchEvent(this.newTransferEvent(transaction));
+ };
+ settings.txHelper.onconversion = async (transaction: any): Promise<any> => {
+ window.dispatchEvent(this.newConversionEvent(transaction));
+ };
+ settings.registry.onload = (addressReturned: number): void => {
+ this.loggingService.sendInfoLevelMessage(`Loaded network contracts ${addressReturned}`);
+ this.readyStateProcessor(settings, readyStateElements.network, address, offset, limit);
+ };
+
+ settings.registry.load();
+ }
+
+ readyStateProcessor(settings: Settings, bit: number, address: string, offset: number, limit: number): void {
+ this.readyState |= bit;
+ if (this.readyStateTarget === this.readyState && this.readyStateTarget) {
+ const wHeadSync: Worker = new Worker('./../assets/js/block-sync/head.js');
+ wHeadSync.onmessage = (m) => {
+ settings.txHelper.processReceipt(m.data);
+ };
+ wHeadSync.postMessage({
+ w3_provider: settings.w3.provider,
+ });
+ if (address === null) {
+ this.transactionService.getAllTransactions(offset, limit).pipe(first()).subscribe(res => {
+ this.fetcher(settings, res);
+ });
+ } else {
+ this.transactionService.getAddressTransactions(address, offset, limit).pipe(first()).subscribe(res => {
+ this.fetcher(settings, res);
+ });
+ }
+ }
+ }
+
+ newTransferEvent(tx: any): any {
+ return new CustomEvent('cic_transfer', {
+ detail: {
+ tx,
+ },
+ });
+ }
+
+ newConversionEvent(tx: any): any {
+ return new CustomEvent('cic_convert', {
+ detail: {
+ tx,
+ },
+ });
+ }
+
+ async scan(settings: Settings, lo: number, hi: number, bloomBlockBytes: Uint8Array, bloomBlocktxBytes: Uint8Array, bloomRounds: any): Promise<void> {
+ const w: Worker = new Worker('./../assets/js/block-sync/ondemand.js');
+ w.onmessage = (m) => {
+ settings.txHelper.processReceipt(m.data);
+ };
+ w.postMessage({
+ w3_provider: settings.w3.provider,
+ lo,
+ hi,
+ filters: [
+ bloomBlockBytes,
+ bloomBlocktxBytes,
+ ],
+ filter_rounds: bloomRounds,
+ });
+ }
+
+ fetcher(settings: Settings, transactionsInfo: any): void {
+ const blockFilterBinstr: string = window.atob(transactionsInfo.block_filter);
+ const bOne: Uint8Array = new Uint8Array(blockFilterBinstr.length);
+ bOne.map((e, i, v) => v[i] = blockFilterBinstr.charCodeAt(i));
+
+ const blocktxFilterBinstr: string = window.atob(transactionsInfo.blocktx_filter);
+ const bTwo: Uint8Array = new Uint8Array(blocktxFilterBinstr.length);
+ bTwo.map((e, i, v) => v[i] = blocktxFilterBinstr.charCodeAt(i));
+
+ settings.scanFilter(settings, transactionsInfo.low, transactionsInfo.high, bOne, bTwo, transactionsInfo.filter_rounds);
+ }
+}
+
+ +
+ src/app/_services/error-dialog.service.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(dialog: MatDialog)
+ |
+ ||||||
+ Defined in src/app/_services/error-dialog.service.ts:9
+ |
+ ||||||
+
+ Parameters :
+
+
|
+
+ + + + openDialog + + + + | +||||
+openDialog(data)
+ |
+ ||||
+ Defined in src/app/_services/error-dialog.service.ts:15
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + Public + dialog + + + | +
+ Type : MatDialog
+
+ |
+
+ Defined in src/app/_services/error-dialog.service.ts:12
+ |
+
+ + + + Public + isDialogOpen + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ Defined in src/app/_services/error-dialog.service.ts:9
+ |
+
import { Injectable } from '@angular/core';
+import {MatDialog, MatDialogRef} from '@angular/material/dialog';
+import {ErrorDialogComponent} from '@app/shared/error-dialog/error-dialog.component';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ErrorDialogService {
+ public isDialogOpen: boolean = false;
+
+ constructor(
+ public dialog: MatDialog,
+ ) { }
+
+ openDialog(data): any {
+ if (this.isDialogOpen) {
+ return false;
+ }
+ this.isDialogOpen = true;
+ const dialogRef: MatDialogRef<any> = this.dialog.open(ErrorDialogComponent, {
+ width: '300px',
+ data
+ });
+
+ dialogRef.afterClosed().subscribe(() => this.isDialogOpen = false);
+ }
+}
+
+ +
+ src/app/_helpers/global-error-handler.ts
+
+
+ ErrorHandler
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(loggingService: LoggingService, router: Router)
+ |
+ |||||||||
+ Defined in src/app/_helpers/global-error-handler.ts:18
+ |
+ |||||||||
+
+ Parameters :
+
+
|
+
+ + + + handleError + + + + | +||||||
+handleError(error: Error)
+ |
+ ||||||
+ Defined in src/app/_helpers/global-error-handler.ts:27
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + Private + isWarning + + + + | +||||||
+
+ isWarning(errorTraceString: string)
+ |
+ ||||||
+ Defined in src/app/_helpers/global-error-handler.ts:62
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ boolean
+
+
+
+
+ |
+
+ + + + logError + + + + | +||||||
+logError(error: any)
+ |
+ ||||||
+ Defined in src/app/_helpers/global-error-handler.ts:47
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + Private + sentencesForWarningLogging + + + | +
+ Type : Array<string>
+
+ |
+
+ Default value : []
+ |
+
+ Defined in src/app/_helpers/global-error-handler.ts:18
+ |
+
import {ErrorHandler, Injectable} from '@angular/core';
+import {LoggingService} from '@app/_services/logging.service';
+import {HttpErrorResponse} from '@angular/common/http';
+import {Router} from '@angular/router';
+
+// A generalized http response error
+export class HttpError extends Error {
+ public status: number;
+ constructor(message: string, status: number) {
+ super(message);
+ this.status = status;
+ this.name = 'HttpError';
+ }
+}
+
+@Injectable()
+export class GlobalErrorHandler extends ErrorHandler {
+ private sentencesForWarningLogging: Array<string> = [];
+
+ constructor(
+ private loggingService: LoggingService,
+ private router: Router
+ ) {
+ super();
+ }
+
+ handleError(error: Error): void {
+ this.logError(error);
+ const message: string = error.message ? error.message : error.toString();
+
+ // if (error.status) {
+ // error = new Error(message);
+ // }
+
+ const errorTraceString: string = `Error message:\n${message}.\nStack trace: ${error.stack}`;
+
+ const isWarning: boolean = this.isWarning(errorTraceString);
+ if (isWarning) {
+ this.loggingService.sendWarnLevelMessage(errorTraceString, {error});
+ } else {
+ this.loggingService.sendErrorLevelMessage(errorTraceString, this, {error});
+ }
+
+ throw error;
+ }
+
+ logError(error: any): void {
+ const route: string = this.router.url;
+ if (error instanceof HttpErrorResponse) {
+ this.loggingService.sendErrorLevelMessage(
+ `There was an HTTP error on route ${route}.\n${error.message}.\nStatus code: ${(error as HttpErrorResponse).status}`,
+ this, {error});
+ } else if (error instanceof TypeError) {
+ this.loggingService.sendErrorLevelMessage(`There was a Type error on route ${route}.\n${error.message}`, this, {error});
+ } else if (error instanceof Error) {
+ this.loggingService.sendErrorLevelMessage(`There was a general error on route ${route}.\n${error.message}`, this, {error});
+ } else {
+ this.loggingService.sendErrorLevelMessage(`Nobody threw an error but something happened on route ${route}!`, this, {error});
+ }
+ }
+
+ private isWarning(errorTraceString: string): boolean {
+ let isWarning: boolean = true;
+ if (errorTraceString.includes('/src/app/')) {
+ isWarning = false;
+ }
+
+ this.sentencesForWarningLogging.forEach((whiteListSentence: string) => {
+ if (errorTraceString.includes(whiteListSentence)) {
+ isWarning = true;
+ }
+ });
+
+ return isWarning;
+ }
+}
+
+ +
+ src/app/_services/location.service.ts
+
+ Methods+ |
+
+
|
+
+constructor(httpClient: HttpClient)
+ |
+ ||||||
+ Defined in src/app/_services/location.service.ts:10
+ |
+ ||||||
+
+ Parameters :
+
+
|
+
+ + + + getAreaNameByLocation + + + + | +||||||
+getAreaNameByLocation(location: string)
+ |
+ ||||||
+ Defined in src/app/_services/location.service.ts:20
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getAreaNames + + + + | +
+getAreaNames()
+ |
+
+ Defined in src/app/_services/location.service.ts:16
+ |
+
+
+
+ Returns :
+ Observable<any>
+
+ |
+
+ + + + getAreaTypeByArea + + + + | +||||||
+getAreaTypeByArea(area: string)
+ |
+ ||||||
+ Defined in src/app/_services/location.service.ts:28
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getAreaTypes + + + + | +
+getAreaTypes()
+ |
+
+ Defined in src/app/_services/location.service.ts:24
+ |
+
+
+
+ Returns :
+ Observable<any>
+
+ |
+
import { Injectable } from '@angular/core';
+import {Observable} from 'rxjs';
+import {environment} from '@src/environments/environment';
+import {first} from 'rxjs/operators';
+import {HttpClient} from '@angular/common/http';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class LocationService {
+
+ constructor(
+ private httpClient: HttpClient,
+ ) { }
+
+ getAreaNames(): Observable<any> {
+ return this.httpClient.get(`${environment.cicMetaUrl}/areanames`);
+ }
+
+ getAreaNameByLocation(location: string): Observable<any> {
+ return this.httpClient.get(`${environment.cicMetaUrl}/areanames/${location.toLowerCase()}`);
+ }
+
+ getAreaTypes(): Observable<any> {
+ return this.httpClient.get(`${environment.cicMetaUrl}/areatypes`).pipe(first());
+ }
+
+ getAreaTypeByArea(area: string): Observable<any> {
+ return this.httpClient.get(`${environment.cicMetaUrl}/areatypes/${area.toLowerCase()}`).pipe(first());
+ }
+}
+
+ +
+ src/app/_services/logging.service.ts
+
+ Properties+ |
+
+ + | +
+ Methods+ |
+
+
|
+
+constructor(logger: NGXLogger)
+ |
+ ||||||
+ Defined in src/app/_services/logging.service.ts:9
+ |
+ ||||||
+
+ Parameters :
+
+
|
+
+ + + + sendDebugLevelMessage + + + + | +||||||||||||
+sendDebugLevelMessage(message: any, source: any, error: any)
+ |
+ ||||||||||||
+ Defined in src/app/_services/logging.service.ts:22
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + sendErrorLevelMessage + + + + | +||||||||||||
+sendErrorLevelMessage(message: any, source: any, error: any)
+ |
+ ||||||||||||
+ Defined in src/app/_services/logging.service.ts:38
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + sendFatalLevelMessage + + + + | +||||||||||||
+sendFatalLevelMessage(message: any, source: any, error: any)
+ |
+ ||||||||||||
+ Defined in src/app/_services/logging.service.ts:42
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + sendInfoLevelMessage + + + + | +||||||
+sendInfoLevelMessage(message: any)
+ |
+ ||||||
+ Defined in src/app/_services/logging.service.ts:26
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + sendLogLevelMessage + + + + | +||||||||||||
+sendLogLevelMessage(message: any, source: any, error: any)
+ |
+ ||||||||||||
+ Defined in src/app/_services/logging.service.ts:30
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + sendTraceLevelMessage + + + + | +||||||||||||
+sendTraceLevelMessage(message: any, source: any, error: any)
+ |
+ ||||||||||||
+ Defined in src/app/_services/logging.service.ts:18
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + sendWarnLevelMessage + + + + | +|||||||||
+sendWarnLevelMessage(message: any, error: any)
+ |
+ |||||||||
+ Defined in src/app/_services/logging.service.ts:34
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + canDebug + + + | +
+ Type : boolean
+
+ |
+
+ Defined in src/app/_services/logging.service.ts:9
+ |
+
+ + + + env + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_services/logging.service.ts:8
+ |
+
import {Injectable, isDevMode} from '@angular/core';
+import {NGXLogger} from 'ngx-logger';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class LoggingService {
+ env: string;
+ canDebug: boolean;
+
+ constructor(private logger: NGXLogger) {
+ // TRACE|DEBUG|INFO|LOG|WARN|ERROR|FATAL|OFF
+ if (isDevMode()) {
+ this.sendInfoLevelMessage('Dropping into debug mode');
+ }
+ }
+
+ sendTraceLevelMessage(message: any, source: any, error: any): void {
+ this.logger.trace(message, source, error);
+ }
+
+ sendDebugLevelMessage(message: any, source: any, error: any): void {
+ this.logger.debug(message, source, error);
+ }
+
+ sendInfoLevelMessage(message: any): void {
+ this.logger.info(message);
+ }
+
+ sendLogLevelMessage(message: any, source: any, error: any): void {
+ this.logger.log(message, source, error);
+ }
+
+ sendWarnLevelMessage(message: any, error: any): void {
+ this.logger.warn(message, error);
+ }
+
+ sendErrorLevelMessage(message: any, source: any, error: any): void {
+ this.logger.error(message, source, error);
+ }
+
+ sendFatalLevelMessage(message: any, source: any, error: any): void {
+ this.logger.fatal(message, source, error);
+ }
+}
+
+ +
+ src/app/_services/registry.service.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor()
+ |
+
+ Defined in src/app/_services/registry.service.ts:14
+ |
+
+ + + + getRegistry + + + + | +
+getRegistry()
+ |
+
+ Defined in src/app/_services/registry.service.ts:21
+ |
+
+
+
+ Returns :
+ any
+
+ |
+
+ + + + getWeb3 + + + + | +
+getWeb3()
+ |
+
+ Defined in src/app/_services/registry.service.ts:25
+ |
+
+
+
+ Returns :
+ any
+
+ |
+
+ + + + fileGetter + + + | +
+ Type : FileGetter
+
+ |
+
+ Default value : new HttpGetter()
+ |
+
+ Defined in src/app/_services/registry.service.ts:12
+ |
+
+ + + + registry + + + | +
+ Type : CICRegistry
+
+ |
+
+ Default value : new CICRegistry(this.web3, environment.registryAddress, 'CICRegistry', this.fileGetter,
+ ['../../assets/js/block-sync/data'])
+ |
+
+ Defined in src/app/_services/registry.service.ts:13
+ |
+
+ + + + web3 + + + | +
+ Type : Web3
+
+ |
+
+ Default value : new Web3(environment.web3Provider)
+ |
+
+ Defined in src/app/_services/registry.service.ts:11
+ |
+
import { Injectable } from '@angular/core';
+import Web3 from 'web3';
+import {environment} from '@src/environments/environment';
+import {CICRegistry, FileGetter} from 'cic-client';
+import {HttpGetter} from '@app/_helpers';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class RegistryService {
+ web3: Web3 = new Web3(environment.web3Provider);
+ fileGetter: FileGetter = new HttpGetter();
+ registry: CICRegistry = new CICRegistry(this.web3, environment.registryAddress, 'CICRegistry', this.fileGetter,
+ ['../../assets/js/block-sync/data']);
+
+ constructor() {
+ this.registry.declaratorHelper.addTrust(environment.trustedDeclaratorAddress);
+ this.registry.load();
+ }
+
+ getRegistry(): any {
+ return this.registry;
+ }
+
+ getWeb3(): any {
+ return this.web3;
+ }
+}
+
+ +
+ src/app/_services/token.service.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(httpClient: HttpClient, registryService: RegistryService)
+ |
+ |||||||||
+ Defined in src/app/_services/token.service.ts:15
+ |
+ |||||||||
+
+ Parameters :
+
+
|
+
+ + + + Async + getTokenBalance + + + + | +||||||
+
+ getTokenBalance(address: string)
+ |
+ ||||||
+ Defined in src/app/_services/token.service.ts:38
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<number>
+
+
+
+
+ |
+
+ + + + getTokenBySymbol + + + + | +||||||
+getTokenBySymbol(symbol: string)
+ |
+ ||||||
+ Defined in src/app/_services/token.service.ts:34
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + Async + getTokens + + + + | +
+
+ getTokens()
+ |
+
+ Defined in src/app/_services/token.service.ts:29
+ |
+
+
+
+ Returns :
+ Promise<Array<Promise<string>>>
+
+ |
+
+ + + + LoadEvent + + + | +
+ Type : EventEmitter<number>
+
+ |
+
+ Default value : new EventEmitter<number>()
+ |
+
+ Defined in src/app/_services/token.service.ts:15
+ |
+
+ + + + registry + + + | +
+ Type : CICRegistry
+
+ |
+
+ Defined in src/app/_services/token.service.ts:13
+ |
+
+ + + + tokenRegistry + + + | +
+ Type : TokenRegistry
+
+ |
+
+ Defined in src/app/_services/token.service.ts:14
+ |
+
import { EventEmitter, Injectable } from '@angular/core';
+import {environment} from '@src/environments/environment';
+import {BehaviorSubject, Observable} from 'rxjs';
+import {CICRegistry} from 'cic-client';
+import {TokenRegistry} from '@app/_eth';
+import {HttpClient} from '@angular/common/http';
+import {RegistryService} from '@app/_services/registry.service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TokenService {
+ registry: CICRegistry;
+ tokenRegistry: TokenRegistry;
+ LoadEvent: EventEmitter<number> = new EventEmitter<number>();
+
+ constructor(
+ private httpClient: HttpClient,
+ private registryService: RegistryService,
+ ) {
+ this.registry = registryService.getRegistry();
+ this.registry.load();
+ this.registry.onload = async (address: string): Promise<void> => {
+ this.tokenRegistry = new TokenRegistry(await this.registry.getContractAddressByName('TokenRegistry'));
+ this.LoadEvent.next(Date.now());
+ };
+ }
+
+ async getTokens(): Promise<Array<Promise<string>>> {
+ const count: number = await this.tokenRegistry.totalTokens();
+ return Array.from({length: count}, async (v, i) => await this.tokenRegistry.entry(i));
+ }
+
+ getTokenBySymbol(symbol: string): Observable<any> {
+ return this.httpClient.get(`${environment.cicCacheUrl}/tokens/${symbol}`);
+ }
+
+ async getTokenBalance(address: string): Promise<number> {
+ const sarafuToken = await this.registry.addToken(await this.tokenRegistry.entry(0));
+ return await sarafuToken.methods.balanceOf(address).call();
+ }
+}
+
+ +
+ src/app/_services/transaction.service.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(httpClient: HttpClient, authService: AuthService, userService: UserService, loggingService: LoggingService, registryService: RegistryService)
+ |
+ ||||||||||||||||||
+ Defined in src/app/_services/transaction.service.ts:31
+ |
+ ||||||||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + addTransaction + + + + | +|||||||||
+addTransaction(transaction, cacheSize: number)
+ |
+ |||||||||
+ Defined in src/app/_services/transaction.service.ts:89
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + getAccountInfo + + + + | +||||||
+getAccountInfo(account: string)
+ |
+ ||||||
+ Defined in src/app/_services/transaction.service.ts:102
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + getAddressTransactions + + + + | +||||||||||||
+getAddressTransactions(address: string, offset: number, limit: number)
+ |
+ ||||||||||||
+ Defined in src/app/_services/transaction.service.ts:49
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getAllTransactions + + + + | +|||||||||
+getAllTransactions(offset: number, limit: number)
+ |
+ |||||||||
+ Defined in src/app/_services/transaction.service.ts:45
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + resetTransactionsList + + + + | +
+resetTransactionsList()
+ |
+
+ Defined in src/app/_services/transaction.service.ts:97
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + Async + setConversion + + + + | +||||||
+
+ setConversion(conversion, cacheSize)
+ |
+ ||||||
+ Defined in src/app/_services/transaction.service.ts:73
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + Async + setTransaction + + + + | +|||||||||
+
+ setTransaction(transaction, cacheSize: number)
+ |
+ |||||||||
+ Defined in src/app/_services/transaction.service.ts:53
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + Async + transferRequest + + + + | +|||||||||||||||
+
+ transferRequest(tokenAddress: string, senderAddress: string, recipientAddress: string, value: number)
+ |
+ |||||||||||||||
+ Defined in src/app/_services/transaction.service.ts:108
+ |
+ |||||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<any>
+
+
+
+
+ |
+
+ + + + registry + + + | +
+ Type : CICRegistry
+
+ |
+
+ Defined in src/app/_services/transaction.service.ts:31
+ |
+
+ + + + Private + transactionList + + + | +
+ Default value : new BehaviorSubject<any[]>(this.transactions)
+ |
+
+ Defined in src/app/_services/transaction.service.ts:27
+ |
+
+ + + + transactions + + + | +
+ Type : any[]
+
+ |
+
+ Default value : []
+ |
+
+ Defined in src/app/_services/transaction.service.ts:26
+ |
+
+ + + + transactionsSubject + + + | +
+ Default value : this.transactionList.asObservable()
+ |
+
+ Defined in src/app/_services/transaction.service.ts:28
+ |
+
+ + + + userInfo + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_services/transaction.service.ts:29
+ |
+
+ + + + web3 + + + | +
+ Type : Web3
+
+ |
+
+ Defined in src/app/_services/transaction.service.ts:30
+ |
+
import { Injectable } from '@angular/core';
+import {first} from 'rxjs/operators';
+import {BehaviorSubject, Observable} from 'rxjs';
+import {environment} from '@src/environments/environment';
+import {Envelope, User} from 'cic-client-meta';
+import {UserService} from '@app/_services/user.service';
+import { Keccak } from 'sha3';
+import { utils } from 'ethers';
+import {add0x, fromHex, strip0x, toHex} from '@src/assets/js/ethtx/dist/hex';
+import {Tx} from '@src/assets/js/ethtx/dist';
+import {toValue} from '@src/assets/js/ethtx/dist/tx';
+import * as secp256k1 from 'secp256k1';
+import {AuthService} from '@app/_services/auth.service';
+import {defaultAccount} from '@app/_models';
+import {LoggingService} from '@app/_services/logging.service';
+import {HttpClient} from '@angular/common/http';
+import {CICRegistry} from 'cic-client';
+import {RegistryService} from '@app/_services/registry.service';
+import Web3 from 'web3';
+const vCard = require('vcard-parser');
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TransactionService {
+ transactions: any[] = [];
+ private transactionList = new BehaviorSubject<any[]>(this.transactions);
+ transactionsSubject = this.transactionList.asObservable();
+ userInfo: any;
+ web3: Web3;
+ registry: CICRegistry;
+
+ constructor(
+ private httpClient: HttpClient,
+ private authService: AuthService,
+ private userService: UserService,
+ private loggingService: LoggingService,
+ private registryService: RegistryService,
+ ) {
+ this.web3 = this.registryService.getWeb3();
+ this.registry = registryService.getRegistry();
+ this.registry.load();
+ }
+
+ getAllTransactions(offset: number, limit: number): Observable<any> {
+ return this.httpClient.get(`${environment.cicCacheUrl}/tx/${offset}/${limit}`);
+ }
+
+ getAddressTransactions(address: string, offset: number, limit: number): Observable<any> {
+ return this.httpClient.get(`${environment.cicCacheUrl}/tx/${address}/${offset}/${limit}`);
+ }
+
+ async setTransaction(transaction, cacheSize: number): Promise<void> {
+ if (this.transactions.find(cachedTx => cachedTx.tx.txHash === transaction.tx.txHash)) { return; }
+ transaction.value = Number(transaction.value);
+ transaction.type = 'transaction';
+ try {
+ this.userService.getAccountDetailsFromMeta(await User.toKey(transaction.from)).pipe(first()).subscribe((res) => {
+ transaction.sender = this.getAccountInfo(res.body);
+ }, error => {
+ transaction.sender = defaultAccount;
+ });
+ this.userService.getAccountDetailsFromMeta(await User.toKey(transaction.to)).pipe(first()).subscribe((res) => {
+ transaction.recipient = this.getAccountInfo(res.body);
+ }, error => {
+ transaction.recipient = defaultAccount;
+ });
+ } finally {
+ this.addTransaction(transaction, cacheSize);
+ }
+ }
+
+ async setConversion(conversion, cacheSize): Promise<void> {
+ if (this.transactions.find(cachedTx => cachedTx.tx.txHash === conversion.tx.txHash)) { return; }
+ conversion.type = 'conversion';
+ conversion.fromValue = Number(conversion.fromValue);
+ conversion.toValue = Number(conversion.toValue);
+ try {
+ this.userService.getAccountDetailsFromMeta(await User.toKey(conversion.trader)).pipe(first()).subscribe((res) => {
+ conversion.sender = conversion.recipient = this.getAccountInfo(res.body);
+ }, error => {
+ conversion.sender = conversion.recipient = defaultAccount;
+ });
+ } finally {
+ this.addTransaction(conversion, cacheSize);
+ }
+ }
+
+ addTransaction(transaction, cacheSize: number): void {
+ this.transactions.unshift(transaction);
+ if (this.transactions.length > cacheSize) {
+ this.transactions.length = cacheSize;
+ }
+ this.transactionList.next(this.transactions);
+ }
+
+ resetTransactionsList(): void {
+ this.transactions = [];
+ this.transactionList.next(this.transactions);
+ }
+
+ getAccountInfo(account: string): any {
+ let accountInfo = Envelope.fromJSON(JSON.stringify(account)).unwrap().m.data;
+ accountInfo.vcard = vCard.parse(atob(accountInfo.vcard));
+ return accountInfo;
+ }
+
+ async transferRequest(tokenAddress: string, senderAddress: string, recipientAddress: string, value: number): Promise<any> {
+ const transferAuthAddress = await this.registry.getContractAddressByName('TransferAuthorization');
+ const hashFunction = new Keccak(256);
+ hashFunction.update('createRequest(address,address,address,uint256)');
+ const hash = hashFunction.digest();
+ const methodSignature = hash.toString('hex').substring(0, 8);
+ const abiCoder = new utils.AbiCoder();
+ const abi = await abiCoder.encode(['address', 'address', 'address', 'uint256'], [senderAddress, recipientAddress, tokenAddress, value]);
+ const data = fromHex(methodSignature + strip0x(abi));
+ const tx = new Tx(environment.bloxbergChainId);
+ tx.nonce = await this.web3.eth.getTransactionCount(senderAddress);
+ tx.gasPrice = Number(await this.web3.eth.getGasPrice());
+ tx.gasLimit = 8000000;
+ tx.to = fromHex(strip0x(transferAuthAddress));
+ tx.value = toValue(value);
+ tx.data = data;
+ const txMsg = tx.message();
+ const privateKey = this.authService.mutableKeyStore.getPrivateKey();
+ if (!privateKey.isDecrypted()) {
+ const password = window.prompt('password');
+ await privateKey.decrypt(password);
+ }
+ const signatureObject = secp256k1.ecdsaSign(txMsg, privateKey.keyPacket.privateParams.d);
+ const r = signatureObject.signature.slice(0, 32);
+ const s = signatureObject.signature.slice(32);
+ const v = signatureObject.recid;
+ tx.setSignature(r, s, v);
+ const txWire = add0x(toHex(tx.serializeRLP()));
+ const result = await this.web3.eth.sendSignedTransaction(txWire);
+ this.loggingService.sendInfoLevelMessage(`Result: ${result}`);
+ const transaction = await this.web3.eth.getTransaction(result.transactionHash);
+ this.loggingService.sendInfoLevelMessage(`Transaction: ${transaction}`);
+ }
+}
+
+ +
+ src/app/_services/user.service.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(httpClient: HttpClient, loggingService: LoggingService, tokenService: TokenService, registryService: RegistryService, authService: AuthService)
+ |
+ ||||||||||||||||||
+ Defined in src/app/_services/user.service.ts:34
+ |
+ ||||||||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + approveAction + + + + | +||||||
+approveAction(id: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:123
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + Async + changeAccountInfo + + + + | +||||||||||||||||||||||||||||||||||||
+
+ changeAccountInfo(address: string, name: string, phoneNumber: string, age: string, type: string, bio: string, gender: string, businessCategory: string, userLocation: string, location: string, locationType: string)
+ |
+ ||||||||||||||||||||||||||||||||||||
+ Defined in src/app/_services/user.service.ts:65
+ |
+ ||||||||||||||||||||||||||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<any>
+
+
+
+
+ |
+
+ + + + Async + getAccountByAddress + + + + | +||||||||||||
+
+ getAccountByAddress(accountAddress: string, limit: number)
+ |
+ ||||||||||||
+ Defined in src/app/_services/user.service.ts:160
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<Observable<AccountDetails>>
+
+
+
+
+ |
+
+ + + + Async + getAccountByPhone + + + + | +||||||||||||
+
+ getAccountByPhone(phoneNumber: string, limit: number)
+ |
+ ||||||||||||
+ Defined in src/app/_services/user.service.ts:179
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<Observable<AccountDetails>>
+
+
+
+
+ |
+
+ + + + getAccountDetailsFromMeta + + + + | +||||||
+getAccountDetailsFromMeta(userKey: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:131
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getAccountStatus + + + + | +||||||
+getAccountStatus(phone: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:56
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getAccountTypes + + + + | +
+getAccountTypes()
+ |
+
+ Defined in src/app/_services/user.service.ts:207
+ |
+
+
+
+ Returns :
+ Observable<any>
+
+ |
+
+ + + + getActionById + + + + | +||||||
+getActionById(id: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:119
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getActions + + + + | +
+getActions()
+ |
+
+ Defined in src/app/_services/user.service.ts:115
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + getCategories + + + + | +
+getCategories()
+ |
+
+ Defined in src/app/_services/user.service.ts:199
+ |
+
+
+
+ Returns :
+ Observable<any>
+
+ |
+
+ + + + getCategoryByProduct + + + + | +||||||
+getCategoryByProduct(product: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:203
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getGenders + + + + | +
+getGenders()
+ |
+
+ Defined in src/app/_services/user.service.ts:215
+ |
+
+
+
+ Returns :
+ Observable<any>
+
+ |
+
+ + + + getLockedAccounts + + + + | +|||||||||
+getLockedAccounts(offset: number, limit: number)
+ |
+ |||||||||
+ Defined in src/app/_services/user.service.ts:61
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getTransactionTypes + + + + | +
+getTransactionTypes()
+ |
+
+ Defined in src/app/_services/user.service.ts:211
+ |
+
+
+
+ Returns :
+ Observable<any>
+
+ |
+
+ + + + Async + loadAccounts + + + + | +||||||||||||
+
+ loadAccounts(limit: number, offset: number)
+ |
+ ||||||||||||
+ Defined in src/app/_services/user.service.ts:149
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + resetAccountsList + + + + | +
+resetAccountsList()
+ |
+
+ Defined in src/app/_services/user.service.ts:192
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + resetPin + + + + | +||||||
+resetPin(phone: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:51
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + revokeAction + + + + | +||||||
+revokeAction(id: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:127
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + searchAccountByName + + + + | +||||||
+searchAccountByName(name: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:197
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + Async + updateMeta + + + + | +||||||||||||
+
+ updateMeta(syncableAccount: Syncable, accountKey: string, headers: HttpHeaders)
+ |
+ ||||||||||||
+ Defined in src/app/_services/user.service.ts:107
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<any>
+
+
+
+
+ |
+
+ + + + wrap + + + + | +|||||||||
+wrap(syncable: Syncable, signer: Signer)
+ |
+ |||||||||
+ Defined in src/app/_services/user.service.ts:135
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<Envelope>
+
+
+
+
+ |
+
+ + + + accounts + + + | +
+ Type : Array<AccountDetails>
+
+ |
+
+ Default value : []
+ |
+
+ Defined in src/app/_services/user.service.ts:28
+ |
+
+ + + + Private + accountsList + + + | +
+ Type : BehaviorSubject<Array<AccountDetails>>
+
+ |
+
+ Default value : new BehaviorSubject<Array<AccountDetails>>(this.accounts)
+ |
+
+ Defined in src/app/_services/user.service.ts:29
+ |
+
+ + + + accountsSubject + + + | +
+ Type : Observable<Array<AccountDetails>>
+
+ |
+
+ Default value : this.accountsList.asObservable()
+ |
+
+ Defined in src/app/_services/user.service.ts:30
+ |
+
+ + + + actions + + + | +
+ Type : Array<any>
+
+ |
+
+ Default value : []
+ |
+
+ Defined in src/app/_services/user.service.ts:32
+ |
+
+ + + + Private + actionsList + + + | +
+ Type : BehaviorSubject<any>
+
+ |
+
+ Default value : new BehaviorSubject<any>(this.actions)
+ |
+
+ Defined in src/app/_services/user.service.ts:33
+ |
+
+ + + + actionsSubject + + + | +
+ Type : Observable<Array<any>>
+
+ |
+
+ Default value : this.actionsList.asObservable()
+ |
+
+ Defined in src/app/_services/user.service.ts:34
+ |
+
+ + + + headers + + + | +
+ Type : HttpHeaders
+
+ |
+
+ Default value : new HttpHeaders({'x-cic-automerge': 'client'})
+ |
+
+ Defined in src/app/_services/user.service.ts:23
+ |
+
+ + + + keystore + + + | +
+ Type : MutableKeyStore
+
+ |
+
+ Defined in src/app/_services/user.service.ts:24
+ |
+
+ + + + registry + + + | +
+ Type : CICRegistry
+
+ |
+
+ Defined in src/app/_services/user.service.ts:26
+ |
+
+ + + + signer + + + | +
+ Type : Signer
+
+ |
+
+ Defined in src/app/_services/user.service.ts:25
+ |
+
import {Injectable} from '@angular/core';
+import {BehaviorSubject, Observable, Subject} from 'rxjs';
+import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
+import {environment} from '@src/environments/environment';
+import {first} from 'rxjs/operators';
+import {ArgPair, Envelope, Phone, Syncable, User} from 'cic-client-meta';
+import {AccountDetails} from '@app/_models';
+import {LoggingService} from '@app/_services/logging.service';
+import {TokenService} from '@app/_services/token.service';
+import {AccountIndex} from '@app/_eth';
+import {MutableKeyStore, PGPSigner, Signer} from '@app/_pgp';
+import {RegistryService} from '@app/_services/registry.service';
+import {CICRegistry} from 'cic-client';
+import {AuthService} from '@app/_services/auth.service';
+import {personValidation, vcardValidation} from '@app/_helpers';
+import {add0x} from '@src/assets/js/ethtx/dist/hex';
+const vCard = require('vcard-parser');
+
+@Injectable({
+ providedIn: 'root'
+})
+export class UserService {
+ headers: HttpHeaders = new HttpHeaders({'x-cic-automerge': 'client'});
+ keystore: MutableKeyStore;
+ signer: Signer;
+ registry: CICRegistry;
+
+ accounts: Array<AccountDetails> = [];
+ private accountsList: BehaviorSubject<Array<AccountDetails>> = new BehaviorSubject<Array<AccountDetails>>(this.accounts);
+ accountsSubject: Observable<Array<AccountDetails>> = this.accountsList.asObservable();
+
+ actions: Array<any> = [];
+ private actionsList: BehaviorSubject<any> = new BehaviorSubject<any>(this.actions);
+ actionsSubject: Observable<Array<any>> = this.actionsList.asObservable();
+
+ constructor(
+ private httpClient: HttpClient,
+ private loggingService: LoggingService,
+ private tokenService: TokenService,
+ private registryService: RegistryService,
+ private authService: AuthService,
+ ) {
+ this.authService.init().then(() => {
+ this.keystore = authService.mutableKeyStore;
+ this.signer = new PGPSigner(this.keystore);
+ });
+ this.registry = registryService.getRegistry();
+ this.registry.load();
+ }
+
+ resetPin(phone: string): Observable<any> {
+ const params: HttpParams = new HttpParams().set('phoneNumber', phone);
+ return this.httpClient.get(`${environment.cicUssdUrl}/pin`, {params});
+ }
+
+ getAccountStatus(phone: string): Observable<any> {
+ const params: HttpParams = new HttpParams().set('phoneNumber', phone);
+ return this.httpClient.get(`${environment.cicUssdUrl}/pin`, {params});
+ }
+
+ getLockedAccounts(offset: number, limit: number): Observable<any> {
+ return this.httpClient.get(`${environment.cicUssdUrl}/accounts/locked/${offset}/${limit}`);
+ }
+
+ async changeAccountInfo(address: string, name: string, phoneNumber: string, age: string, type: string, bio: string, gender: string,
+ businessCategory: string, userLocation: string, location: string, locationType: string
+ ): Promise<any> {
+ const accountInfo: any = {
+ vcard: {
+ fn: [{}],
+ n: [{}],
+ tel: [{}]
+ },
+ location: {}
+ };
+ accountInfo.vcard.fn[0].value = name;
+ accountInfo.vcard.n[0].value = name.split(' ');
+ accountInfo.vcard.tel[0].value = phoneNumber;
+ accountInfo.products = [bio];
+ accountInfo.gender = gender;
+ accountInfo.age = age;
+ accountInfo.type = type;
+ accountInfo.category = businessCategory;
+ accountInfo.location.area = location;
+ accountInfo.location.area_name = userLocation;
+ accountInfo.location.area_type = locationType;
+ await vcardValidation(accountInfo.vcard);
+ accountInfo.vcard = btoa(vCard.generate(accountInfo.vcard));
+ const accountKey: string = await User.toKey(address);
+ this.getAccountDetailsFromMeta(accountKey).pipe(first()).subscribe(async res => {
+ const syncableAccount: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
+ const update: Array<ArgPair> = [];
+ for (const prop in accountInfo) {
+ update.push(new ArgPair(prop, accountInfo[prop]));
+ }
+ syncableAccount.update(update, 'client-branch');
+ await personValidation(syncableAccount.m.data);
+ await this.updateMeta(syncableAccount, accountKey, this.headers);
+ }, async error => {
+ this.loggingService.sendErrorLevelMessage('Can\'t find account info in meta service', this, {error});
+ const syncableAccount: Syncable = new Syncable(accountKey, accountInfo);
+ await this.updateMeta(syncableAccount, accountKey, this.headers);
+ });
+ return accountKey;
+ }
+
+ async updateMeta(syncableAccount: Syncable, accountKey: string, headers: HttpHeaders): Promise<any> {
+ const envelope: Envelope = await this.wrap(syncableAccount , this.signer);
+ const reqBody: string = envelope.toJSON();
+ this.httpClient.put(`${environment.cicMetaUrl}/${accountKey}`, reqBody , { headers }).pipe(first()).subscribe(res => {
+ this.loggingService.sendInfoLevelMessage(`Response: ${res}`);
+ });
+ }
+
+ getActions(): void {
+ this.httpClient.get(`${environment.cicCacheUrl}/actions`).pipe(first()).subscribe(res => this.actionsList.next(res));
+ }
+
+ getActionById(id: string): Observable<any> {
+ return this.httpClient.get(`${environment.cicCacheUrl}/actions/${id}`);
+ }
+
+ approveAction(id: string): Observable<any> {
+ return this.httpClient.post(`${environment.cicCacheUrl}/actions/${id}`, { approval: true });
+ }
+
+ revokeAction(id: string): Observable<any> {
+ return this.httpClient.post(`${environment.cicCacheUrl}/actions/${id}`, { approval: false });
+ }
+
+ getAccountDetailsFromMeta(userKey: string): Observable<any> {
+ return this.httpClient.get(`${environment.cicMetaUrl}/${userKey}`, { headers: this.headers });
+ }
+
+ wrap(syncable: Syncable, signer: Signer): Promise<Envelope> {
+ return new Promise<Envelope>(async (resolve, reject) => {
+ syncable.setSigner(signer);
+ syncable.onwrap = async (env) => {
+ if (env === undefined) {
+ reject();
+ return;
+ }
+ resolve(env);
+ };
+ await syncable.sign();
+ });
+ }
+
+ async loadAccounts(limit: number = 100, offset: number = 0): Promise<void> {
+ this.resetAccountsList();
+ const accountIndexAddress: string = await this.registry.getContractAddressByName('AccountRegistry');
+ const accountIndexQuery = new AccountIndex(accountIndexAddress);
+ const accountAddresses: Array<string> = await accountIndexQuery.last(await accountIndexQuery.totalAccounts());
+ this.loggingService.sendInfoLevelMessage(accountAddresses);
+ for (const accountAddress of accountAddresses.slice(offset, offset + limit)) {
+ await this.getAccountByAddress(accountAddress, limit);
+ }
+ }
+
+ async getAccountByAddress(accountAddress: string, limit: number = 100): Promise<Observable<AccountDetails>> {
+ let accountSubject: Subject<any> = new Subject<any>();
+ this.getAccountDetailsFromMeta(await User.toKey(add0x(accountAddress))).pipe(first()).subscribe(async res => {
+ const account: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
+ const accountInfo = account.m.data;
+ await personValidation(accountInfo);
+ accountInfo.balance = await this.tokenService.getTokenBalance(accountInfo.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]);
+ accountInfo.vcard = vCard.parse(atob(accountInfo.vcard));
+ await vcardValidation(accountInfo.vcard);
+ this.accounts.unshift(accountInfo);
+ if (this.accounts.length > limit) {
+ this.accounts.length = limit;
+ }
+ this.accountsList.next(this.accounts);
+ accountSubject.next(accountInfo);
+ });
+ return accountSubject.asObservable();
+ }
+
+ async getAccountByPhone(phoneNumber: string, limit: number = 100): Promise<Observable<AccountDetails>> {
+ let accountSubject: Subject<any> = new Subject<any>();
+ this.getAccountDetailsFromMeta(await Phone.toKey(phoneNumber)).pipe(first()).subscribe(async res => {
+ const response: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
+ const address: string = response.m.data;
+ const account: Observable<AccountDetails> = await this.getAccountByAddress(address, limit);
+ account.subscribe(result => {
+ accountSubject.next(result);
+ });
+ });
+ return accountSubject.asObservable();
+ }
+
+ resetAccountsList(): void {
+ this.accounts = [];
+ this.accountsList.next(this.accounts);
+ }
+
+ searchAccountByName(name: string): any { return; }
+
+ getCategories(): Observable<any> {
+ return this.httpClient.get(`${environment.cicMetaUrl}/categories`);
+ }
+
+ getCategoryByProduct(product: string): Observable<any> {
+ return this.httpClient.get(`${environment.cicMetaUrl}/categories/${product.toLowerCase()}`);
+ }
+
+ getAccountTypes(): Observable<any> {
+ return this.httpClient.get(`${environment.cicMetaUrl}/accounttypes`);
+ }
+
+ getTransactionTypes(): Observable<any> {
+ return this.httpClient.get(`${environment.cicMetaUrl}/transactiontypes`);
+ }
+
+ getGenders(): Observable<any> {
+ return this.httpClient.get(`${environment.cicMetaUrl}/genders`);
+ }
+}
+
+ +
+ src/app/_interceptors/error.interceptor.ts
+
+ Methods+ |
+
+
|
+
+constructor(errorDialogService: ErrorDialogService, loggingService: LoggingService, router: Router)
+ |
+ ||||||||||||
+ + | +||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + intercept + + + + | +|||||||||
+intercept(request: HttpRequest
+ |
+ |||||||||
+ + | +|||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<HttpEvent<unknown>>
+
+
+
+
+ |
+
import {Injectable} from '@angular/core';
+import {
+ HttpRequest,
+ HttpHandler,
+ HttpEvent,
+ HttpInterceptor, HttpErrorResponse
+} from '@angular/common/http';
+import {Observable, throwError} from 'rxjs';
+import {catchError} from 'rxjs/operators';
+import {ErrorDialogService, LoggingService} from '@app/_services';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class ErrorInterceptor implements HttpInterceptor {
+
+ constructor(
+ private errorDialogService: ErrorDialogService,
+ private loggingService: LoggingService,
+ private router: Router
+ ) {}
+
+ intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
+ return next.handle(request).pipe(
+ catchError((err: HttpErrorResponse) => {
+ let errorMessage: string;
+ if (err.error instanceof ErrorEvent) {
+ // A client-side or network error occurred. Handle it accordingly.
+ errorMessage = `An error occurred: ${err.error.message}`;
+ } else {
+ // The backend returned an unsuccessful response code.
+ // The response body may contain clues as to what went wrong.
+ errorMessage = `Backend returned code ${err.status}, body was: ${JSON.stringify(err.error)}`;
+ }
+ this.loggingService.sendErrorLevelMessage(errorMessage, this, {error: err});
+ switch (err.status) {
+ case 401: // unauthorized
+ this.router.navigateByUrl('/auth').then();
+ break;
+ case 403: // forbidden
+ alert('Access to resource is not allowed!');
+ break;
+ }
+ // Return an observable with a user-facing error message.
+ return throwError(err);
+ })
+ );
+ }
+}
+
+ +
+ src/app/_interceptors/http-config.interceptor.ts
+
+ Methods+ |
+
+
|
+
+constructor()
+ |
+
+ + | +
+ + + + intercept + + + + | +|||||||||
+intercept(request: HttpRequest
+ |
+ |||||||||
+ + | +|||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<HttpEvent<unknown>>
+
+
+
+
+ |
+
import { Injectable } from '@angular/core';
+import {
+ HttpRequest,
+ HttpHandler,
+ HttpEvent,
+ HttpInterceptor
+} from '@angular/common/http';
+import { Observable } from 'rxjs';
+
+@Injectable()
+export class HttpConfigInterceptor implements HttpInterceptor {
+
+ constructor() {}
+
+ intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
+ // const token: string = sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN'));
+
+ // if (token) {
+ // request = request.clone({headers: request.headers.set('Authorization', 'Bearer ' + token)});
+ // }
+
+ return next.handle(request);
+ }
+}
+
+ +
+ src/app/_interceptors/logging.interceptor.ts
+
+ Methods+ |
+
+
|
+
+constructor(loggingService: LoggingService)
+ |
+ ||||||
+ + | +||||||
+
+ Parameters :
+
+
|
+
+ + + + intercept + + + + | +|||||||||
+intercept(request: HttpRequest
+ |
+ |||||||||
+ + | +|||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<HttpEvent<unknown>>
+
+
+
+
+ |
+
import { Injectable } from '@angular/core';
+import {
+ HttpRequest,
+ HttpHandler,
+ HttpEvent,
+ HttpInterceptor,
+ HttpResponse
+} from '@angular/common/http';
+import {Observable} from 'rxjs';
+import {LoggingService} from '@app/_services/logging.service';
+import {finalize, tap} from 'rxjs/operators';
+
+@Injectable()
+export class LoggingInterceptor implements HttpInterceptor {
+
+ constructor(
+ private loggingService: LoggingService
+ ) {}
+
+ intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
+ return next.handle(request);
+ // this.loggingService.sendInfoLevelMessage(request);
+ // const startTime: number = Date.now();
+ // let status: string;
+ //
+ // return next.handle(request).pipe(tap(event => {
+ // status = '';
+ // if (event instanceof HttpResponse) {
+ // status = 'succeeded';
+ // }
+ // }, error => status = 'failed'),
+ // finalize(() => {
+ // const elapsedTime: number = Date.now() - startTime;
+ // const message: string = `${request.method} request for ${request.urlWithParams} ${status} in ${elapsedTime} ms`;
+ // this.loggingService.sendInfoLevelMessage(message);
+ // }));
+ }
+}
+
+ +
+ src/app/_helpers/mock-backend.ts
+
+ Methods+ |
+
+
|
+
+ + + + intercept + + + + | +|||||||||
+intercept(request: HttpRequest
+ |
+ |||||||||
+ Defined in src/app/_helpers/mock-backend.ts:240
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<HttpEvent<any>>
+
+
+
+
+ |
+
import {HTTP_INTERCEPTORS, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse} from '@angular/common/http';
+import {Injectable} from '@angular/core';
+import {Observable, of, throwError} from 'rxjs';
+import {delay, dematerialize, materialize, mergeMap} from 'rxjs/operators';
+import {Action, AreaName, AreaType, Category, Token} from '@app/_models';
+
+const actions: Array<Action> = [
+ { id: 1, user: 'Tom', role: 'enroller', action: 'Disburse RSV 100', approval: false },
+ { id: 2, user: 'Christine', role: 'admin', action: 'Change user phone number', approval: true },
+ { id: 3, user: 'Will', role: 'superadmin', action: 'Reclaim RSV 1000', approval: true },
+ { id: 4, user: 'Vivian', role: 'enroller', action: 'Complete user profile', approval: true },
+ { id: 5, user: 'Jack', role: 'enroller', action: 'Reclaim RSV 200', approval: false },
+ { id: 6, user: 'Patience', role: 'enroller', action: 'Change user information', approval: false }
+];
+
+const tokens: Array<Token> = [
+ {
+ name: 'Giftable Reserve', symbol: 'GRZ', address: '0xa686005CE37Dce7738436256982C3903f2E4ea8E', supply: '1000000001000000000000000000',
+ decimals: '18', reserves: {}
+ },
+ {
+ name: 'Demo Token', symbol: 'DEMO', address: '0xc80D6aFF8194114c52AEcD84c9f15fd5c8abb187', supply: '99999999999999998976',
+ decimals: '18', reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '99999999999999998976'}},
+ reserveRatio: '1000000', owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
+ },
+ {
+ name: 'Foo Token', symbol: 'FOO', address: '0x9ceD86089f7aBB5A97B40eb0E7521e7aa308d354', supply: '1000000000000000001014',
+ decimals: '18', reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '1000000000000000001014'}},
+ reserveRatio: '1000000', owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
+ },
+ {
+ name: 'testb', symbol: 'tstb', address: '0xC63cFA91A3BFf41cE31Ff436f67D3ACBC977DB95', supply: '99000', decimals: '18',
+ reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '99000'}}, reserveRatio: '1000000',
+ owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
+ },
+ {
+ name: 'testa', symbol: 'tsta', address: '0x8fA4101ef19D0a078239d035659e92b278bD083C', supply: '9981', decimals: '18',
+ reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '9981'}}, reserveRatio: '1000000',
+ owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
+ },
+ {
+ name: 'testc', symbol: 'tstc', address: '0x4A6fA6bc3BfE4C9661bC692D9798425350C9e3D4', supply: '100990', decimals: '18',
+ reserves: {'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {weight: '1000000', balance: '100990'}}, reserveRatio: '1000000',
+ owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a'
+ }
+];
+
+const categories: Array<Category> = [
+ {
+ name: 'system',
+ products: ['system', 'office main', 'office main phone']
+ },
+ {
+ name: 'education',
+ products: ['book', 'coach', 'teacher', 'sch', 'school', 'pry', 'education', 'student', 'mwalimu', 'maalim', 'consultant', 'consult',
+ 'college', 'university', 'lecturer', 'primary', 'secondary', 'daycare', 'babycare', 'baby care', 'elim', 'eimu', 'nursery',
+ 'red cross', 'volunteer', 'instructor', 'journalist', 'lesson', 'academy', 'headmistress', 'headteacher', 'cyber', 'researcher',
+ 'professor', 'demo', 'expert', 'tution', 'tuition', 'children', 'headmaster', 'educator', 'Marital counsellor', 'counsellor',
+ 'trainer', 'vijana', 'youth', 'intern', 'redcross', 'KRCS', 'danish', 'science', 'data', 'facilitator', 'vitabu', 'kitabu']
+ },
+ {
+ name: 'faith',
+ products: ['pastor', 'imam', 'madrasa', 'religous', 'religious', 'ustadh', 'ustadhi', 'Marital counsellor', 'counsellor', 'church',
+ 'kanisa', 'mksiti', 'donor']
+ },
+ {
+ name: 'government',
+ products: ['elder', 'chief', 'police', 'government', 'country', 'county', 'soldier', 'village admin', 'ward', 'leader', 'kra',
+ 'mailman', 'immagration', 'immigration']
+ },
+ {
+ name: 'environment',
+ products: ['conservation', 'toilet', 'choo', 'garbage', 'fagio', 'waste', 'tree', 'taka', 'scrap', 'cleaning', 'gardener', 'rubbish',
+ 'usafi', 'mazingira', 'miti', 'trash', 'cleaner', 'plastic', 'collection', 'seedling', 'seedlings', 'recycling']
+ },
+ {
+ name: 'farming',
+ products: ['farm', 'farmer', 'farming', 'mkulima', 'kulima', 'ukulima', 'wakulima', 'jembe', 'shamba']
+ },
+ {
+ name: 'labour',
+ products: ['artist', 'agent', 'guard', 'askari', 'accountant', 'baker', 'beadwork', 'beauty', 'business', 'barber', 'casual',
+ 'electrian', 'caretaker', 'car wash', 'capenter', 'construction', 'chef', 'catering', 'cobler', 'cobbler', 'carwash', 'dhobi',
+ 'landlord', 'design', 'carpenter', 'fundi', 'hawking', 'hawker', 'househelp', 'hsehelp', 'house help', 'help', 'housegirl', 'kushona',
+ 'juakali', 'jualikali', 'juacali', 'jua kali', 'shepherd', 'makuti', 'kujenga', 'kinyozi', 'kazi', 'knitting', 'kufua', 'fua',
+ 'hustler', 'biashara', 'labour', 'labor', 'laundry', 'repair', 'hair', 'posho', 'mill', 'mtambo', 'uvuvi', 'engineer', 'manager',
+ 'tailor', 'nguo', 'mason', 'mtumba', 'garage', 'mechanic', 'mjenzi', 'mfugaji', 'painter', 'receptionist', 'printing', 'programming',
+ 'plumb', 'charging', 'salon', 'mpishi', 'msusi', 'mgema', 'footballer', 'photocopy', 'peddler', 'staff', 'sales', 'service', 'saloon',
+ 'seremala', 'security', 'insurance', 'secretary', 'shoe', 'shepard', 'shephard', 'tout', 'tv', 'mvuvi', 'mawe', 'majani', 'maembe',
+ 'freelance', 'mjengo', 'electronics', 'photographer', 'programmer', 'electrician', 'washing', 'bricks', 'welder', 'welding',
+ 'working', 'worker', 'watchman', 'waiter', 'waitress', 'viatu', 'yoga', 'guitarist', 'house', 'artisan', 'musician', 'trade',
+ 'makonge', 'ujenzi', 'vendor', 'watchlady', 'marketing', 'beautician', 'photo', 'metal work', 'supplier', 'law firm', 'brewer']
+ },
+ {
+ name: 'food',
+ products: ['avocado', 'bhajia', 'bajia', 'mbonga', 'bofu', 'beans', 'biscuits', 'biringanya', 'banana', 'bananas', 'crisps', 'chakula',
+ 'coconut', 'chapati', 'cereal', 'chipo', 'chapo', 'chai', 'chips', 'cassava', 'cake', 'cereals', 'cook', 'corn', 'coffee', 'chicken',
+ 'dagaa', 'donut', 'dough', 'groundnuts', 'hotel', 'holel', 'hoteli', 'butcher', 'butchery', 'fruit', 'food', 'fruits', 'fish',
+ 'githeri', 'grocery', 'grocer', 'pojo', 'papa', 'goats', 'mabenda', 'mbenda', 'poultry', 'soda', 'peanuts', 'potatoes', 'samosa',
+ 'soko', 'samaki', 'tomato', 'tomatoes', 'mchele', 'matunda', 'mango', 'melon', 'mellon', 'nyanya', 'nyama', 'omena', 'umena', 'ndizi',
+ 'njugu', 'kamba kamba', 'khaimati', 'kaimati', 'kunde', 'kuku', 'kahawa', 'keki', 'muguka', 'miraa', 'milk', 'choma', 'maziwa',
+ 'mboga', 'mbog', 'busaa', 'chumvi', 'cabbages', 'mabuyu', 'machungwa', 'mbuzi', 'mnazi', 'mchicha', 'ngombe', 'ngano', 'nazi',
+ 'oranges', 'peanuts', 'mkate', 'bread', 'mikate', 'vitungu', 'sausages', 'maize', 'mbata', 'mchuzi', 'mchuuzi', 'mandazi', 'mbaazi',
+ 'mahindi', 'maandazi', 'mogoka', 'meat', 'mhogo', 'mihogo', 'muhogo', 'maharagwe', 'miwa', 'mahamri', 'mitumba', 'simsim', 'porridge',
+ 'pilau', 'vegetable', 'egg', 'mayai', 'mifugo', 'unga', 'good', 'sima', 'sweet', 'sweats', 'sambusa', 'snacks', 'sugar', 'suger',
+ 'ugoro', 'sukari', 'soup', 'spinach', 'smokie', 'smokies', 'sukuma', 'tea', 'uji', 'ugali', 'uchuzi', 'uchuuzi', 'viazi', 'yoghurt',
+ 'yogurt', 'wine', 'marondo', 'maandzi', 'matoke', 'omeno', 'onions', 'nzugu', 'korosho', 'barafu', 'juice']
+ },
+ {
+ name: 'water',
+ products: ['maji', 'water']
+ },
+ {
+ name: 'health',
+ products: ['agrovet', 'dispensary', 'barakoa', 'chemist', 'Chemicals', 'chv', 'doctor', 'daktari', 'dawa', 'hospital', 'herbalist',
+ 'mganga', 'sabuni', 'soap', 'nurse', 'heath', 'community health worker', 'clinic', 'clinical', 'mask', 'medicine', 'lab technician',
+ 'pharmacy', 'cosmetics', 'veterinary', 'vet', 'sickly', 'emergency response', 'emergency']
+ },
+ {
+ name: 'savings',
+ products: ['chama', 'group', 'savings', 'loan', 'silc', 'vsla', 'credit', 'finance']
+ },
+ {
+ name: 'shop',
+ products: ['bag', 'bead', 'belt', 'bedding', 'jik', 'bed', 'cement', 'botique', 'boutique', 'lines', 'kibanda', 'kiosk', 'spareparts',
+ 'candy', 'cloth', 'electricals', 'mutumba', 'cafe', 'leso', 'lesso', 'duka', 'spare parts', 'socks', 'malimali', 'mitungi',
+ 'mali mali', 'hardware', 'detergent', 'detergents', 'dera', 'retail', 'kamba', 'pombe', 'pampers', 'pool', 'phone', 'simu', 'mangwe',
+ 'mikeka', 'movie', 'shop', 'acces', 'mchanga', 'uto', 'airtime', 'matress', 'mattress', 'mattresses', 'mpsea', 'mpesa', 'shirt',
+ 'wholesaler', 'perfume', 'playstation', 'tissue', 'vikapu', 'uniform', 'flowers', 'vitenge', 'utencils', 'utensils', 'station',
+ 'jewel', 'pool table', 'club', 'pub', 'bar', 'furniture', 'm-pesa', 'vyombo']
+ },
+ {
+ name: 'transport',
+ products: ['kebeba', 'beba', 'bebabeba', 'bike', 'bicycle', 'matatu', 'boda', 'bodaboda', 'cart', 'carrier', 'tour', 'travel', 'driver',
+ 'dereva', 'tout', 'conductor', 'kubeba', 'tuktuk', 'taxi', 'piki', 'pikipiki', 'manamba', 'trasportion', 'mkokoteni', 'mover',
+ 'motorist', 'motorbike', 'transport', 'transpoter', 'gari', 'magari', 'makanga', 'car']
+ },
+ {
+ name: 'fuel/energy',
+ products: ['timber', 'timberyard', 'biogas', 'charcol', 'charcoal', 'kuni', 'mbao', 'fuel', 'makaa', 'mafuta', 'moto', 'solar', 'stima',
+ 'fire', 'firewood', 'wood', 'oil', 'taa', 'gas', 'paraffin', 'parrafin', 'parafin', 'petrol', 'petro', 'kerosine', 'kerosene',
+ 'diesel']
+ },
+ {
+ name: 'other',
+ products: ['other', 'none', 'unknown', 'none']
+ }
+];
+
+const areaNames: Array<AreaName> = [
+ {
+ name: 'Mukuru Nairobi',
+ locations: ['kayaba', 'kayba', 'kambi', 'mukuru', 'masai', 'hazina', 'south', 'tetra', 'tetrapak', 'ruben', 'rueben', 'kingston',
+ 'korokocho', 'kingstone', 'kamongo', 'lungalunga', 'sinai', 'sigei', 'lungu', 'lunga lunga', 'owino road', 'seigei']
+ },
+ {
+ name: 'Kinango Kwale',
+ locations: ['amani', 'bofu', 'chibuga', 'chikomani', 'chilongoni', 'chigojoni', 'chinguluni', 'chigato', 'chigale', 'chikole',
+ 'chilongoni', 'chilumani', 'chigojoni', 'chikomani', 'chizini', 'chikomeni', 'chidzuvini', 'chidzivuni', 'chikuyu', 'chizingo',
+ 'doti', 'dzugwe', 'dzivani', 'dzovuni', 'hanje', 'kasemeni', 'katundani', 'kibandaogo', 'kibandaongo', 'kwale', 'kinango',
+ 'kidzuvini', 'kalalani', 'kafuduni', 'kaloleni', 'kilibole', 'lutsangani', 'peku', 'gona', 'guro', 'gandini', 'mkanyeni', 'myenzeni',
+ 'miyenzeni', 'miatsiani', 'mienzeni', 'mnyenzeni', 'minyenzeni', 'miyani', 'mioleni', 'makuluni', 'mariakani', 'makobeni', 'madewani',
+ 'mwangaraba', 'mwashanga', 'miloeni', 'mabesheni', 'mazeras', 'mazera', 'mlola', 'muugano', 'mulunguni', 'mabesheni', 'miatsani',
+ 'miatsiani', 'mwache', 'mwangani', 'mwehavikonje', 'miguneni', 'nzora', 'nzovuni', 'vikinduni', 'vikolani', 'vitangani', 'viogato',
+ 'vyogato', 'vistangani', 'yapha', 'yava', 'yowani', 'ziwani', 'majengo', 'matuga', 'vigungani', 'vidziweni', 'vinyunduni', 'ukunda',
+ 'kokotoni', 'mikindani']
+ },
+ {
+ name: 'Misc Nairobi',
+ locations: ['nairobi', 'west', 'lindi', 'kibera', 'kibira', 'kibra', 'makina', 'soweto', 'olympic', 'kangemi', 'ruiru', 'congo',
+ 'kawangware', 'kwangware', 'donholm', 'dagoreti', 'dandora', 'kabete', 'sinai', 'donhom', 'donholm', 'huruma', 'kitengela',
+ 'makadara', ',mlolongo', 'kenyatta', 'mlolongo', 'tassia', 'tasia', 'gatina', '56', 'industrial', 'kariobangi', 'kasarani', 'kayole',
+ 'mathare', 'pipe', 'juja', 'uchumi', 'jogoo', 'umoja', 'thika', 'kikuyu', 'stadium', 'buru buru', 'ngong', 'starehe', 'mwiki',
+ 'fuata', 'kware', 'kabiro', 'embakassi', 'embakasi', 'kmoja', 'east', 'githurai', 'landi', 'langata', 'limuru', 'mathere',
+ 'dagoretti', 'kirembe', 'muugano', 'mwiki', 'toi market']
+ },
+ {
+ name: 'Misc Mombasa',
+ locations: ['mombasa', 'likoni', 'bangla', 'bangladesh', 'kizingo', 'old town', 'makupa', 'mvita', 'ngombeni', 'ngómbeni', 'ombeni',
+ 'magongo', 'miritini', 'changamwe', 'jomvu', 'ohuru', 'tudor', 'diani']
+ },
+ {
+ name: 'Kisauni',
+ locations: ['bamburi', 'kisauni', 'mworoni', 'nyali', 'shanzu', 'bombolulu', 'mtopanga', 'mjambere', 'majaoni', 'manyani', 'magogoni',
+ 'junda', 'mwakirunge', 'mshomoroni']
+ },
+ {
+ name: 'Kilifi',
+ locations: ['kilfi', 'kilifi', 'mtwapa', 'takaungu', 'makongeni', 'mnarani', 'mnarani', 'office', 'g.e', 'ge', 'raibai', 'ribe']
+ },
+ {
+ name: 'Kakuma',
+ locations: ['kakuma']
+ },
+ {
+ name: 'Kitui',
+ locations: ['kitui', 'mwingi']
+ },
+ {
+ name: 'Nyanza',
+ locations: ['busia', 'nyalgunga', 'mbita', 'siaya', 'kisumu', 'nyalenda', 'hawinga', 'rangala', 'uyoma', 'mumias', 'homabay', 'homaboy',
+ 'migori', 'kusumu']
+ },
+ {
+ name: 'Misc Rural Counties',
+ locations: ['makueni', 'meru', 'kisii', 'bomet', 'machakos', 'bungoma', 'eldoret', 'kakamega', 'kericho', 'kajiado', 'nandi', 'nyeri',
+ 'wote', 'kiambu', 'mwea', 'nakuru', 'narok']
+ },
+ {
+ name: 'other',
+ locations: ['other', 'none', 'unknown']
+ }
+];
+
+const areaTypes: Array<AreaType> = [
+ {
+ name: 'urban',
+ area: ['urban', 'nairobi', 'mombasa']
+ },
+ {
+ name: 'rural',
+ area: ['rural', 'kakuma', 'kwale', 'kinango', 'kitui', 'nyanza']
+ },
+ {
+ name: 'periurban',
+ area: ['kilifi', 'periurban']
+ },
+ {
+ name: 'other',
+ area: ['other']
+ }
+];
+
+const accountTypes: Array<string> = ['user', 'cashier', 'vendor', 'tokenagent', 'group'];
+const transactionTypes: Array<string> = ['transactions', 'conversions', 'disbursements', 'rewards', 'reclamation'];
+const genders: Array<string> = ['male', 'female', 'other'];
+
+@Injectable()
+export class MockBackendInterceptor implements HttpInterceptor {
+ intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
+ const { url, method, headers, body } = request;
+
+ // wrap in delayed observable to simulate server api call\
+ // call materialize and dematerialize to ensure delay even is thrown
+ return of(null)
+ .pipe(mergeMap(handleRoute))
+ .pipe(materialize())
+ .pipe(delay(500))
+ .pipe(dematerialize());
+
+ function handleRoute(): Observable<any> {
+ switch (true) {
+ case url.endsWith('/actions') && method === 'GET':
+ return getActions();
+ case url.match(/\/actions\/\d+$/) && method === 'GET':
+ return getActionById();
+ case url.match(/\/actions\/\d+$/) && method === 'POST':
+ return approveAction();
+ case url.endsWith('/tokens') && method === 'GET':
+ return getTokens();
+ case url.match(/\/tokens\/\w+$/) && method === 'GET':
+ return getTokenBySymbol();
+ case url.endsWith('/categories') && method === 'GET':
+ return getCategories();
+ case url.match(/\/categories\/\w+$/) && method === 'GET':
+ return getCategoryByProduct();
+ case url.endsWith('/areanames') && method === 'GET':
+ return getAreaNames();
+ case url.match(/\/areanames\/\w+$/) && method === 'GET':
+ return getAreaNameByLocation();
+ case url.endsWith('/areatypes') && method === 'GET':
+ return getAreaTypes();
+ case url.match(/\/areatypes\/\w+$/) && method === 'GET':
+ return getAreaTypeByArea();
+ case url.endsWith('/accounttypes') && method === 'GET':
+ return getAccountTypes();
+ case url.endsWith('/transactiontypes') && method === 'GET':
+ return getTransactionTypes();
+ case url.endsWith('/genders') && method === 'GET':
+ return getGenders();
+ default:
+ // pass through any requests not handled above
+ return next.handle(request);
+ }
+ }
+
+ // route functions
+
+ function getActions(): Observable<HttpResponse<any>> {
+ return ok(actions);
+ }
+
+ function getActionById(): Observable<HttpResponse<any>> {
+ const queriedAction: Action = actions.find(action => action.id === idFromUrl());
+ return ok(queriedAction);
+ }
+
+ function approveAction(): Observable<HttpResponse<any>> {
+ const queriedAction: Action = actions.find(action => action.id === idFromUrl());
+ queriedAction.approval = body.approval;
+ const message: string = `Action approval status set to ${body.approval} successfully!`;
+ return ok(message);
+ }
+
+ function getTokens(): Observable<HttpResponse<any>> {
+ return ok(tokens);
+ }
+
+ function getTokenBySymbol(): Observable<HttpResponse<any>> {
+ const queriedToken: Token = tokens.find(token => token.symbol === stringFromUrl());
+ return ok(queriedToken);
+ }
+
+ function getCategories(): Observable<HttpResponse<any>> {
+ const categoryList: Array<string> = categories.map(category => category.name);
+ return ok(categoryList);
+ }
+
+ function getCategoryByProduct(): Observable<HttpResponse<any>> {
+ const queriedCategory: Category = categories.find(category => category.products.includes(stringFromUrl()));
+ return ok(queriedCategory.name);
+ }
+
+ function getAreaNames(): Observable<HttpResponse<any>> {
+ const areaNameList: Array<string> = areaNames.map(areaName => areaName.name);
+ return ok(areaNameList);
+ }
+
+ function getAreaNameByLocation(): Observable<HttpResponse<any>> {
+ const queriedAreaName: AreaName = areaNames.find(areaName => areaName.locations.includes(stringFromUrl()));
+ return ok(queriedAreaName.name);
+ }
+
+ function getAreaTypes(): Observable<HttpResponse<any>> {
+ const areaTypeList: Array<string> = areaTypes.map(areaType => areaType.name);
+ return ok(areaTypeList);
+ }
+
+ function getAreaTypeByArea(): Observable<HttpResponse<any>> {
+ const queriedAreaType: AreaType = areaTypes.find(areaType => areaType.area.includes(stringFromUrl()));
+ return ok(queriedAreaType.name);
+ }
+
+ function getAccountTypes(): Observable<HttpResponse<any>> {
+ return ok(accountTypes);
+ }
+
+ function getTransactionTypes(): Observable<HttpResponse<any>> {
+ return ok(transactionTypes);
+ }
+
+ function getGenders(): Observable<HttpResponse<any>> {
+ return ok(genders);
+ }
+
+ // helper functions
+
+ function ok(responseBody: any): Observable<HttpResponse<any>> {
+ return of(new HttpResponse({ status: 200, body: responseBody }));
+ }
+
+ function error(message): Observable<any> {
+ return throwError({ status: 400, error: { message } });
+ }
+
+ function idFromUrl(): number {
+ const urlParts: Array<string> = url.split('/');
+ return parseInt(urlParts[urlParts.length - 1], 10);
+ }
+
+ function stringFromUrl(): string {
+ const urlParts: Array<string> = url.split('/');
+ return urlParts[urlParts.length - 1];
+ }
+ }
+}
+
+export const MockBackendProvider = {
+ provide: HTTP_INTERCEPTORS,
+ useClass: MockBackendInterceptor,
+ multi: true
+};
+
+ +
+ src/app/_models/account.ts
+
+ Properties+ |
+
+
|
+
+ + age + | +
+ age:
+ |
+
+ Type : string
+
+ |
+
+ Optional + | +
+ + balance + | +
+ balance:
+ |
+
+ Type : number
+
+ |
+
+ Optional + | +
+ + category + | +
+ category:
+ |
+
+ Type : string
+
+ |
+
+ Optional + | +
+ + date_registered + | +
+ date_registered:
+ |
+
+ Type : number
+
+ |
+
+ + gender + | +
+ gender:
+ |
+
+ Type : string
+
+ |
+
+ + identities + | +
+ identities:
+ |
+
+ Type : literal type
+
+ |
+
+ + location + | +
+ location:
+ |
+
+ Type : literal type
+
+ |
+
+ + products + | +
+ products:
+ |
+
+ Type : string[]
+
+ |
+
+ + type + | +
+ type:
+ |
+
+ Type : string
+
+ |
+
+ Optional + | +
+ + vcard + | +
+ vcard:
+ |
+
+ Type : literal type
+
+ |
+
interface AccountDetails {
+ date_registered: number;
+ gender: string;
+ age?: string;
+ type?: string;
+ balance?: number;
+ identities: {
+ evm: {
+ 'bloxberg:8996': string[];
+ 'oldchain:1': string[];
+ };
+ latitude: number;
+ longitude: number;
+ };
+ location: {
+ area?: string;
+ area_name: string;
+ area_type?: string;
+ };
+ products: string[];
+ category?: string;
+ vcard: {
+ email: [{
+ value: string;
+ }];
+ fn: [{
+ value: string;
+ }];
+ n: [{
+ value: string[];
+ }];
+ tel: [{
+ meta: {
+ TYP: string[];
+ },
+ value: string;
+ }],
+ version: [{
+ value: string;
+ }];
+ };
+}
+
+interface Signature {
+ algo: string;
+ data: string;
+ digest: string;
+ engine: string;
+}
+
+interface Meta {
+ data: AccountDetails;
+ id: string;
+ signature: Signature;
+}
+
+interface MetaResponse {
+ id: string;
+ m: Meta;
+}
+
+const defaultAccount: AccountDetails = {
+ date_registered: Date.now(),
+ gender: 'other',
+ identities: {
+ evm: {
+ 'bloxberg:8996': [''],
+ 'oldchain:1': [''],
+ },
+ latitude: 0,
+ longitude: 0,
+ },
+ location: {
+ area_name: 'Kilifi',
+ },
+ products: [],
+ vcard: {
+ email: [{
+ value: '',
+ }],
+ fn: [{
+ value: 'Sarafu Contract',
+ }],
+ n: [{
+ value: ['Sarafu', 'Contract'],
+ }],
+ tel: [{
+ meta: {
+ TYP: [],
+ },
+ value: '',
+ }],
+ version: [{
+ value: '3.0',
+ }],
+ },
+};
+
+export {
+ AccountDetails,
+ Signature,
+ Meta,
+ MetaResponse,
+ defaultAccount
+};
+
+ +
+ src/app/_models/mappings.ts
+
+ Properties+ |
+
+ + | +
+ + action + | +
+ action:
+ |
+
+ Type : string
+
+ |
+
+ + approval + | +
+ approval:
+ |
+
+ Type : boolean
+
+ |
+
+ + id + | +
+ id:
+ |
+
+ Type : number
+
+ |
+
+ + role + | +
+ role:
+ |
+
+ Type : string
+
+ |
+
+ + user + | +
+ user:
+ |
+
+ Type : string
+
+ |
+
interface Action {
+ id: number;
+ user: string;
+ role: string;
+ action: string;
+ approval: boolean;
+}
+
+interface Category {
+ name: string;
+ products: Array<string>;
+}
+
+interface AreaName {
+ name: string;
+ locations: Array<string>;
+}
+
+interface AreaType {
+ name: string;
+ area: Array<string>;
+}
+
+export {
+ Action,
+ Category,
+ AreaName,
+ AreaType
+};
+
+ +
+ src/app/_models/mappings.ts
+
+ Properties+ |
+
+ + | +
+ + locations + | +
+ locations:
+ |
+
+ Type : Array<string>
+
+ |
+
+ + name + | +
+ name:
+ |
+
+ Type : string
+
+ |
+
interface Action {
+ id: number;
+ user: string;
+ role: string;
+ action: string;
+ approval: boolean;
+}
+
+interface Category {
+ name: string;
+ products: Array<string>;
+}
+
+interface AreaName {
+ name: string;
+ locations: Array<string>;
+}
+
+interface AreaType {
+ name: string;
+ area: Array<string>;
+}
+
+export {
+ Action,
+ Category,
+ AreaName,
+ AreaType
+};
+
+ +
+ src/app/_models/mappings.ts
+
+ Properties+ |
+
+ + | +
+ + area + | +
+ area:
+ |
+
+ Type : Array<string>
+
+ |
+
+ + name + | +
+ name:
+ |
+
+ Type : string
+
+ |
+
interface Action {
+ id: number;
+ user: string;
+ role: string;
+ action: string;
+ approval: boolean;
+}
+
+interface Category {
+ name: string;
+ products: Array<string>;
+}
+
+interface AreaName {
+ name: string;
+ locations: Array<string>;
+}
+
+interface AreaType {
+ name: string;
+ area: Array<string>;
+}
+
+export {
+ Action,
+ Category,
+ AreaName,
+ AreaType
+};
+
+ +
+ src/app/_models/mappings.ts
+
+ Properties+ |
+
+ + | +
+ + name + | +
+ name:
+ |
+
+ Type : string
+
+ |
+
+ + products + | +
+ products:
+ |
+
+ Type : Array<string>
+
+ |
+
interface Action {
+ id: number;
+ user: string;
+ role: string;
+ action: string;
+ approval: boolean;
+}
+
+interface Category {
+ name: string;
+ products: Array<string>;
+}
+
+interface AreaName {
+ name: string;
+ locations: Array<string>;
+}
+
+interface AreaType {
+ name: string;
+ area: Array<string>;
+}
+
+export {
+ Action,
+ Category,
+ AreaName,
+ AreaType
+};
+
+ +
+ src/app/_models/account.ts
+
+ Properties+ |
+
+ + | +
+ + data + | +
+ data:
+ |
+
+ Type : AccountDetails
+
+ |
+
+ + id + | +
+ id:
+ |
+
+ Type : string
+
+ |
+
+ + signature + | +
+ signature:
+ |
+
+ Type : Signature
+
+ |
+
interface AccountDetails {
+ date_registered: number;
+ gender: string;
+ age?: string;
+ type?: string;
+ balance?: number;
+ identities: {
+ evm: {
+ 'bloxberg:8996': string[];
+ 'oldchain:1': string[];
+ };
+ latitude: number;
+ longitude: number;
+ };
+ location: {
+ area?: string;
+ area_name: string;
+ area_type?: string;
+ };
+ products: string[];
+ category?: string;
+ vcard: {
+ email: [{
+ value: string;
+ }];
+ fn: [{
+ value: string;
+ }];
+ n: [{
+ value: string[];
+ }];
+ tel: [{
+ meta: {
+ TYP: string[];
+ },
+ value: string;
+ }],
+ version: [{
+ value: string;
+ }];
+ };
+}
+
+interface Signature {
+ algo: string;
+ data: string;
+ digest: string;
+ engine: string;
+}
+
+interface Meta {
+ data: AccountDetails;
+ id: string;
+ signature: Signature;
+}
+
+interface MetaResponse {
+ id: string;
+ m: Meta;
+}
+
+const defaultAccount: AccountDetails = {
+ date_registered: Date.now(),
+ gender: 'other',
+ identities: {
+ evm: {
+ 'bloxberg:8996': [''],
+ 'oldchain:1': [''],
+ },
+ latitude: 0,
+ longitude: 0,
+ },
+ location: {
+ area_name: 'Kilifi',
+ },
+ products: [],
+ vcard: {
+ email: [{
+ value: '',
+ }],
+ fn: [{
+ value: 'Sarafu Contract',
+ }],
+ n: [{
+ value: ['Sarafu', 'Contract'],
+ }],
+ tel: [{
+ meta: {
+ TYP: [],
+ },
+ value: '',
+ }],
+ version: [{
+ value: '3.0',
+ }],
+ },
+};
+
+export {
+ AccountDetails,
+ Signature,
+ Meta,
+ MetaResponse,
+ defaultAccount
+};
+
+ +
+ src/app/_models/account.ts
+
+ Properties+ |
+
+ + | +
+ + id + | +
+ id:
+ |
+
+ Type : string
+
+ |
+
+ + m + | +
+ m:
+ |
+
+ Type : Meta
+
+ |
+
interface AccountDetails {
+ date_registered: number;
+ gender: string;
+ age?: string;
+ type?: string;
+ balance?: number;
+ identities: {
+ evm: {
+ 'bloxberg:8996': string[];
+ 'oldchain:1': string[];
+ };
+ latitude: number;
+ longitude: number;
+ };
+ location: {
+ area?: string;
+ area_name: string;
+ area_type?: string;
+ };
+ products: string[];
+ category?: string;
+ vcard: {
+ email: [{
+ value: string;
+ }];
+ fn: [{
+ value: string;
+ }];
+ n: [{
+ value: string[];
+ }];
+ tel: [{
+ meta: {
+ TYP: string[];
+ },
+ value: string;
+ }],
+ version: [{
+ value: string;
+ }];
+ };
+}
+
+interface Signature {
+ algo: string;
+ data: string;
+ digest: string;
+ engine: string;
+}
+
+interface Meta {
+ data: AccountDetails;
+ id: string;
+ signature: Signature;
+}
+
+interface MetaResponse {
+ id: string;
+ m: Meta;
+}
+
+const defaultAccount: AccountDetails = {
+ date_registered: Date.now(),
+ gender: 'other',
+ identities: {
+ evm: {
+ 'bloxberg:8996': [''],
+ 'oldchain:1': [''],
+ },
+ latitude: 0,
+ longitude: 0,
+ },
+ location: {
+ area_name: 'Kilifi',
+ },
+ products: [],
+ vcard: {
+ email: [{
+ value: '',
+ }],
+ fn: [{
+ value: 'Sarafu Contract',
+ }],
+ n: [{
+ value: ['Sarafu', 'Contract'],
+ }],
+ tel: [{
+ meta: {
+ TYP: [],
+ },
+ value: '',
+ }],
+ version: [{
+ value: '3.0',
+ }],
+ },
+};
+
+export {
+ AccountDetails,
+ Signature,
+ Meta,
+ MetaResponse,
+ defaultAccount
+};
+
+ +
+ src/app/_pgp/pgp-key-store.ts
+
+
+ KeyStore
+
+ Methods+ |
+
+
|
+
+ + + + clearKeysInKeyring + + + + | +
+clearKeysInKeyring()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:30
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + getEncryptKeys + + + + | +
+getEncryptKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:14
+ |
+
+
+
+ Returns :
+ Array<any>
+
+ |
+
+ + + + getFingerprint + + + + | +
+getFingerprint()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:19
+ |
+
+
+
+ Returns :
+ string
+
+ |
+
+ + + + getKeyId + + + + | +||||||
+getKeyId(key: any)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:20
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ string
+
+
+
+
+ |
+
+ + + + getKeysForId + + + + | +||||||
+getKeysForId(keyId: string)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:22
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Array<any>
+
+
+
+
+ |
+
+ + + + getPrivateKey + + + + | +
+getPrivateKey()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:16
+ |
+
+
+
+ Returns :
+ any
+
+ |
+
+ + + + getPrivateKeyForId + + + + | +||||||
+getPrivateKeyForId(keyId: string)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:24
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + getPrivateKeyId + + + + | +
+getPrivateKeyId()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:21
+ |
+
+
+
+ Returns :
+ string
+
+ |
+
+ + + + getPrivateKeys + + + + | +
+getPrivateKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:15
+ |
+
+
+
+ Returns :
+ Array<any>
+
+ |
+
+ + + + getPublicKeyForId + + + + | +||||||
+getPublicKeyForId(keyId: string)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:23
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + getPublicKeyForSubkeyId + + + + | +||||||
+getPublicKeyForSubkeyId(subkeyId: string)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:25
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + getPublicKeys + + + + | +
+getPublicKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:11
+ |
+
+
+
+ Returns :
+ Array<any>
+
+ |
+
+ + + + getPublicKeysForAddress + + + + | +||||||
+getPublicKeysForAddress(address: string)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:26
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Array<any>
+
+
+
+
+ |
+
+ + + + getTrustedActiveKeys + + + + | +
+getTrustedActiveKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:13
+ |
+
+
+
+ Returns :
+ Array<any>
+
+ |
+
+ + + + getTrustedKeys + + + + | +
+getTrustedKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:12
+ |
+
+
+
+ Returns :
+ Array<any>
+
+ |
+
+ + + + importKeyPair + + + + | +|||||||||
+importKeyPair(publicKey: any, privateKey: any)
+ |
+ |||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:8
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + importPrivateKey + + + + | +||||||
+importPrivateKey(privateKey: any)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:10
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + importPublicKey + + + + | +||||||
+importPublicKey(publicKey: any)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:9
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + isEncryptedPrivateKey + + + + | +||||||
+isEncryptedPrivateKey(privateKey: any)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:18
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+
+ |
+
+ + + + isValidKey + + + + | +||||||
+isValidKey(key: any)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:17
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+
+ |
+
+ + + + loadKeyring + + + + | +
+loadKeyring()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:7
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + removeKeysForId + + + + | +||||||
+removeKeysForId(keyId: string)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:27
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Array<any>
+
+
+
+
+ |
+
+ + + + removePublicKey + + + + | +||||||
+removePublicKey(publicKey: any)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:29
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + removePublicKeyForId + + + + | +||||||
+removePublicKeyForId(keyId: string)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:28
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + sign + + + + | +||||||
+sign(plainText: string)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:31
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<any>
+
+
+
+
+ |
+
import { KeyStore } from 'cic-client-meta';
+// TODO should we put this on the mutable key store object
+import * as openpgp from 'openpgp';
+const keyring = new openpgp.Keyring();
+
+interface MutableKeyStore extends KeyStore {
+ loadKeyring(): void;
+ importKeyPair(publicKey: any, privateKey: any): Promise<void>;
+ importPublicKey(publicKey: any): void;
+ importPrivateKey(privateKey: any): Promise<void>;
+ getPublicKeys(): Array<any>;
+ getTrustedKeys(): Array<any>;
+ getTrustedActiveKeys(): Array<any>;
+ getEncryptKeys(): Array<any>;
+ getPrivateKeys(): Array<any>;
+ getPrivateKey(): any;
+ isValidKey(key: any): Promise<boolean>;
+ isEncryptedPrivateKey(privateKey: any): Promise<boolean>;
+ getFingerprint(): string;
+ getKeyId(key: any): string;
+ getPrivateKeyId(): string;
+ getKeysForId(keyId: string): Array<any>;
+ getPublicKeyForId(keyId: string): any;
+ getPrivateKeyForId(keyId: string): any;
+ getPublicKeyForSubkeyId(subkeyId: string): any;
+ getPublicKeysForAddress(address: string): Array<any>;
+ removeKeysForId(keyId: string): Array<any>;
+ removePublicKeyForId(keyId: string): any;
+ removePublicKey(publicKey: any): any;
+ clearKeysInKeyring(): void;
+ sign(plainText: string): Promise<any>;
+}
+
+class MutablePgpKeyStore implements MutableKeyStore{
+
+ async loadKeyring(): Promise<void> {
+ await keyring.load();
+ await keyring.store();
+ }
+
+ async importKeyPair(publicKey: any, privateKey: any): Promise<void> {
+ await keyring.publicKeys.importKey(publicKey);
+ await keyring.privateKeys.importKey(privateKey);
+ }
+
+ importPublicKey(publicKey: any): void {
+ keyring.publicKeys.importKey(publicKey);
+ }
+
+ async importPrivateKey(privateKey: any): Promise<void> {
+ await keyring.privateKeys.importKey(privateKey);
+ }
+
+ getPublicKeys(): Array<any> {
+ return keyring.publicKeys.keys;
+ }
+
+ getTrustedKeys(): Array<any> {
+ return keyring.publicKeys.keys;
+ }
+
+ getTrustedActiveKeys(): Array<any> {
+ return keyring.publicKeys.keys;
+ }
+
+ getEncryptKeys(): Array<any> {
+ return [];
+ }
+
+ getPrivateKeys(): Array<any> {
+ return keyring.privateKeys.keys;
+ }
+
+ getPrivateKey(): any {
+ return keyring.privateKeys && keyring.privateKeys.keys[0];
+ }
+
+ async isValidKey(key): Promise<boolean> {
+ // There is supposed to be an openpgp.readKey() method but I can't find it?
+ const _key = await openpgp.key.readArmored(key);
+ return !_key.err;
+ }
+
+ async isEncryptedPrivateKey(privateKey: any): Promise<boolean> {
+ const imported = await openpgp.key.readArmored(privateKey);
+ for (const key of imported.keys) {
+ if (key.isDecrypted()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ getFingerprint(): string {
+ // TODO Handle multiple keys
+ return keyring.privateKeys && keyring.privateKeys.keys[0] && keyring.privateKeys.keys[0].keyPacket &&
+ keyring.privateKeys.keys[0].keyPacket.fingerprint;
+ }
+
+ getKeyId(key: any): string {
+ return key.getKeyId().toHex();
+ }
+
+ getPrivateKeyId(): string {
+ // TODO is there a library that comes with angular for doing this?
+ return keyring.privateKeys && keyring.privateKeys.keys[0] && keyring.privateKeys.keys[0].getKeyId().toHex();
+ }
+
+ getKeysForId(keyId: string): Array<any> {
+ return keyring.getKeysForId(keyId);
+ }
+
+ getPublicKeyForId(keyId): any {
+ return keyring.publicKeys.getForId(keyId);
+ }
+
+ getPrivateKeyForId(keyId): any {
+ return keyring.privateKeys.getForId(keyId);
+ }
+
+ getPublicKeyForSubkeyId(subkeyId): any {
+ return keyring.publicKeys.getForId(subkeyId, true);
+ }
+
+ getPublicKeysForAddress(address): Array<any> {
+ return keyring.publicKeys.getForAddress(address);
+ }
+
+ removeKeysForId(keyId): Array<any> {
+ return keyring.removeKeysForId(keyId);
+ }
+
+ removePublicKeyForId(keyId): any {
+ return keyring.publicKeys.removeForId(keyId);
+ }
+
+ removePublicKey(publicKey: any): any {
+ const keyId = publicKey.getKeyId().toHex();
+ return keyring.publicKeys.removeForId(keyId);
+ }
+
+ clearKeysInKeyring(): void {
+ keyring.clear();
+ }
+
+ async sign(plainText): Promise<any> {
+ const privateKey = this.getPrivateKey();
+ if (!privateKey.isDecrypted()) {
+ const password = window.prompt('password');
+ await privateKey.decrypt(password);
+ }
+ const opts = {
+ message: openpgp.message.fromText(plainText),
+ privateKeys: [privateKey],
+ detached: true,
+ };
+ const signatureObject = await openpgp.sign(opts);
+ return signatureObject.signature;
+ }
+}
+
+export {
+ MutablePgpKeyStore,
+ MutableKeyStore
+};
+
+ +
+ src/app/_pgp/pgp-signer.ts
+
+ Methods+ |
+
+
|
+
+ + + + digest + + + + | +
+digest()
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:7
+ |
+
+
+
+ Returns :
+ string
+
+ |
+
import {MutableKeyStore} from '@app/_pgp/pgp-key-store';
+import {LoggingService} from '@app/_services/logging.service';
+
+const openpgp = require('openpgp');
+
+interface Signable {
+ digest(): string;
+}
+
+type Signature = {
+ engine: string
+ algo: string
+ data: string
+ digest: string;
+};
+
+interface Signer {
+ onsign(signature: Signature): void;
+ onverify(flag: boolean): void;
+ fingerprint(): string;
+ prepare(material: Signable): boolean;
+ verify(digest: string, signature: Signature): void;
+ sign(digest: string): Promise<void>;
+}
+
+class PGPSigner implements Signer {
+
+ engine = 'pgp';
+ algo = 'sha256';
+ dgst: string;
+ signature: Signature;
+ keyStore: MutableKeyStore;
+ onsign: (signature: Signature) => void;
+ onverify: (flag: boolean) => void;
+ loggingService: LoggingService;
+
+ constructor(keyStore: MutableKeyStore) {
+ this.keyStore = keyStore;
+ this.onsign = (signature: Signature) => {};
+ this.onverify = (flag: boolean) => {};
+ }
+
+ public fingerprint(): string {
+ return this.keyStore.getFingerprint();
+ }
+
+ public prepare(material: Signable): boolean {
+ this.dgst = material.digest();
+ return true;
+ }
+
+ public verify(digest: string, signature: Signature): void {
+ openpgp.signature.readArmored(signature.data).then((sig) => {
+ const opts = {
+ message: openpgp.cleartext.fromText(digest),
+ publicKeys: this.keyStore.getTrustedKeys(),
+ signature: sig,
+ };
+ openpgp.verify(opts).then((v) => {
+ let i = 0;
+ for (i = 0; i < v.signatures.length; i++) {
+ const s = v.signatures[i];
+ if (s.valid) {
+ this.onverify(s);
+ return;
+ }
+ }
+ this.loggingService.sendErrorLevelMessage(`Checked ${i} signature(s) but none valid`, this, {error: '404 Not found!'});
+ this.onverify(false);
+ });
+ }).catch((e) => {
+ this.loggingService.sendErrorLevelMessage(e.message, this, {error: e});
+ this.onverify(false);
+ });
+ }
+
+ public async sign(digest: string): Promise<void> {
+ const m = openpgp.cleartext.fromText(digest);
+ const pk = this.keyStore.getPrivateKey();
+ if (!pk.isDecrypted()) {
+ const password = window.prompt('password');
+ await pk.decrypt(password);
+ }
+ const opts = {
+ message: m,
+ privateKeys: [pk],
+ detached: true,
+ };
+ openpgp.sign(opts).then((s) => {
+ this.signature = {
+ engine: this.engine,
+ algo: this.algo,
+ data: s.signature,
+ // TODO: fix for browser later
+ digest,
+ };
+ this.onsign(this.signature);
+ }).catch((e) => {
+ this.loggingService.sendErrorLevelMessage(e.message, this, {error: e});
+ this.onsign(undefined);
+ });
+ }
+}
+
+export {
+ Signable,
+ Signature,
+ Signer,
+ PGPSigner
+};
+
+ +
+ src/app/_models/account.ts
+
+ Properties+ |
+
+ + | +
+ + algo + | +
+ algo:
+ |
+
+ Type : string
+
+ |
+
+ + data + | +
+ data:
+ |
+
+ Type : string
+
+ |
+
+ + digest + | +
+ digest:
+ |
+
+ Type : string
+
+ |
+
+ + engine + | +
+ engine:
+ |
+
+ Type : string
+
+ |
+
interface AccountDetails {
+ date_registered: number;
+ gender: string;
+ age?: string;
+ type?: string;
+ balance?: number;
+ identities: {
+ evm: {
+ 'bloxberg:8996': string[];
+ 'oldchain:1': string[];
+ };
+ latitude: number;
+ longitude: number;
+ };
+ location: {
+ area?: string;
+ area_name: string;
+ area_type?: string;
+ };
+ products: string[];
+ category?: string;
+ vcard: {
+ email: [{
+ value: string;
+ }];
+ fn: [{
+ value: string;
+ }];
+ n: [{
+ value: string[];
+ }];
+ tel: [{
+ meta: {
+ TYP: string[];
+ },
+ value: string;
+ }],
+ version: [{
+ value: string;
+ }];
+ };
+}
+
+interface Signature {
+ algo: string;
+ data: string;
+ digest: string;
+ engine: string;
+}
+
+interface Meta {
+ data: AccountDetails;
+ id: string;
+ signature: Signature;
+}
+
+interface MetaResponse {
+ id: string;
+ m: Meta;
+}
+
+const defaultAccount: AccountDetails = {
+ date_registered: Date.now(),
+ gender: 'other',
+ identities: {
+ evm: {
+ 'bloxberg:8996': [''],
+ 'oldchain:1': [''],
+ },
+ latitude: 0,
+ longitude: 0,
+ },
+ location: {
+ area_name: 'Kilifi',
+ },
+ products: [],
+ vcard: {
+ email: [{
+ value: '',
+ }],
+ fn: [{
+ value: 'Sarafu Contract',
+ }],
+ n: [{
+ value: ['Sarafu', 'Contract'],
+ }],
+ tel: [{
+ meta: {
+ TYP: [],
+ },
+ value: '',
+ }],
+ version: [{
+ value: '3.0',
+ }],
+ },
+};
+
+export {
+ AccountDetails,
+ Signature,
+ Meta,
+ MetaResponse,
+ defaultAccount
+};
+
+ +
+ src/app/_pgp/pgp-signer.ts
+
+ Methods+ |
+
+ + | +
+ + + + fingerprint + + + + | +
+fingerprint()
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:20
+ |
+
+
+
+ Returns :
+ string
+
+ |
+
+ + + + onsign + + + + | +||||||
+onsign(signature: Signature)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-signer.ts:18
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + onverify + + + + | +||||||
+onverify(flag: boolean)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-signer.ts:19
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + prepare + + + + | +||||||
+prepare(material: Signable)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-signer.ts:21
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ boolean
+
+
+
+
+ |
+
+ + + + sign + + + + | +||||||
+sign(digest: string)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-signer.ts:23
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + verify + + + + | +|||||||||
+verify(digest: string, signature: Signature)
+ |
+ |||||||||
+ Defined in src/app/_pgp/pgp-signer.ts:22
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
import {MutableKeyStore} from '@app/_pgp/pgp-key-store';
+import {LoggingService} from '@app/_services/logging.service';
+
+const openpgp = require('openpgp');
+
+interface Signable {
+ digest(): string;
+}
+
+type Signature = {
+ engine: string
+ algo: string
+ data: string
+ digest: string;
+};
+
+interface Signer {
+ onsign(signature: Signature): void;
+ onverify(flag: boolean): void;
+ fingerprint(): string;
+ prepare(material: Signable): boolean;
+ verify(digest: string, signature: Signature): void;
+ sign(digest: string): Promise<void>;
+}
+
+class PGPSigner implements Signer {
+
+ engine = 'pgp';
+ algo = 'sha256';
+ dgst: string;
+ signature: Signature;
+ keyStore: MutableKeyStore;
+ onsign: (signature: Signature) => void;
+ onverify: (flag: boolean) => void;
+ loggingService: LoggingService;
+
+ constructor(keyStore: MutableKeyStore) {
+ this.keyStore = keyStore;
+ this.onsign = (signature: Signature) => {};
+ this.onverify = (flag: boolean) => {};
+ }
+
+ public fingerprint(): string {
+ return this.keyStore.getFingerprint();
+ }
+
+ public prepare(material: Signable): boolean {
+ this.dgst = material.digest();
+ return true;
+ }
+
+ public verify(digest: string, signature: Signature): void {
+ openpgp.signature.readArmored(signature.data).then((sig) => {
+ const opts = {
+ message: openpgp.cleartext.fromText(digest),
+ publicKeys: this.keyStore.getTrustedKeys(),
+ signature: sig,
+ };
+ openpgp.verify(opts).then((v) => {
+ let i = 0;
+ for (i = 0; i < v.signatures.length; i++) {
+ const s = v.signatures[i];
+ if (s.valid) {
+ this.onverify(s);
+ return;
+ }
+ }
+ this.loggingService.sendErrorLevelMessage(`Checked ${i} signature(s) but none valid`, this, {error: '404 Not found!'});
+ this.onverify(false);
+ });
+ }).catch((e) => {
+ this.loggingService.sendErrorLevelMessage(e.message, this, {error: e});
+ this.onverify(false);
+ });
+ }
+
+ public async sign(digest: string): Promise<void> {
+ const m = openpgp.cleartext.fromText(digest);
+ const pk = this.keyStore.getPrivateKey();
+ if (!pk.isDecrypted()) {
+ const password = window.prompt('password');
+ await pk.decrypt(password);
+ }
+ const opts = {
+ message: m,
+ privateKeys: [pk],
+ detached: true,
+ };
+ openpgp.sign(opts).then((s) => {
+ this.signature = {
+ engine: this.engine,
+ algo: this.algo,
+ data: s.signature,
+ // TODO: fix for browser later
+ digest,
+ };
+ this.onsign(this.signature);
+ }).catch((e) => {
+ this.loggingService.sendErrorLevelMessage(e.message, this, {error: e});
+ this.onsign(undefined);
+ });
+ }
+}
+
+export {
+ Signable,
+ Signature,
+ Signer,
+ PGPSigner
+};
+
+ +
+ src/app/_models/staff.ts
+
+ Properties+ |
+
+ + | +
+ + comment + | +
+ comment:
+ |
+
+ Type : string
+
+ |
+
+ + email + | +
+ email:
+ |
+
+ Type : string
+
+ |
+
+ + name + | +
+ name:
+ |
+
+ Type : string
+
+ |
+
+ + tag + | +
+ tag:
+ |
+
+ Type : number
+
+ |
+
+ + userid + | +
+ userid:
+ |
+
+ Type : string
+
+ |
+
interface Staff {
+ comment: string;
+ email: string;
+ name: string;
+ tag: number;
+ userid: string;
+}
+
+export {
+ Staff
+};
+
+ +
+ src/app/_models/token.ts
+
+ Properties+ |
+
+ + | +
+ + address + | +
+ address:
+ |
+
+ Type : string
+
+ |
+
+ + decimals + | +
+ decimals:
+ |
+
+ Type : string
+
+ |
+
+ + name + | +
+ name:
+ |
+
+ Type : string
+
+ |
+
+ + owner + | +
+ owner:
+ |
+
+ Type : string
+
+ |
+
+ Optional + | +
+ + reserveRatio + | +
+ reserveRatio:
+ |
+
+ Type : string
+
+ |
+
+ Optional + | +
+ + reserves + | +
+ reserves:
+ |
+
+ Type : literal type
+
+ |
+
+ + supply + | +
+ supply:
+ |
+
+ Type : string
+
+ |
+
+ + symbol + | +
+ symbol:
+ |
+
+ Type : string
+
+ |
+
interface Token {
+ name: string;
+ symbol: string;
+ address: string;
+ supply: string;
+ decimals: string;
+ reserves: {
+ '0xa686005CE37Dce7738436256982C3903f2E4ea8E'?: {
+ weight: string;
+ balance: string;
+ }
+ };
+ reserveRatio?: string;
+ owner?: string;
+}
+
+export {
+ Token
+};
+
+