diff --git a/docs/compodoc/classes/AccountIndex.html b/docs/compodoc/classes/AccountIndex.html new file mode 100644 index 0000000..9808103 --- /dev/null +++ b/docs/compodoc/classes/AccountIndex.html @@ -0,0 +1,866 @@ + + +
+ + ++
+ src/app/_eth/accountIndex.ts
+
+
+
Provides an instance of the accounts registry contract. +Allows querying of accounts that have been registered as valid accounts in the network.
+ + + + + ++
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(contractAddress: string, signerAddress?: string)
+ |
+ ||||||||||||
+ Defined in src/app/_eth/accountIndex.ts:26
+ |
+ ||||||||||||
+ Create a connection to the deployed account registry contract. +
+ Parameters :
+
+
|
+
+ + + + contract + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_eth/accountIndex.ts:22
+ |
+
+ The instance of the account registry contract. + |
+
+ + + + contractAddress + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_eth/accountIndex.ts:24
+ |
+
+ The deployed account registry contract's address. + |
+
+ + + + signerAddress + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_eth/accountIndex.ts:26
+ |
+
+ The account address of the account that deployed the account registry contract. + |
+
+ + + + Public + Async + addToAccountRegistry + + + + | +||||||||
+
+ addToAccountRegistry(address: string)
+ |
+ ||||||||
+ Defined in src/app/_eth/accountIndex.ts:58
+ |
+ ||||||||
+ Registers an account to the accounts registry. +Requires availability of the signer address. +
+ Parameters :
+
+
+ Example :
+
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+ true - If registration is successful or account had already been registered. + + |
+
+ + + + Public + Async + haveAccount + + + + | +||||||||
+
+ haveAccount(address: string)
+ |
+ ||||||||
+ Defined in src/app/_eth/accountIndex.ts:79
+ |
+ ||||||||
+ Checks whether a specific account address has been registered in the accounts registry. +Returns "true" for available and "false" otherwise. +
+ Parameters :
+
+
+ Example :
+
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+ true - If the address has been registered in the accounts registry. + + |
+
+ + + + Public + Async + last + + + + | +||||||||
+
+ last(numberOfAccounts: number)
+ |
+ ||||||||
+ Defined in src/app/_eth/accountIndex.ts:96
+ |
+ ||||||||
+ Returns a specified number of the most recently registered accounts. +
+ Parameters :
+
+
+ Example :
+
+
+
+
+
+ Returns :
+ Promise<Array<string>>
+
+
+
+ An array of registered account addresses. + + |
+
+ + + + Public + Async + totalAccounts + + + + | +
+
+ totalAccounts()
+ |
+
+ Defined in src/app/_eth/accountIndex.ts:122
+ |
+
+ Returns the total number of accounts that have been registered in the network. +
+ Example :
+
+
+
+
+
+ Returns :
+ Promise<number>
+
+
+
+ The total number of registered accounts. + + |
+
import Web3 from 'web3';
+
+// Application imports
+import { Web3Service } from '@app/_services/web3.service';
+import { environment } from '@src/environments/environment';
+
+/** Fetch the account registry contract's ABI. */
+const abi: Array<any> = require('@src/assets/js/block-sync/data/AccountsIndex.json');
+/** Establish a connection to the blockchain network. */
+const web3: Web3 = Web3Service.getInstance();
+
+/**
+ * Provides an instance of the accounts registry contract.
+ * Allows querying of accounts that have been registered as valid accounts in the network.
+ *
+ * @remarks
+ * This is our interface to the accounts registry contract.
+ */
+export class AccountIndex {
+ /** The instance of the account registry contract. */
+ contract: any;
+ /** The deployed account registry contract's address. */
+ contractAddress: string;
+ /** The account address of the account that deployed the account registry contract. */
+ signerAddress: string;
+
+ /**
+ * Create a connection to the deployed account registry contract.
+ *
+ * @param contractAddress - The deployed account registry contract's address.
+ * @param signerAddress - The account address of the account that deployed the account registry contract.
+ */
+ 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];
+ }
+ }
+
+ /**
+ * Registers an account to the accounts registry.
+ * Requires availability of the signer address.
+ *
+ * @async
+ * @example
+ * Prints "true" for registration of '0xc0ffee254729296a45a3885639AC7E10F9d54979':
+ * ```typescript
+ * console.log(await addToAccountRegistry('0xc0ffee254729296a45a3885639AC7E10F9d54979'));
+ * ```
+ *
+ * @param address - The account address to be registered to the accounts registry contract.
+ * @returns true - If registration is successful or account had already been registered.
+ */
+ 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;
+ }
+
+ /**
+ * Checks whether a specific account address has been registered in the accounts registry.
+ * Returns "true" for available and "false" otherwise.
+ *
+ * @async
+ * @example
+ * Prints "true" or "false" depending on whether '0xc0ffee254729296a45a3885639AC7E10F9d54979' has been registered:
+ * ```typescript
+ * console.log(await haveAccount('0xc0ffee254729296a45a3885639AC7E10F9d54979'));
+ * ```
+ *
+ * @param address - The account address to be validated.
+ * @returns true - If the address has been registered in the accounts registry.
+ */
+ public async haveAccount(address: string): Promise<boolean> {
+ return (await this.contract.methods.have(address).call()) !== 0;
+ }
+
+ /**
+ * Returns a specified number of the most recently registered accounts.
+ *
+ * @async
+ * @example
+ * Prints an array of accounts:
+ * ```typescript
+ * console.log(await last(5));
+ * ```
+ *
+ * @param numberOfAccounts - The number of accounts to return from the accounts registry.
+ * @returns An array of registered account addresses.
+ */
+ public async last(numberOfAccounts: number): Promise<Array<string>> {
+ const count: number = await this.totalAccounts();
+ let lowest: number = count - numberOfAccounts;
+ if (lowest < 0) {
+ lowest = 0;
+ }
+ const accounts: Array<string> = [];
+ for (let i = count - 1; i >= lowest; i--) {
+ const account: string = await this.contract.methods.entry(i).call();
+ accounts.push(account);
+ }
+ return accounts;
+ }
+
+ /**
+ * Returns the total number of accounts that have been registered in the network.
+ *
+ * @async
+ * @example
+ * Prints the total number of registered accounts:
+ * ```typescript
+ * console.log(await totalAccounts());
+ * ```
+ *
+ * @returns The total number of registered accounts.
+ */
+ public async totalAccounts(): Promise<number> {
+ return await this.contract.methods.entryCount().call();
+ }
+}
+
+ +
+ 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
+
+
+
Custom provider that defines how form controls behave with regards to displaying error messages.
+ + + + ++
+ ErrorStateMatcher
+
+ Methods+ |
+
+
|
+
+ + + + isErrorState + + + + | +||||||||||||
+isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null)
+ |
+ ||||||||||||
+ + | +||||||||||||
+ Checks whether an invalid input has been made and an error should be made. +
+ Parameters :
+
+
+
+
+ Returns :
+ boolean
+
+
+
+ true - If an invalid input has been made to the form control. + + |
+
import { ErrorStateMatcher } from '@angular/material/core';
+import { FormControl, FormGroupDirective, NgForm } from '@angular/forms';
+
+/**
+ * Custom provider that defines how form controls behave with regards to displaying error messages.
+ *
+ */
+export class CustomErrorStateMatcher implements ErrorStateMatcher {
+ /**
+ * Checks whether an invalid input has been made and an error should be made.
+ *
+ * @param control - Tracks the value and validation status of an individual form control.
+ * @param form - Binding of an existing FormGroup to a DOM element.
+ * @returns true - If an invalid input has been made to the form control.
+ */
+ 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
+
+
+
Provides methods to perform custom validation to form inputs.
+ + + + + + +
+ Methods+ |
+
+
|
+
+ + + + Static + passwordMatchValidator + + + + | +||||||||
+
+ passwordMatchValidator(control: AbstractControl)
+ |
+ ||||||||
+ Defined in src/app/_helpers/custom.validator.ts:13
+ |
+ ||||||||
+ Sets errors to the confirm password input field if it does not match with the value in the password input field. +
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + Static + patternValidator + + + + | +||||||||||||
+
+ patternValidator(regex: RegExp, error: ValidationErrors)
+ |
+ ||||||||||||
+ Defined in src/app/_helpers/custom.validator.ts:28
+ |
+ ||||||||||||
+ Sets errors to a form field if it does not match with the regular expression given. +
+ Parameters :
+
+
+
+
+ Returns :
+ ValidationErrors | null
+
+
+
+ The map of errors returned from failed validation checks. + + |
+
import { AbstractControl, ValidationErrors } from '@angular/forms';
+
+/**
+ * Provides methods to perform custom validation to form inputs.
+ */
+export class CustomValidator {
+ /**
+ * Sets errors to the confirm password input field if it does not match with the value in the password input field.
+ *
+ * @param control - The control object of the form being validated.
+ */
+ 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 });
+ }
+ }
+
+ /**
+ * Sets errors to a form field if it does not match with the regular expression given.
+ *
+ * @param regex - The regular expression to match with the form field.
+ * @param error - Defines the map of errors to return from failed validation checks.
+ * @returns The map of errors returned from failed validation checks.
+ */
+ 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
+
+
+
A generalized http response error.
+ + + ++
+ Error
+
+
+ Properties+ |
+
+
|
+
+constructor(message: string, status: number)
+ |
+
+ Defined in src/app/_helpers/global-error-handler.ts:16
+ |
+
+ Initialize the HttpError class. + |
+
+ + + + Public + status + + + | +
+ Type : number
+
+ |
+
+ Defined in src/app/_helpers/global-error-handler.ts:16
+ |
+
+ The error's status code. + |
+
import { HttpErrorResponse } from '@angular/common/http';
+import { ErrorHandler, Injectable } from '@angular/core';
+import { Router } from '@angular/router';
+
+// Application imports
+import { LoggingService } from '@app/_services/logging.service';
+
+/**
+ * A generalized http response error.
+ *
+ * @extends Error
+ */
+export class HttpError extends Error {
+ /** The error's status code. */
+ public status: number;
+
+ /**
+ * Initialize the HttpError class.
+ *
+ * @param message - The message given by the error.
+ * @param status - The status code given by the error.
+ */
+ constructor(message: string, status: number) {
+ super(message);
+ this.status = status;
+ this.name = 'HttpError';
+ }
+}
+
+/**
+ * Provides a hook for centralized exception handling.
+ *
+ * @extends ErrorHandler
+ */
+@Injectable()
+export class GlobalErrorHandler extends ErrorHandler {
+ /**
+ * An array of sentence sections that denote warnings.
+ */
+ private sentencesForWarningLogging: Array<string> = [];
+
+ /**
+ * Initialization of the Global Error Handler.
+ *
+ * @param loggingService - A service that provides logging capabilities.
+ * @param router - A service that provides navigation among views and URL manipulation capabilities.
+ */
+ constructor(private loggingService: LoggingService, private router: Router) {
+ super();
+ }
+
+ /**
+ * Handles different types of errors.
+ *
+ * @param error - An error objects thrown when a runtime errors occurs.
+ */
+ 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;
+ }
+
+ /**
+ * Checks if an error is of type warning.
+ *
+ * @param errorTraceString - A description of the error and it's stack trace.
+ * @returns true - If the error is of type warning.
+ */
+ 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;
+ }
+
+ /**
+ * Write appropriate logs according to the type of error.
+ *
+ * @param error - An error objects thrown when a runtime errors occurs.
+ */
+ 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 }
+ );
+ }
+ }
+}
+
+export function rejectBody(error): { status: any; statusText: any } {
+ return {
+ status: error.status,
+ statusText: error.statusText,
+ };
+}
+
+ +
+ src/app/_pgp/pgp-key-store.ts
+
+
+
Provides a keyring for pgp keys.
+ + + + ++
+ MutableKeyStore
+
+ Methods+ |
+
+
|
+
+ + + + clearKeysInKeyring + + + + | +
+clearKeysInKeyring()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:167
+ |
+
+ Remove all keys from the keyring. +
+ Returns :
+ void
+
+ |
+
+ + + + getEncryptKeys + + + + | +
+getEncryptKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:178
+ |
+
+ Get all the encryption keys. +
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of encryption keys. + + |
+
+ + + + getFingerprint + + + + | +
+getFingerprint()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:186
+ |
+
+ Get the first private key's fingerprint. +
+
+
+ Returns :
+ string
+
+
+
+ The first private key's fingerprint. + + |
+
+ + + + getKeyId + + + + | +||||||||
+getKeyId(key: any)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:201
+ |
+ ||||||||
+ Get a key's keyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ string
+
+
+
+ The key's keyId. + + |
+
+ + + + getKeysForId + + + + | +||||||||
+getKeysForId(keyId: string)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:210
+ |
+ ||||||||
+ Get keys from the keyring using their keyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of the keys with that keyId. + + |
+
+ + + + getPrivateKey + + + + | +
+getPrivateKey()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:218
+ |
+
+ Get the first private key. +
+
+
+ Returns :
+ any
+
+
+
+ The first private key. + + |
+
+ + + + getPrivateKeyForId + + + + | +||||||
+getPrivateKeyForId(keyId)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:227
+ |
+ ||||||
+ Get a private key from the keyring using it's keyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+ The private key with that keyId. + + |
+
+ + + + getPrivateKeyId + + + + | +
+getPrivateKeyId()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:235
+ |
+
+ Get the first private key's keyID. +
+
+
+ Returns :
+ string
+
+
+
+ The first private key's keyId. + + |
+
+ + + + getPrivateKeys + + + + | +
+getPrivateKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:248
+ |
+
+ Get all private keys. +
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of all private keys. + + |
+
+ + + + getPublicKeyForId + + + + | +||||||
+getPublicKeyForId(keyId)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:257
+ |
+ ||||||
+ Get a public key from the keyring using it's keyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+ The public key with that keyId. + + |
+
+ + + + getPublicKeyForSubkeyId + + + + | +||||||
+getPublicKeyForSubkeyId(subkeyId)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:266
+ |
+ ||||||
+ Get a public key from the keyring using it's subkeyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+ The public key with that subkeyId. + + |
+
+ + + + getPublicKeys + + + + | +
+getPublicKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:274
+ |
+
+ Get all the public keys. +
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of public keys. + + |
+
+ + + + getPublicKeysForAddress + + + + | +||||||
+getPublicKeysForAddress(address)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:283
+ |
+ ||||||
+ Get public keys from the keyring using their address. +
+ Parameters :
+
+
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of the public keys with that address. + + |
+
+ + + + getTrustedActiveKeys + + + + | +
+getTrustedActiveKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:291
+ |
+
+ Get all the trusted active keys. +
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of trusted active keys. + + |
+
+ + + + getTrustedKeys + + + + | +
+getTrustedKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:299
+ |
+
+ Get all the trusted keys. +
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of trusted keys. + + |
+
+ + + + Async + importKeyPair + + + + | +||||||||||||
+
+ importKeyPair(publicKey: any, privateKey: any)
+ |
+ ||||||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:310
+ |
+ ||||||||||||
+ Add a key pair to keyring. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + Async + importPrivateKey + + + + | +||||||||
+
+ importPrivateKey(privateKey: any)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:325
+ |
+ ||||||||
+ Add private key to keyring. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + Async + importPublicKey + + + + | +||||||||
+
+ importPublicKey(publicKey: any)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:339
+ |
+ ||||||||
+ Add public key to keyring. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + Async + isEncryptedPrivateKey + + + + | +||||||||
+
+ isEncryptedPrivateKey(privateKey: any)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:353
+ |
+ ||||||||
+ Verify that a private key is encrypted. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+ true - If private key is encrypted. + + |
+
+ + + + Async + isValidKey + + + + | +||||||
+
+ isValidKey(key)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:369
+ |
+ ||||||
+ Test if the input is a valid key. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+ true - If the input is a valid key. + + |
+
+ + + + Async + loadKeyring + + + + | +
+
+ loadKeyring()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:379
+ |
+
+ Instantiate the keyring in the keystore. +
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + removeKeysForId + + + + | +||||||
+removeKeysForId(keyId)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:389
+ |
+ ||||||
+ Remove a public key from the keyring using it's keyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of the removed keys. + + |
+
+ + + + removePublicKey + + + + | +||||||||
+removePublicKey(publicKey: any)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:398
+ |
+ ||||||||
+ Remove a public key from the keyring. +
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+ The removed public key. + + |
+
+ + + + removePublicKeyForId + + + + | +||||||
+removePublicKeyForId(keyId)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:408
+ |
+ ||||||
+ Remove a public key from the keyring using it's keyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+ The removed public key. + + |
+
+ + + + Async + sign + + + + | +||||||
+
+ sign(plainText)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:418
+ |
+ ||||||
+ Sign message using private key. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<any>
+
+
+
+ The generated signature. + + |
+
import { KeyStore } from 'cic-client-meta';
+// TODO should we put this on the mutable key store object
+import * as openpgp from 'openpgp';
+
+/** An openpgp Keyring instance. */
+const keyring = new openpgp.Keyring();
+
+/**
+ * Mutable Key store interface.
+ *
+ * @extends KeyStore
+ */
+interface MutableKeyStore extends KeyStore {
+ /** Remove all keys from the keyring. */
+ clearKeysInKeyring(): void;
+ /**
+ * Get all the encryption keys.
+ * @returns An array of encryption keys.
+ * @remarks
+ * Current implementation doesn't include encryption keys.
+ * This is included to appease the implemented Keystore interface.
+ */
+ getEncryptKeys(): Array<any>;
+ /**
+ * Get the first private key's fingerprint.
+ * @returns The first private key's fingerprint.
+ */
+ getFingerprint(): string;
+ /**
+ * Get a key's keyId.
+ * @param key - The key to fetch the keyId from.
+ * @returns The key's keyId.
+ */
+ getKeyId(key: any): string;
+ /**
+ * Get keys from the keyring using their keyId.
+ * @param keyId - The keyId of the keys to be fetched from the keyring.
+ * @returns An array of the keys with that keyId.
+ */
+ getKeysForId(keyId: string): Array<any>;
+ /**
+ * Get the first private key.
+ * @returns The first private key.
+ */
+ getPrivateKey(): any;
+ /**
+ * Get a private key from the keyring using it's keyId.
+ * @param keyId - The keyId of the private key to be fetched from the keyring.
+ * @returns The private key with that keyId.
+ */
+ getPrivateKeyForId(keyId: string): any;
+ /**
+ * Get the first private key's keyID.
+ * @returns The first private key's keyId.
+ */
+ getPrivateKeyId(): string;
+ /**
+ * Get all private keys.
+ * @returns An array of all private keys.
+ */
+ getPrivateKeys(): Array<any>;
+ /**
+ * Get a public key from the keyring using it's keyId.
+ * @param keyId - The keyId of the public key to be fetched from the keyring.
+ * @returns The public key with that keyId.
+ */
+ getPublicKeyForId(keyId: string): any;
+ /**
+ * Get a public key from the keyring using it's subkeyId.
+ * @param subkeyId - The subkeyId of the public key to be fetched from the keyring.
+ * @returns The public key with that subkeyId.
+ */
+ getPublicKeyForSubkeyId(subkeyId: string): any;
+ /**
+ * Get all the public keys.
+ * @returns An array of public keys.
+ */
+ getPublicKeys(): Array<any>;
+ /**
+ * Get public keys from the keyring using their address.
+ * @param address - The address of the public keys to be fetched from the keyring.
+ * @returns An array of the public keys with that address.
+ */
+ getPublicKeysForAddress(address: string): Array<any>;
+ /**
+ * Get all the trusted active keys.
+ * @returns An array of trusted active keys.
+ */
+ getTrustedActiveKeys(): Array<any>;
+ /**
+ * Get all the trusted keys.
+ * @returns An array of trusted keys.
+ */
+ getTrustedKeys(): Array<any>;
+ /**
+ * Add a key pair to keyring.
+ * @async
+ * @param publicKey - The public key to be added to the keyring.
+ * @param privateKey - The private key to be added to the keyring.
+ * @throws Error
+ */
+ importKeyPair(publicKey: any, privateKey: any): Promise<void>;
+ /**
+ * Add private key to keyring.
+ * @async
+ * @param privateKey - The private key to be added to the keyring.
+ * @throws Error
+ */
+ importPrivateKey(privateKey: any): Promise<void>;
+ /**
+ * Add public key to keyring.
+ * @async
+ * @param publicKey - The public key to be added to the keyring.
+ * @throws Error
+ */
+ importPublicKey(publicKey: any): Promise<void>;
+ /**
+ * Verify that a private key is encrypted.
+ * @async
+ * @param privateKey - The private key to verify.
+ * @returns true - If private key is encrypted.
+ */
+ isEncryptedPrivateKey(privateKey: any): Promise<boolean>;
+ /**
+ * Test if the input is a valid key.
+ * @async
+ * @param key - The input to be validated.
+ * @returns true - If the input is a valid key.
+ */
+ isValidKey(key: any): Promise<boolean>;
+ /**
+ * Instantiate the keyring in the keystore.
+ * @async
+ */
+ loadKeyring(): void;
+ /**
+ * Remove a public key from the keyring using it's keyId.
+ * @param keyId - The keyId of the keys to be removed from the keyring.
+ * @returns An array of the removed keys.
+ */
+ removeKeysForId(keyId: string): Array<any>;
+ /**
+ * Remove a public key from the keyring.
+ * @param publicKey - The public key to be removed from the keyring.
+ * @returns The removed public key.
+ */
+ removePublicKey(publicKey: any): any;
+ /**
+ * Remove a public key from the keyring using it's keyId.
+ * @param keyId - The keyId of the public key to be removed from the keyring.
+ * @returns The removed public key.
+ */
+ removePublicKeyForId(keyId: string): any;
+ /**
+ * Sign message using private key.
+ * @async
+ * @param plainText - The message to be signed.
+ * @returns The generated signature.
+ */
+ sign(plainText: string): Promise<any>;
+}
+
+/** Provides a keyring for pgp keys. */
+class MutablePgpKeyStore implements MutableKeyStore {
+ /** Remove all keys from the keyring. */
+ clearKeysInKeyring(): void {
+ keyring.clear();
+ }
+
+ /**
+ * Get all the encryption keys.
+ * @returns An array of encryption keys.
+ * @remarks
+ * Current implementation doesn't include encryption keys.
+ * This is included to appease the implemented Keystore interface.
+ */
+ getEncryptKeys(): Array<any> {
+ return [];
+ }
+
+ /**
+ * Get the first private key's fingerprint.
+ * @returns The first private key's fingerprint.
+ */
+ getFingerprint(): string {
+ // TODO Handle multiple keys
+ return (
+ keyring.privateKeys &&
+ keyring.privateKeys.keys[0] &&
+ keyring.privateKeys.keys[0].keyPacket &&
+ keyring.privateKeys.keys[0].keyPacket.fingerprint
+ );
+ }
+
+ /**
+ * Get a key's keyId.
+ * @param key - The key to fetch the keyId from.
+ * @returns The key's keyId.
+ */
+ getKeyId(key: any): string {
+ return key.getKeyId().toHex();
+ }
+
+ /**
+ * Get keys from the keyring using their keyId.
+ * @param keyId - The keyId of the keys to be fetched from the keyring.
+ * @returns An array of the keys with that keyId.
+ */
+ getKeysForId(keyId: string): Array<any> {
+ return keyring.getKeysForId(keyId);
+ }
+
+ /**
+ * Get the first private key.
+ * @returns The first private key.
+ */
+ getPrivateKey(): any {
+ return keyring.privateKeys && keyring.privateKeys.keys[0];
+ }
+
+ /**
+ * Get a private key from the keyring using it's keyId.
+ * @param keyId - The keyId of the private key to be fetched from the keyring.
+ * @returns The private key with that keyId.
+ */
+ getPrivateKeyForId(keyId): any {
+ return keyring.privateKeys && keyring.privateKeys.getForId(keyId);
+ }
+
+ /**
+ * Get the first private key's keyID.
+ * @returns The first private key's keyId.
+ */
+ 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()
+ );
+ }
+
+ /**
+ * Get all private keys.
+ * @returns An array of all private keys.
+ */
+ getPrivateKeys(): Array<any> {
+ return keyring.privateKeys && keyring.privateKeys.keys;
+ }
+
+ /**
+ * Get a public key from the keyring using it's keyId.
+ * @param keyId - The keyId of the public key to be fetched from the keyring.
+ * @returns The public key with that keyId.
+ */
+ getPublicKeyForId(keyId): any {
+ return keyring.publicKeys && keyring.publicKeys.getForId(keyId);
+ }
+
+ /**
+ * Get a public key from the keyring using it's subkeyId.
+ * @param subkeyId - The subkeyId of the public key to be fetched from the keyring.
+ * @returns The public key with that subkeyId.
+ */
+ getPublicKeyForSubkeyId(subkeyId): any {
+ return keyring.publicKeys && keyring.publicKeys.getForId(subkeyId, true);
+ }
+
+ /**
+ * Get all the public keys.
+ * @returns An array of public keys.
+ */
+ getPublicKeys(): Array<any> {
+ return keyring.publicKeys && keyring.publicKeys.keys;
+ }
+
+ /**
+ * Get public keys from the keyring using their address.
+ * @param address - The address of the public keys to be fetched from the keyring.
+ * @returns An array of the public keys with that address.
+ */
+ getPublicKeysForAddress(address): Array<any> {
+ return keyring.publicKeys && keyring.publicKeys.getForAddress(address);
+ }
+
+ /**
+ * Get all the trusted active keys.
+ * @returns An array of trusted active keys.
+ */
+ getTrustedActiveKeys(): Array<any> {
+ return keyring.publicKeys && keyring.publicKeys.keys;
+ }
+
+ /**
+ * Get all the trusted keys.
+ * @returns An array of trusted keys.
+ */
+ getTrustedKeys(): Array<any> {
+ return keyring.publicKeys && keyring.publicKeys.keys;
+ }
+
+ /**
+ * Add a key pair to keyring.
+ * @async
+ * @param publicKey - The public key to be added to the keyring.
+ * @param privateKey - The private key to be added to the keyring.
+ * @throws Error
+ */
+ async importKeyPair(publicKey: any, privateKey: any): Promise<void> {
+ try {
+ await keyring.publicKeys.importKey(publicKey);
+ await keyring.privateKeys.importKey(privateKey);
+ } catch (error) {
+ throw error;
+ }
+ }
+
+ /**
+ * Add private key to keyring.
+ * @async
+ * @param privateKey - The private key to be added to the keyring.
+ * @throws Error
+ */
+ async importPrivateKey(privateKey: any): Promise<void> {
+ try {
+ await keyring.privateKeys.importKey(privateKey);
+ } catch (error) {
+ throw error;
+ }
+ }
+
+ /**
+ * Add public key to keyring.
+ * @async
+ * @param publicKey - The public key to be added to the keyring.
+ * @throws Error
+ */
+ async importPublicKey(publicKey: any): Promise<void> {
+ try {
+ await keyring.publicKeys.importKey(publicKey);
+ } catch (error) {
+ throw error;
+ }
+ }
+
+ /**
+ * Verify that a private key is encrypted.
+ * @async
+ * @param privateKey - The private key to verify.
+ * @returns true - If private key is encrypted.
+ */
+ 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;
+ }
+
+ /**
+ * Test if the input is a valid key.
+ * @async
+ * @param key - The input to be validated.
+ * @returns true - If the input is a valid key.
+ */
+ async isValidKey(key): Promise<boolean> {
+ // There is supposed to be an openpgp.readKey() method but I can't find it?
+ const testKey = await openpgp.key.readArmored(key);
+ return !testKey.err;
+ }
+
+ /**
+ * Instantiate the keyring in the keystore.
+ * @async
+ */
+ async loadKeyring(): Promise<void> {
+ await keyring.load();
+ await keyring.store();
+ }
+
+ /**
+ * Remove a public key from the keyring using it's keyId.
+ * @param keyId - The keyId of the keys to be removed from the keyring.
+ * @returns An array of the removed keys.
+ */
+ removeKeysForId(keyId): Array<any> {
+ return keyring.removeKeysForId(keyId);
+ }
+
+ /**
+ * Remove a public key from the keyring.
+ * @param publicKey - The public key to be removed from the keyring.
+ * @returns The removed public key.
+ */
+ removePublicKey(publicKey: any): any {
+ const keyId = publicKey.getKeyId().toHex();
+ return keyring.publicKeys && keyring.publicKeys.removeForId(keyId);
+ }
+
+ /**
+ * Remove a public key from the keyring using it's keyId.
+ * @param keyId - The keyId of the public key to be removed from the keyring.
+ * @returns The removed public key.
+ */
+ removePublicKeyForId(keyId): any {
+ return keyring.publicKeys && keyring.publicKeys.removeForId(keyId);
+ }
+
+ /**
+ * Sign message using private key.
+ * @async
+ * @param plainText - The message to be signed.
+ * @returns The generated signature.
+ */
+ 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;
+ }
+}
+
+/** @exports */
+export { MutableKeyStore, MutablePgpKeyStore };
+
+ +
+ src/app/_pgp/pgp-signer.ts
+
+
+
Provides functionality for signing and verifying signed messages.
+ + + + ++
+ Signer
+
+ Properties+ |
+
+ + | +
+ Methods+ |
+
+
|
+
+constructor(keyStore: MutableKeyStore)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-signer.ts:74
+ |
+ ||||||||
+ Initializing the Signer. +
+ Parameters :
+
+
|
+
+ + + + algo + + + | +
+ Type : string
+
+ |
+
+ Default value : 'sha256'
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:60
+ |
+
+ Encryption algorithm used + |
+
+ + + + dgst + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:62
+ |
+
+ Message digest + |
+
+ + + + engine + + + | +
+ Type : string
+
+ |
+
+ Default value : 'pgp'
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:64
+ |
+
+ Encryption engine used. + |
+
+ + + + keyStore + + + | +
+ Type : MutableKeyStore
+
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:66
+ |
+
+ A keystore holding pgp keys. + |
+
+ + + + loggingService + + + | +
+ Type : LoggingService
+
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:68
+ |
+
+ A service that provides logging capabilities. + |
+
+ + + + onsign + + + | +
+ Type : function
+
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:70
+ |
+
+ Event triggered on successful signing of message. + |
+
+ + + + onverify + + + | +
+ Type : function
+
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:72
+ |
+
+ Event triggered on successful verification of a signature. + |
+
+ + + + signature + + + | +
+ Type : Signature
+
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:74
+ |
+
+ Generated signature + |
+
+ + + + Public + fingerprint + + + + | +
+
+ fingerprint()
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:90
+ |
+
+ Get the private key fingerprint. +
+
+
+ Returns :
+ string
+
+
+
+ A private key fingerprint. + + |
+
+ + + + Public + prepare + + + + | +||||||||
+
+ prepare(material: Signable)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-signer.ts:99
+ |
+ ||||||||
+ Load the message digest. +
+ Parameters :
+
+
+
+
+ Returns :
+ boolean
+
+
+
+ true - If digest has been loaded successfully. + + |
+
+ + + + Public + Async + sign + + + + | +||||||||
+
+ sign(digest: string)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-signer.ts:109
+ |
+ ||||||||
+ Signs a message using a private key. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + Public + verify + + + + | +||||||||||||
+
+ verify(digest: string, signature: Signature)
+ |
+ ||||||||||||
+ Defined in src/app/_pgp/pgp-signer.ts:144
+ |
+ ||||||||||||
+ Verify that signature is valid. +
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
import * as openpgp from 'openpgp';
+
+// Application imports
+import { MutableKeyStore } from '@app/_pgp/pgp-key-store';
+import { LoggingService } from '@app/_services/logging.service';
+
+/** Signable object interface */
+interface Signable {
+ /** The message to be signed. */
+ digest(): string;
+}
+
+/** Signature object interface */
+interface Signature {
+ /** Encryption algorithm used */
+ algo: string;
+ /** Data to be signed. */
+ data: string;
+ /** Message digest */
+ digest: string;
+ /** Encryption engine used. */
+ engine: string;
+}
+
+/** Signer interface */
+interface Signer {
+ /**
+ * Get the private key fingerprint.
+ * @returns A private key fingerprint.
+ */
+ fingerprint(): string;
+ /** Event triggered on successful signing of message. */
+ onsign(signature: Signature): void;
+ /** Event triggered on successful verification of a signature. */
+ onverify(flag: boolean): void;
+ /**
+ * Load the message digest.
+ * @param material - A signable object.
+ * @returns true - If digest has been loaded successfully.
+ */
+ prepare(material: Signable): boolean;
+ /**
+ * Signs a message using a private key.
+ * @async
+ * @param digest - The message to be signed.
+ */
+ sign(digest: string): Promise<void>;
+ /**
+ * Verify that signature is valid.
+ * @param digest - The message that was signed.
+ * @param signature - The generated signature.
+ */
+ verify(digest: string, signature: Signature): void;
+}
+
+/** Provides functionality for signing and verifying signed messages. */
+class PGPSigner implements Signer {
+ /** Encryption algorithm used */
+ algo = 'sha256';
+ /** Message digest */
+ dgst: string;
+ /** Encryption engine used. */
+ engine = 'pgp';
+ /** A keystore holding pgp keys. */
+ keyStore: MutableKeyStore;
+ /** A service that provides logging capabilities. */
+ loggingService: LoggingService;
+ /** Event triggered on successful signing of message. */
+ onsign: (signature: Signature) => void;
+ /** Event triggered on successful verification of a signature. */
+ onverify: (flag: boolean) => void;
+ /** Generated signature */
+ signature: Signature;
+
+ /**
+ * Initializing the Signer.
+ * @param keyStore - A keystore holding pgp keys.
+ */
+ constructor(keyStore: MutableKeyStore) {
+ this.keyStore = keyStore;
+ this.onsign = (signature: Signature) => {};
+ this.onverify = (flag: boolean) => {};
+ }
+
+ /**
+ * Get the private key fingerprint.
+ * @returns A private key fingerprint.
+ */
+ public fingerprint(): string {
+ return this.keyStore.getFingerprint();
+ }
+
+ /**
+ * Load the message digest.
+ * @param material - A signable object.
+ * @returns true - If digest has been loaded successfully.
+ */
+ public prepare(material: Signable): boolean {
+ this.dgst = material.digest();
+ return true;
+ }
+
+ /**
+ * Signs a message using a private key.
+ * @async
+ * @param digest - The message to be signed.
+ */
+ 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);
+ });
+ }
+
+ /**
+ * Verify that signature is valid.
+ * @param digest - The message that was signed.
+ * @param signature - The generated signature.
+ */
+ 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);
+ });
+ }
+}
+
+/** @exports */
+export { PGPSigner, Signable, Signature, Signer };
+
+ +
+ src/app/_models/settings.ts
+
+
+
Settings class
+ + + + + + +
+ Properties+ |
+
+
|
+
+constructor(scanFilter: any)
+ |
+ ||||||||
+ Defined in src/app/_models/settings.ts:13
+ |
+ ||||||||
+ Initialize the settings. +
+ Parameters :
+
+
|
+
+ + + + registry + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_models/settings.ts:4
+ |
+
+ CIC Registry instance + |
+
+ + + + scanFilter + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_models/settings.ts:6
+ |
+
+ A resource for searching through blocks on the blockchain network. + |
+
+ + + + txHelper + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_models/settings.ts:8
+ |
+
+ Transaction Helper instance + |
+
+ + + + w3 + + + | +
+ Type : W3
+
+ |
+
+ Default value : {
+ engine: undefined,
+ provider: undefined,
+ }
+ |
+
+ Defined in src/app/_models/settings.ts:10
+ |
+
+ Web3 Object + |
+
class Settings {
+ /** CIC Registry instance */
+ registry: any;
+ /** A resource for searching through blocks on the blockchain network. */
+ scanFilter: any;
+ /** Transaction Helper instance */
+ txHelper: any;
+ /** Web3 Object */
+ w3: W3 = {
+ engine: undefined,
+ provider: undefined,
+ };
+
+ /**
+ * Initialize the settings.
+ *
+ * @param scanFilter - A resource for searching through blocks on the blockchain network.
+ */
+ constructor(scanFilter: any) {
+ this.scanFilter = scanFilter;
+ }
+}
+
+/** Web3 object interface */
+interface W3 {
+ /** An active web3 instance connected to the blockchain network. */
+ engine: any;
+ /** The connection socket to the blockchain network. */
+ provider: any;
+}
+
+/** @exports */
+export { Settings, W3 };
+
+ +
+ src/app/_eth/token-registry.ts
+
+
+
Provides an instance of the token registry contract. +Allows querying of tokens that have been registered as valid tokens in the network.
+ + + + + ++
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(contractAddress: string, signerAddress?: string)
+ |
+ ||||||||||||
+ Defined in src/app/_eth/token-registry.ts:26
+ |
+ ||||||||||||
+ Create a connection to the deployed token registry contract. +
+ Parameters :
+
+
|
+
+ + + + contract + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_eth/token-registry.ts:22
+ |
+
+ The instance of the token registry contract. + |
+
+ + + + contractAddress + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_eth/token-registry.ts:24
+ |
+
+ The deployed token registry contract's address. + |
+
+ + + + signerAddress + + + | +
+ Type : string
+
+ |
+
+ Defined in src/app/_eth/token-registry.ts:26
+ |
+
+ The account address of the account that deployed the token registry contract. + |
+
+ + + + Public + Async + addressOf + + + + | +||||||||
+
+ addressOf(identifier: string)
+ |
+ ||||||||
+ Defined in src/app/_eth/token-registry.ts:57
+ |
+ ||||||||
+ Returns the address of the token with a given identifier. +
+ Parameters :
+
+
+ Example :
+
+
+
+
+
+ Returns :
+ Promise<string>
+
+
+
+ The address of the token assigned the specified identifier in the token registry. + + |
+
+ + + + Public + Async + entry + + + + | +||||||||
+
+ entry(serial: number)
+ |
+ ||||||||
+ Defined in src/app/_eth/token-registry.ts:75
+ |
+ ||||||||
+ Returns the address of a token with the given serial in the token registry. +
+ Parameters :
+
+
+ Example :
+
+
+
+
+
+ Returns :
+ Promise<string>
+
+
+
+ The address of the token with the specified serial number. + + |
+
+ + + + Public + Async + totalTokens + + + + | +
+
+ totalTokens()
+ |
+
+ Defined in src/app/_eth/token-registry.ts:91
+ |
+
+ Returns the total number of tokens that have been registered in the network. +
+ Example :
+
+
+
+
+
+ Returns :
+ Promise<number>
+
+
+
+ The total number of registered tokens. + + |
+
import Web3 from 'web3';
+
+// Application imports
+import { environment } from '@src/environments/environment';
+import { Web3Service } from '@app/_services/web3.service';
+
+/** Fetch the token registry contract's ABI. */
+const abi: Array<any> = require('@src/assets/js/block-sync/data/TokenUniqueSymbolIndex.json');
+/** Establish a connection to the blockchain network. */
+const web3: Web3 = Web3Service.getInstance();
+
+/**
+ * Provides an instance of the token registry contract.
+ * Allows querying of tokens that have been registered as valid tokens in the network.
+ *
+ * @remarks
+ * This is our interface to the token registry contract.
+ */
+export class TokenRegistry {
+ /** The instance of the token registry contract. */
+ contract: any;
+ /** The deployed token registry contract's address. */
+ contractAddress: string;
+ /** The account address of the account that deployed the token registry contract. */
+ signerAddress: string;
+
+ /**
+ * Create a connection to the deployed token registry contract.
+ *
+ * @param contractAddress - The deployed token registry contract's address.
+ * @param signerAddress - The account address of the account that deployed the token registry contract.
+ */
+ 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];
+ }
+ }
+
+ /**
+ * Returns the address of the token with a given identifier.
+ *
+ * @async
+ * @example
+ * Prints the address of the token with the identifier 'sarafu':
+ * ```typescript
+ * console.log(await addressOf('sarafu'));
+ * ```
+ *
+ * @param identifier - The name or identifier of the token to be fetched from the token registry.
+ * @returns The address of the token assigned the specified identifier in the token registry.
+ */
+ 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();
+ }
+
+ /**
+ * Returns the address of a token with the given serial in the token registry.
+ *
+ * @async
+ * @example
+ * Prints the address of the token with the serial '2':
+ * ```typescript
+ * console.log(await entry(2));
+ * ```
+ *
+ * @param serial - The serial number of the token to be fetched.
+ * @return The address of the token with the specified serial number.
+ */
+ public async entry(serial: number): Promise<string> {
+ return await this.contract.methods.entry(serial).call();
+ }
+
+ /**
+ * Returns the total number of tokens that have been registered in the network.
+ *
+ * @async
+ * @example
+ * Prints the total number of registered tokens:
+ * ```typescript
+ * console.log(await totalTokens());
+ * ```
+ *
+ * @returns The total number of registered tokens.
+ */
+ public async totalTokens(): Promise<number> {
+ return await this.contract.methods.entryCount().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
+
+ |
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<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
+ |
+
+ + | +
+ + + + tokenSymbol + + + | +
+ Type : string
+
+ |
+
+ + | +
+ + + + 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;
+ tokenSymbol: 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.route.paramMap.subscribe((params: Params) => {
+ this.accountAddress = add0x(params.get('id'));
+ this.bloxbergLink =
+ 'https://blockexplorer.bloxberg.org/address/' + this.accountAddress + '/transactions';
+ });
+ }
+
+ async ngOnInit(): Promise<void> {
+ this.accountInfoForm = this.formBuilder.group({
+ firstName: ['', Validators.required],
+ lastName: ['', 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],
+ });
+ await this.blockSyncService.init();
+ await this.tokenService.init();
+ await this.transactionService.init();
+ await this.userService.init();
+ await this.blockSyncService.blockSync(this.accountAddress);
+ this.userService.resetAccountsList();
+ (await this.userService.getAccountByAddress(this.accountAddress, 100)).subscribe(
+ async (res) => {
+ if (res !== undefined) {
+ this.account = res;
+ this.cdr.detectChanges();
+ this.loggingService.sendInfoLevelMessage(this.account);
+ const fullName = this.account.vcard?.fn[0].value.split(' ');
+ this.accountInfoForm.patchValue({
+ firstName: fullName[0].split(',')[0],
+ lastName: fullName.slice(1).join(' '),
+ 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 ||
+ this.userService.getCategoryByProduct(this.account.products[0]),
+ userLocation: this.account.location.area_name,
+ location:
+ this.account.location.area ||
+ this.locationService
+ .getAreaNameByLocation(this.account.location.area_name)
+ .pipe(first())
+ .subscribe((response) => {
+ return response;
+ }),
+ locationType:
+ this.account.location.area_type ||
+ this.locationService
+ .getAreaTypeByArea(this.accountInfoFormStub.location.value)
+ .pipe(first())
+ .subscribe((response) => {
+ return response;
+ }),
+ });
+ this.userService
+ .getAccountStatus(this.account.vcard?.tel[0].value)
+ .pipe(first())
+ .subscribe((response) => (this.accountStatus = response.status));
+ } else {
+ alert('Account not found!');
+ }
+ }
+ );
+ 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.cdr.detectChanges();
+ });
+
+ this.transactionService.transactionsSubject.subscribe((transactions) => {
+ this.transactionsDataSource = new MatTableDataSource<any>(transactions);
+ this.transactionsDataSource.paginator = this.transactionTablePaginator;
+ this.transactionsDataSource.sort = this.transactionTableSort;
+ this.transactions = transactions;
+ this.cdr.detectChanges();
+ });
+ 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));
+ this.tokenService.load.subscribe(async (status: boolean) => {
+ if (status) {
+ this.tokenSymbol = await this.tokenService.getTokenSymbol();
+ }
+ });
+ }
+
+ 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.firstName.value + ', ' + this.accountInfoFormStub.lastName.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}} {{ tokenSymbol | uppercase }}</span>
+ <span class="ml-2"><strong>Created:</strong> {{account?.date_registered | unixDate}}</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>First Name: *</mat-label>
+ <input matInput type="text" id="firstName" placeholder="{{account?.vcard?.fn[0].value.split(' ')[0]}}"
+ value="{{account?.vcard?.fn[0].value.split(' ')[0]}}" formControlName="firstName" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && accountInfoFormStub.firstName.errors">First Name is required.</mat-error>
+ </mat-form-field>
+ </div>
+
+ <div class="col-md-6 col-lg-4">
+ <mat-form-field appearance="outline">
+ <mat-label>Last Name(s): *</mat-label>
+ <input matInput type="text" id="lastName" placeholder="{{account?.vcard?.fn[0].value.split(' ').slice(1).join(' ')}}"
+ value="{{account?.vcard?.fn[0].value.split(' ').slice(1).join(' ')}}" formControlName="lastName" [errorStateMatcher]="matcher">
+ <mat-error *ngIf="submitted && accountInfoFormStub.lastName.errors">Last 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}} {{ tokenSymbol | uppercase }}</td>
+ <td>{{account?.date_registered | unixDate}}</td>
+ <td>
+ <span class="badge badge-success 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" (closeWindow)="transaction = $event"></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>
+
+ <table 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 || transaction.from}} </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 || transaction.to}} </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}} {{ tokenSymbol | uppercase }}</span>
+ <span *ngIf="transaction.type == 'conversion'">{{transaction?.toValue | tokenRatio}} {{ tokenSymbol | uppercase }}</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 | unixDate}} </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="transactionsDisplayedColumns"></tr>
+ <tr mat-row *matRowDef="let transaction; columns: transactionsDisplayedColumns" matRipple
+ (click)="viewTransaction(transaction)"></tr>
+ </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 | unixDate}} </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}} {{tokenSymbol | uppercase}} </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 :
+
+
|
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<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
+ ) {}
+
+ async ngOnInit(): Promise<void> {
+ await this.userService.init();
+ 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
+
+ |
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<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 ngOnInit(): Promise<void> {
+ await this.userService.init();
+ 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.accountsSubject.subscribe((accounts) => {
+ this.dataSource = new MatTableDataSource<any>(accounts);
+ this.dataSource.paginator = this.paginator;
+ this.dataSource.sort = this.sort;
+ this.accounts = accounts;
+ });
+ this.userService
+ .getAccountTypes()
+ .pipe(first())
+ .subscribe((res) => (this.accountTypes = res));
+ }
+
+ 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 | unixDate}} </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:50
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ string
+
+
+
+
+ |
+
+ + + + approveAction + + + + | +||||||
+approveAction(action: any)
+ |
+ ||||||
+ Defined in src/app/pages/admin/admin.component.ts:54
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + disapproveAction + + + + | +||||||
+disapproveAction(action: any)
+ |
+ ||||||
+ Defined in src/app/pages/admin/admin.component.ts:65
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + doFilter + + + + | +||||||
+doFilter(value: string)
+ |
+ ||||||
+ Defined in src/app/pages/admin/admin.component.ts:46
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + downloadCsv + + + + | +
+downloadCsv()
+ |
+
+ Defined in src/app/pages/admin/admin.component.ts:80
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + expandCollapse + + + + | +||||
+expandCollapse(row)
+ |
+ ||||
+ Defined in src/app/pages/admin/admin.component.ts:76
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ Defined in src/app/pages/admin/admin.component.ts:35
+ |
+
+
+
+ Returns :
+ Promise<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) {}
+
+ async ngOnInit(): Promise<void> {
+ await this.userService.init();
+ 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;
+ });
+ }
+
+ 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
+
+
+ OnInit
+
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, swUpdate: SwUpdate)
+ |
+ ||||||||||||||||||
+ Defined in src/app/app.component.ts:21
+ |
+ ||||||||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + window:cic_convert + + + + | +
+ Arguments : '$event'
+ |
+
+window:cic_convert(event: CustomEvent)
+ |
+
+ Defined in src/app/app.component.ts:88
+ |
+
+ + + + window:cic_transfer + + + + | +
+ Arguments : '$event'
+ |
+
+window:cic_transfer(event: CustomEvent)
+ |
+
+ Defined in src/app/app.component.ts:82
+ |
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ Defined in src/app/app.component.ts:34
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + onResize + + + + | +||||
+onResize(e)
+ |
+ ||||
+ Defined in src/app/app.component.ts:57
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + mediaQuery + + + | +
+ Type : MediaQueryList
+
+ |
+
+ Default value : window.matchMedia('(max-width: 768px)')
+ |
+
+ Defined in src/app/app.component.ts:21
+ |
+
+ + + + readyState + + + | +
+ Type : number
+
+ |
+
+ Default value : 0
+ |
+
+ Defined in src/app/app.component.ts:20
+ |
+
+ + + + readyStateTarget + + + | +
+ Type : number
+
+ |
+
+ Default value : 3
+ |
+
+ Defined in src/app/app.component.ts:19
+ |
+
+ + + + title + + + | +
+ Type : string
+
+ |
+
+ Default value : 'CICADA'
+ |
+
+ Defined in src/app/app.component.ts:18
+ |
+
import { ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core';
+import {
+ AuthService,
+ ErrorDialogService,
+ LoggingService,
+ TransactionService,
+} from '@app/_services';
+import { catchError } from 'rxjs/operators';
+import { SwUpdate } from '@angular/service-worker';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: './app.component.html',
+ styleUrls: ['./app.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class AppComponent implements OnInit {
+ 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,
+ private swUpdate: SwUpdate
+ ) {
+ this.mediaQuery.addEventListener('change', this.onResize);
+ this.onResize(this.mediaQuery);
+ }
+
+ async ngOnInit(): Promise<void> {
+ await this.authService.init();
+ await this.transactionService.init();
+ try {
+ const publicKeys = await this.authService.getPublicKeys();
+ await this.authService.mutableKeyStore.importPublicKey(publicKeys);
+ this.authService.getTrustedUsers();
+ } catch (error) {
+ this.errorDialogService.openDialog({
+ message: 'Trusted keys endpoint cannot be reached. Please try again later.',
+ });
+ // TODO do something to halt user progress...show a sad cicada page 🦗?
+ }
+ if (!this.swUpdate.isEnabled) {
+ this.swUpdate.available.subscribe(() => {
+ if (confirm('New Version available. Load New Version?')) {
+ window.location.reload();
+ }
+ });
+ }
+ }
+
+ // 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, errorDialogService: ErrorDialogService)
+ |
+ |||||||||||||||
+ Defined in src/app/auth/auth.component.ts:19
+ |
+ |||||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + Async + login + + + + | +
+
+ login()
+ |
+
+ Defined in src/app/auth/auth.component.ts:53
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ Defined in src/app/auth/auth.component.ts:28
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + Async + onSubmit + + + + | +
+
+ onSubmit()
+ |
+
+ Defined in src/app/auth/auth.component.ts:41
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + switchWindows + + + + | +
+switchWindows()
+ |
+
+ Defined in src/app/auth/auth.component.ts:66
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + toggleDisplay + + + + | +||||||
+toggleDisplay(element: any)
+ |
+ ||||||
+ Defined in src/app/auth/auth.component.ts:73
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + keyForm + + + | +
+ Type : FormGroup
+
+ |
+
+ Defined in src/app/auth/auth.component.ts:16
+ |
+
+ + + + loading + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ Defined in src/app/auth/auth.component.ts:18
+ |
+
+ + + + matcher + + + | +
+ Type : CustomErrorStateMatcher
+
+ |
+
+ Default value : new CustomErrorStateMatcher()
+ |
+
+ Defined in src/app/auth/auth.component.ts:19
+ |
+
+ + + + submitted + + + | +
+ Type : boolean
+
+ |
+
+ Default value : false
+ |
+
+ Defined in src/app/auth/auth.component.ts:17
+ |
+
+ + keyFormStub + | +
+ getkeyFormStub()
+ |
+
+ Defined in src/app/auth/auth.component.ts:37
+ |
+
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 { ErrorDialogService } from '@app/_services/error-dialog.service';
+import { LoggingService } from '@app/_services/logging.service';
+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,
+ private errorDialogService: ErrorDialogService
+ ) {}
+
+ async ngOnInit(): Promise<void> {
+ this.keyForm = this.formBuilder.group({
+ key: ['', Validators.required],
+ });
+ if (this.authService.getPrivateKey()) {
+ this.authService.loginView();
+ }
+ }
+
+ 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;
+ }
+
+ async login(): Promise<void> {
+ try {
+ const loginResult = await this.authService.login();
+ if (loginResult) {
+ this.router.navigate(['/home']);
+ }
+ } catch (HttpError) {
+ this.errorDialogService.openDialog({
+ message: HttpError.message,
+ });
+ }
+ }
+
+ switchWindows(): void {
+ 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';
+ }
+ }
+}
+
+ <app-network-status></app-network-status>
+<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 background-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 :
+
+
|
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<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
+ ) {}
+
+ async ngOnInit(): Promise<void> {
+ await this.userService.init();
+ 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 |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor()
+ |
+
+ Defined in src/app/shared/footer/footer.component.ts:10
+ |
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ Defined in src/app/shared/footer/footer.component.ts:13
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + currentYear + + + | +
+ Default value : new Date().getFullYear()
+ |
+
+ Defined in src/app/shared/footer/footer.component.ts:10
+ |
+
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 {
+ currentYear = new Date().getFullYear();
+ constructor() {}
+
+ ngOnInit(): void {}
+}
+
+ <!-- Footer Start -->
+<footer class="footer">
+ <a target="blank" title="GPL-3" href="https://www.gnu.org/licenses/gpl-3.0.en.html"> Copyleft </a> 🄯.
+ {{ currentYear }}
+ <a target="blank" title="Gitlab@GrassrootsEconomics" href="https://gitlab.com/grassrootseconomics"><u> Grassroots Economics </u></a>
+
+</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/shared/network-status/network-status.component.ts
+
+
+ OnInit
+
changeDetection | +ChangeDetectionStrategy.OnPush |
+
selector | +app-network-status |
+
styleUrls | +./network-status.component.scss |
+
templateUrl | +./network-status.component.html |
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(cdr: ChangeDetectorRef)
+ |
+ ||||||
+ + | +||||||
+
+ Parameters :
+
+
|
+
+ + + + handleNetworkChange + + + + | +
+handleNetworkChange()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + noInternetConnection + + + | +
+ Type : boolean
+
+ |
+
+ Default value : !navigator.onLine
+ |
+
+ + | +
import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
+
+@Component({
+ selector: 'app-network-status',
+ templateUrl: './network-status.component.html',
+ styleUrls: ['./network-status.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class NetworkStatusComponent implements OnInit {
+ noInternetConnection: boolean = !navigator.onLine;
+
+ constructor(private cdr: ChangeDetectorRef) {
+ this.handleNetworkChange();
+ }
+
+ ngOnInit(): void {}
+
+ handleNetworkChange(): void {
+ setTimeout(() => {
+ if (!navigator.onLine !== this.noInternetConnection) {
+ this.noInternetConnection = !navigator.onLine;
+ this.cdr.detectChanges();
+ }
+ this.handleNetworkChange();
+ }, 5000);
+ }
+}
+
+ <nav class="navbar navbar-dark background-dark">
+ <h1 class="navbar-brand">
+ <div *ngIf="noInternetConnection; then offlineBlock else onlineBlock"></div>
+ <ng-template #offlineBlock>
+ <strong style="color: red;">OFFLINE </strong>
+ <img width="20rem" src="assets/images/no-wifi.svg" alt="Internet Disconnected">
+ </ng-template>
+ <ng-template #onlineBlock>
+ <strong style="color: lawngreen;">ONLINE </strong>
+ <img width="20rem" src="assets/images/wifi.svg" alt="Internet Connected">
+ </ng-template>
+ </h1>
+</nav>
+
+
+ ./network-status.component.scss
+
+ +
+ 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
+
+ |
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<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>
+
+ |
+
+ + | +
+ + + + userInfo + + + | +
+ Type : 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>;
+ userInfo: Staff;
+
+ @ViewChild(MatPaginator) paginator: MatPaginator;
+ @ViewChild(MatSort) sort: MatSort;
+
+ constructor(private authService: AuthService) {}
+
+ async ngOnInit(): Promise<void> {
+ await this.authService.init();
+ this.authService.trustedUsersSubject.subscribe((users) => {
+ this.dataSource = new MatTableDataSource<any>(users);
+ this.dataSource.paginator = this.paginator;
+ this.dataSource.sort = this.sort;
+ this.trustedUsers = users;
+ });
+ this.userInfo = this.authService.getPrivateKeyInfo();
+ }
+
+ 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>CICADA Admin Credentials</h4>
+ <span><strong>UserId: </strong> {{ userInfo?.userid }} </span><br>
+ <span><strong>Username: </strong> {{ userInfo?.name }} </span><br>
+ <span><strong>Email: </strong> {{ userInfo?.email }} </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">
+ <app-network-status></app-network-status>
+ <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 |
+
+ Methods+ |
+
+ + | +
+ Inputs+ |
+
+
|
+
+ Outputs+ |
+
+
|
+
+constructor()
+ |
+
+ + | +
+ + token + | +|
+ Type : Token
+
+ |
+ |
+ + | +
+ + closeWindow + | +|
+ Type : EventEmitter<any>
+
+ |
+ |
+ + | +
+ + + + close + + + + | +
+close()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + ngOnInit + + + + | +
+ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
import {
+ ChangeDetectionStrategy,
+ Component,
+ EventEmitter,
+ Input,
+ OnInit,
+ Output,
+} from '@angular/core';
+import { Token } from '@app/_models';
+
+@Component({
+ selector: 'app-token-details',
+ templateUrl: './token-details.component.html',
+ styleUrls: ['./token-details.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class TokenDetailsComponent implements OnInit {
+ @Input() token: Token;
+
+ @Output() closeWindow: EventEmitter<any> = new EventEmitter<any>();
+
+ constructor() {}
+
+ ngOnInit(): void {}
+
+ close(): void {
+ this.token = null;
+ this.closeWindow.emit(this.token);
+ }
+}
+
+ <div *ngIf="token" class="mb-3 mt-1">
+ <div class="card text-center">
+ <mat-card-title class="card-header">
+ <div class="row">
+ TOKEN DETAILS
+ <button mat-raised-button type="button" class="btn btn-outline-secondary ml-auto mr-2" (click)="close()"> CLOSE </button>
+ </div>
+ </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>
+
+
+ ./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:46
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + downloadCsv + + + + | +
+downloadCsv()
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:54
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:30
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + viewToken + + + + | +||||
+viewToken(token)
+ |
+ ||||
+ Defined in src/app/pages/tokens/tokens.component.ts:50
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + columnsToDisplay + + + | +
+ Type : Array<string>
+
+ |
+
+ Default value : ['name', 'symbol', 'address', 'supply']
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:18
+ |
+
+ + + + dataSource + + + | +
+ Type : MatTableDataSource<any>
+
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:17
+ |
+
+ + + + paginator + + + | +
+ Type : MatPaginator
+
+ |
+
+ Decorators :
+ +
+ @ViewChild(MatPaginator)
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:19
+ |
+
+ + + + sort + + + | +
+ Type : MatSort
+
+ |
+
+ Decorators :
+ +
+ @ViewChild(MatSort)
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:20
+ |
+
+ + + + token + + + | +
+ Type : Token
+
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:22
+ |
+
+ + + + tokens + + + | +
+ Type : Array<Token>
+
+ |
+
+ Defined in src/app/pages/tokens/tokens.component.ts:21
+ |
+
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 { Token } from '@app/_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<Token>;
+ token: Token;
+
+ constructor(
+ private tokenService: TokenService,
+ private loggingService: LoggingService,
+ private router: Router
+ ) {}
+
+ async ngOnInit(): Promise<void> {
+ await this.tokenService.init();
+ this.tokenService.load.subscribe(async (status: boolean) => {
+ if (status) {
+ await this.tokenService.getTokens();
+ }
+ });
+ this.tokenService.tokensSubject.subscribe((tokens) => {
+ this.loggingService.sendInfoLevelMessage(tokens);
+ this.dataSource = new MatTableDataSource(tokens);
+ this.dataSource.paginator = this.paginator;
+ this.dataSource.sort = this.sort;
+ this.tokens = tokens;
+ });
+ }
+
+ doFilter(value: string): void {
+ this.dataSource.filter = value.trim().toLocaleLowerCase();
+ }
+
+ viewToken(token): void {
+ this.token = token;
+ }
+
+ 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">
+
+ <app-token-details [token]="token" (closeWindow)="token = $event"></app-token-details>
+
+ <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:12
+ |
+
+
+
+ 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+ |
+
+
|
+
+ Outputs+ |
+
+
|
+
+constructor(router: Router, transactionService: TransactionService, snackBar: MatSnackBar, tokenService: TokenService)
+ |
+ |||||||||||||||
+ + | +|||||||||||||||
+
+ Parameters :
+
+
|
+
+ + transaction + | +|
+ + | +
+ + closeWindow + | +|
+ Type : EventEmitter<any>
+
+ |
+ |
+ + | +
+ + + + close + + + + | +
+close()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + copyAddress + + + + | +||||||
+copyAddress(address: string)
+ |
+ ||||||
+ + | +||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<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
+
+ |
+
+ + | +
+ + + + tokenName + + + | +
+ Type : string
+
+ |
+
+ + | +
+ + + + tokenSymbol + + + | +
+ Type : string
+
+ |
+
+ + | +
+ + + + traderBloxbergLink + + + | +
+ Type : string
+
+ |
+
+ + | +
import {
+ ChangeDetectionStrategy,
+ Component,
+ EventEmitter,
+ Input,
+ OnInit,
+ Output,
+} from '@angular/core';
+import { Router } from '@angular/router';
+import { TokenService, 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;
+
+ @Output() closeWindow: EventEmitter<any> = new EventEmitter<any>();
+
+ senderBloxbergLink: string;
+ recipientBloxbergLink: string;
+ traderBloxbergLink: string;
+ tokenName: string;
+ tokenSymbol: string;
+
+ constructor(
+ private router: Router,
+ private transactionService: TransactionService,
+ private snackBar: MatSnackBar,
+ private tokenService: TokenService
+ ) {}
+
+ async ngOnInit(): Promise<void> {
+ await this.transactionService.init();
+ await this.tokenService.init();
+ 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';
+ }
+ this.tokenService.load.subscribe(async (status: boolean) => {
+ if (status) {
+ this.tokenSymbol = await this.tokenService.getTokenSymbol();
+ this.tokenName = await this.tokenService.getTokenName();
+ }
+ });
+ }
+
+ 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 });
+ }
+ }
+
+ close(): void {
+ this.transaction = null;
+ this.closeWindow.emit(this.transaction);
+ }
+}
+
+ <div *ngIf="transaction" 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)="close()"> 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: {{transaction.value | tokenRatio}} {{ tokenSymbol | uppercase }}</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: {{ tokenName }}</span>
+ </li>
+ <li class="list-group-item">
+ <span>Symbol: {{ tokenSymbol | uppercase }}</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 | unixDate}}</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 | uppercase}}</span>
+ </li>
+ <li class="list-group-item">
+ <span>Amount: {{transaction.fromValue}} {{transaction.sourceToken.symbol | uppercase}}</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 | uppercase}}</span>
+ </li>
+ <li class="list-group-item">
+ <span>Amount: {{transaction.toValue}} {{transaction.destinationToken.symbol | uppercase}}</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, tokenService: TokenService)
+ |
+ |||||||||||||||
+ + | +|||||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + doFilter + + + + | +|||||||||
+doFilter(value: string, dataSource)
+ |
+ |||||||||
+ + | +|||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + downloadCsv + + + + | +
+downloadCsv()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + filterTransactions + + + + | +
+filterTransactions()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + ngAfterViewInit + + + + | +
+ngAfterViewInit()
+ |
+
+ + | +
+
+
+ Returns :
+ void
+
+ |
+
+ + + + Async + ngOnInit + + + + | +
+
+ ngOnInit()
+ |
+
+ + | +
+
+
+ Returns :
+ Promise<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)
+ |
+
+ + | +
+ + + + tokenSymbol + + + | +
+ Type : string
+
+ |
+
+ + | +
+ + + + 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, TokenService, 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>;
+ tokenSymbol: string;
+
+ @ViewChild(MatPaginator) paginator: MatPaginator;
+ @ViewChild(MatSort) sort: MatSort;
+
+ constructor(
+ private blockSyncService: BlockSyncService,
+ private transactionService: TransactionService,
+ private userService: UserService,
+ private tokenService: TokenService
+ ) {}
+
+ async ngOnInit(): Promise<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;
+ });
+ await this.blockSyncService.init();
+ await this.tokenService.init();
+ await this.transactionService.init();
+ await this.userService.init();
+ await this.blockSyncService.blockSync();
+ this.userService
+ .getTransactionTypes()
+ .pipe(first())
+ .subscribe((res) => (this.transactionsTypes = res));
+ this.tokenService.load.subscribe(async (status: boolean) => {
+ if (status) {
+ this.tokenSymbol = await this.tokenService.getTokenSymbol();
+ }
+ });
+ }
+
+ 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" (closeWindow)="transaction = $event"></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 || transaction.from}} </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 || transaction.to}} </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}} {{ tokenSymbol | uppercase }}</span>
+ <span *ngIf="transaction.type == 'conversion'">{{transaction?.toValue | tokenRatio}} {{ tokenSymbol | uppercase }}</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 | unixDate}} </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 | ++ 100 % + (9/9) + | +
+ + src/app/_eth/accountIndex.ts + | +variable | +abi | ++ 100 % + (1/1) + | +
+ + src/app/_eth/accountIndex.ts + | +variable | +web3 | ++ 100 % + (1/1) + | +
+ + src/app/_eth/token-registry.ts + | +class | +TokenRegistry | ++ 100 % + (8/8) + | +
+ + src/app/_eth/token-registry.ts + | +variable | +abi | ++ 100 % + (1/1) + | +
+ + src/app/_eth/token-registry.ts + | +variable | +web3 | ++ 100 % + (1/1) + | +
+ + src/app/_guards/auth.guard.ts + | +guard | +AuthGuard | ++ 100 % + (3/3) + | +
+ + src/app/_guards/role.guard.ts + | +guard | +RoleGuard | ++ 100 % + (3/3) + | +
+ + src/app/_helpers/array-sum.ts + | +function | +arraySum | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/clipboard-copy.ts + | +function | +copyToClipboard | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/custom-error-state-matcher.ts + | +class | +CustomErrorStateMatcher | ++ 100 % + (2/2) + | +
+ + src/app/_helpers/custom.validator.ts + | +class | +CustomValidator | ++ 100 % + (3/3) + | +
+ + src/app/_helpers/export-csv.ts + | +function | +exportCsv | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/global-error-handler.ts + | +class | +HttpError | ++ 100 % + (3/3) + | +
+ + src/app/_helpers/global-error-handler.ts + | +injectable | +GlobalErrorHandler | ++ 100 % + (6/6) + | +
+ + src/app/_helpers/global-error-handler.ts + | +function | +rejectBody | ++ 0 % + (0/1) + | +
+ + src/app/_helpers/http-getter.ts + | +function | +HttpGetter | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +interceptor | +MockBackendInterceptor | ++ 100 % + (2/2) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +accountTypes | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +actions | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +areaNames | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +areaTypes | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +categories | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +genders | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +MockBackendProvider | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +tokens | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/mock-backend.ts + | +variable | +transactionTypes | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/read-csv.ts + | +function | +parseData | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/read-csv.ts + | +function | +readCsv | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/read-csv.ts + | +variable | +objCsv | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/schema-validation.ts + | +function | +personValidation | ++ 100 % + (1/1) + | +
+ + src/app/_helpers/schema-validation.ts + | +function | +vcardValidation | ++ 100 % + (1/1) + | +
+ + src/app/_interceptors/error.interceptor.ts + | +interceptor | +ErrorInterceptor | ++ 100 % + (3/3) + | +
+ + src/app/_interceptors/http-config.interceptor.ts + | +interceptor | +HttpConfigInterceptor | ++ 100 % + (3/3) + | +
+ + src/app/_interceptors/logging.interceptor.ts + | +interceptor | +LoggingInterceptor | ++ 100 % + (3/3) + | +
+ + src/app/_models/account.ts + | +interface | +AccountDetails | ++ 100 % + (11/11) + | +
+ + src/app/_models/account.ts + | +interface | +Meta | ++ 100 % + (4/4) + | +
+ + src/app/_models/account.ts + | +interface | +MetaResponse | ++ 100 % + (3/3) + | +
+ + src/app/_models/account.ts + | +interface | +Signature | ++ 100 % + (5/5) + | +
+ + src/app/_models/account.ts + | +variable | +defaultAccount | ++ 100 % + (1/1) + | +
+ + src/app/_models/mappings.ts + | +interface | +Action | ++ 100 % + (6/6) + | +
+ + src/app/_models/mappings.ts + | +interface | +AreaName | ++ 100 % + (3/3) + | +
+ + src/app/_models/mappings.ts + | +interface | +AreaType | ++ 100 % + (3/3) + | +
+ + src/app/_models/mappings.ts + | +interface | +Category | ++ 100 % + (3/3) + | +
+ + src/app/_models/settings.ts + | +class | +Settings | ++ 100 % + (6/6) + | +
+ + src/app/_models/settings.ts + | +interface | +W3 | ++ 100 % + (3/3) + | +
+ + src/app/_models/staff.ts + | +interface | +Staff | ++ 100 % + (6/6) + | +
+ + src/app/_models/token.ts + | +interface | +Token | ++ 100 % + (9/9) + | +
+ + src/app/_models/transaction.ts + | +interface | +Conversion | ++ 100 % + (8/8) + | +
+ + src/app/_models/transaction.ts + | +interface | +Transaction | ++ 100 % + (9/9) + | +
+ + src/app/_models/transaction.ts + | +interface | +Tx | ++ 100 % + (6/6) + | +
+ + src/app/_models/transaction.ts + | +interface | +TxToken | ++ 100 % + (4/4) + | +
+ + src/app/_pgp/pgp-key-store.ts + | +class | +MutablePgpKeyStore | ++ 100 % + (26/26) + | +
+ + src/app/_pgp/pgp-key-store.ts + | +interface | +MutableKeyStore | ++ 100 % + (26/26) + | +
+ + src/app/_pgp/pgp-key-store.ts + | +variable | +keyring | ++ 100 % + (1/1) + | +
+ + src/app/_pgp/pgp-signer.ts + | +class | +PGPSigner | ++ 100 % + (14/14) + | +
+ + src/app/_pgp/pgp-signer.ts + | +interface | +Signable | ++ 100 % + (2/2) + | +
+ + src/app/_pgp/pgp-signer.ts + | +interface | +Signature | ++ 100 % + (5/5) + | +
+ + src/app/_pgp/pgp-signer.ts + | +interface | +Signer | ++ 100 % + (7/7) + | +
+ + src/app/_services/auth.service.ts + | +injectable | +AuthService | ++ 0 % + (0/22) + | +
+ + 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/5) + | +
+ + src/app/_services/token.service.ts + | +injectable | +TokenService | ++ 0 % + (0/16) + | +
+ + src/app/_services/transaction.service.ts + | +injectable | +TransactionService | ++ 0 % + (0/17) + | +
+ + src/app/_services/transaction.service.ts + | +variable | +vCard | ++ 0 % + (0/1) + | +
+ + src/app/_services/user.service.ts + | +injectable | +UserService | ++ 0 % + (0/35) + | +
+ + src/app/_services/user.service.ts + | +variable | +vCard | ++ 0 % + (0/1) + | +
+ + src/app/_services/web3.service.ts + | +injectable | +Web3Service | ++ 0 % + (0/4) + | +
+ + src/app/app.component.ts + | +component | +AppComponent | ++ 0 % + (0/10) + | +
+ + src/app/auth/_directives/password-toggle.directive.ts + | +directive | +PasswordToggleDirective | ++ 100 % + (5/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/44) + | +
+ + 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/13) + | +
+ + src/app/pages/tokens/token-details/token-details.component.ts + | +component | +TokenDetailsComponent | ++ 0 % + (0/6) + | +
+ + src/app/pages/tokens/tokens.component.ts + | +component | +TokensComponent | ++ 0 % + (0/12) + | +
+ + src/app/pages/transactions/transaction-details/transaction-details.component.ts + | +component | +TransactionDetailsComponent | ++ 0 % + (0/16) + | +
+ + src/app/pages/transactions/transactions.component.ts + | +component | +TransactionsComponent | ++ 0 % + (0/19) + | +
+ + src/app/shared/_directives/menu-selection.directive.ts + | +directive | +MenuSelectionDirective | ++ 100 % + (3/3) + | +
+ + src/app/shared/_directives/menu-toggle.directive.ts + | +directive | +MenuToggleDirective | ++ 100 % + (3/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/_pipes/unix-date.pipe.ts + | +pipe | +UnixDatePipe | ++ 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/4) + | +
+ + src/app/shared/network-status/network-status.component.ts + | +component | +NetworkStatusComponent | ++ 0 % + (0/5) + | +
+ + 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
+
+
+
Toggle availability of sidebar on menu item selection.
+ + + + + +Selector | +[appMenuSelection] |
+
+ Methods+ |
+
+
|
+
+constructor(elementRef: ElementRef, renderer: Renderer2)
+ |
+ ||||||||||||
+ + | +||||||||||||
+ Handle click events on the html element. +
+ Parameters :
+
+
|
+
+ + + + onMenuSelect + + + + | +
+onMenuSelect()
+ |
+
+ + | +
+ Toggle the availability of the sidebar. +
+ Returns :
+ void
+
+ |
+
import { Directive, ElementRef, Renderer2 } from '@angular/core';
+
+/** Toggle availability of sidebar on menu item selection. */
+@Directive({
+ selector: '[appMenuSelection]',
+})
+export class MenuSelectionDirective {
+ /**
+ * Handle click events on the html element.
+ *
+ * @param elementRef - A wrapper around a native element inside of a View.
+ * @param renderer - Extend this base class to implement custom rendering.
+ */
+ 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();
+ }
+ });
+ }
+
+ /** Toggle the availability of the sidebar. */
+ 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
+
+
+
Toggle availability of sidebar on menu toggle click.
+ + + + + +Selector | +[appMenuToggle] |
+
+ Methods+ |
+
+
|
+
+constructor(elementRef: ElementRef, renderer: Renderer2)
+ |
+ ||||||||||||
+ + | +||||||||||||
+ Handle click events on the html element. +
+ Parameters :
+
+
|
+
+ + + + onMenuToggle + + + + | +
+onMenuToggle()
+ |
+
+ + | +
+ Toggle the availability of the sidebar. +
+ Returns :
+ void
+
+ |
+
import { Directive, ElementRef, Renderer2 } from '@angular/core';
+
+/** Toggle availability of sidebar on menu toggle click. */
+@Directive({
+ selector: '[appMenuToggle]',
+})
+export class MenuToggleDirective {
+ /**
+ * Handle click events on the html element.
+ *
+ * @param elementRef - A wrapper around a native element inside of a View.
+ * @param renderer - Extend this base class to implement custom rendering.
+ */
+ constructor(private elementRef: ElementRef, private renderer: Renderer2) {
+ this.renderer.listen(this.elementRef.nativeElement, 'click', () => {
+ this.onMenuToggle();
+ });
+ }
+
+ /** Toggle the availability of the sidebar. */
+ 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
+
+
+
Toggle password form field input visibility
+ + + + + +Selector | +[appPasswordToggle] |
+
+ Methods+ |
+
+
|
+
+ Inputs+ |
+
+ + | +
+constructor(elementRef: ElementRef, renderer: Renderer2)
+ |
+ ||||||||||||
+ + | +||||||||||||
+ Handle click events on the html element. +
+ Parameters :
+
+
|
+
+ + iconId + | +|
+ Type : string
+
+ |
+ |
+ + | +|
+ The password form field icon id + |
+
+ + id + | +|
+ Type : string
+
+ |
+ |
+ + | +|
+ The password form field id + |
+
+ + + + togglePasswordVisibility + + + + | +
+togglePasswordVisibility()
+ |
+
+ + | +
+ Toggle the visibility of the password input field value and accompanying icon. +
+ Returns :
+ void
+
+ |
+
import { Directive, ElementRef, Input, Renderer2 } from '@angular/core';
+
+/** Toggle password form field input visibility */
+@Directive({
+ selector: '[appPasswordToggle]',
+})
+export class PasswordToggleDirective {
+ /** The password form field id */
+ @Input()
+ id: string;
+
+ /** The password form field icon id */
+ @Input()
+ iconId: string;
+
+ /**
+ * Handle click events on the html element.
+ *
+ * @param elementRef - A wrapper around a native element inside of a View.
+ * @param renderer - Extend this base class to implement custom rendering.
+ */
+ constructor(private elementRef: ElementRef, private renderer: Renderer2) {
+ this.renderer.listen(this.elementRef.nativeElement, 'click', () => {
+ this.togglePasswordVisibility();
+ });
+ }
+
+ /** Toggle the visibility of the password input field value and accompanying icon. */
+ 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 | +[appRouterLink] |
+
+ Properties+ |
+
+
|
+
+ Inputs+ |
+
+
|
+
+ HostListeners+ |
+
+
|
+
+ + routerLink + | +|
+ Type : any
+
+ |
+ |
+ Defined in src/testing/router-link-directive-stub.ts:9
+ |
+
+ + + + click + + + + | +
+click()
+ |
+
+ Defined in src/testing/router-link-directive-stub.ts:13
+ |
+
+ + + + navigatedTo + + + | +
+ Type : any
+
+ |
+
+ Default value : null
+ |
+
+ Defined in src/testing/router-link-directive-stub.ts:10
+ |
+
import {Directive, HostListener, Input} from '@angular/core';
+
+@Directive({
+ selector: '[appRouterLink]'
+})
+// tslint:disable-next-line:directive-class-suffix
+export class RouterLinkDirectiveStub {
+ // tslint:disable-next-line:no-input-rename
+ @Input('routerLink') linkParams: any;
+ navigatedTo: any = null;
+
+ @HostListener('click')
+ onClick(): void {
+ this.navigatedTo = this.linkParams;
+ }
+}
+
+ +
+ src/app/_guards/auth.guard.ts
+
+
+
Auth guard implementation. +Dictates access to routes depending on the authentication status.
+ + + + + + +
+ Methods+ |
+
+
|
+
+constructor(router: Router)
+ |
+ ||||||||
+ Defined in src/app/_guards/auth.guard.ts:21
+ |
+ ||||||||
+ Instantiates the auth guard class. +
+ Parameters :
+
+
|
+
+ + + + canActivate + + + + | +||||||||||||
+canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
+ |
+ ||||||||||||
+ Defined in src/app/_guards/auth.guard.ts:38
+ |
+ ||||||||||||
+ Returns whether navigation to a specific route is acceptable. +Checks if the user has uploaded a private key. +
+ Parameters :
+
+
+
+
+ Returns :
+ Observable | Promise | boolean | UrlTree
+
+
+
+ true - If there is an active private key in the user's localStorage. + + |
+
import { Injectable } from '@angular/core';
+import {
+ ActivatedRouteSnapshot,
+ CanActivate,
+ Router,
+ RouterStateSnapshot,
+ UrlTree,
+} from '@angular/router';
+
+// Third party imports
+import { Observable } from 'rxjs';
+
+/**
+ * Auth guard implementation.
+ * Dictates access to routes depending on the authentication status.
+ */
+@Injectable({
+ providedIn: 'root',
+})
+export class AuthGuard implements CanActivate {
+ /**
+ * Instantiates the auth guard class.
+ *
+ * @param router - A service that provides navigation among views and URL manipulation capabilities.
+ */
+ constructor(private router: Router) {}
+
+ /**
+ * Returns whether navigation to a specific route is acceptable.
+ * Checks if the user has uploaded a private key.
+ *
+ * @param route - Contains the information about a route associated with a component loaded in an outlet at a particular moment in time.
+ * ActivatedRouteSnapshot can also be used to traverse the router state tree.
+ * @param state - Represents the state of the router at a moment in time.
+ * @returns true - If there is an active private key in the user's localStorage.
+ */
+ 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
+
+
+
Role guard implementation. +Dictates access to routes depending on the user's role.
+ + + + + + +
+ Methods+ |
+
+
|
+
+constructor(router: Router)
+ |
+ ||||||||
+ Defined in src/app/_guards/role.guard.ts:21
+ |
+ ||||||||
+ Instantiates the role guard class. +
+ Parameters :
+
+
|
+
+ + + + canActivate + + + + | +||||||||||||
+canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
+ |
+ ||||||||||||
+ Defined in src/app/_guards/role.guard.ts:38
+ |
+ ||||||||||||
+ Returns whether navigation to a specific route is acceptable. +Checks if the user has the required role to access the route. +
+ Parameters :
+
+
+
+
+ Returns :
+ Observable | Promise | boolean | UrlTree
+
+
+
+ true - If the user's role matches with accepted roles. + + |
+
import { Injectable } from '@angular/core';
+import {
+ ActivatedRouteSnapshot,
+ CanActivate,
+ Router,
+ RouterStateSnapshot,
+ UrlTree,
+} from '@angular/router';
+
+// Third party imports
+import { Observable } from 'rxjs';
+
+/**
+ * Role guard implementation.
+ * Dictates access to routes depending on the user's role.
+ */
+@Injectable({
+ providedIn: 'root',
+})
+export class RoleGuard implements CanActivate {
+ /**
+ * Instantiates the role guard class.
+ *
+ * @param router - A service that provides navigation among views and URL manipulation capabilities.
+ */
+ constructor(private router: Router) {}
+
+ /**
+ * Returns whether navigation to a specific route is acceptable.
+ * Checks if the user has the required role to access the route.
+ *
+ * @param route - Contains the information about a route associated with a component loaded in an outlet at a particular moment in time.
+ * ActivatedRouteSnapshot can also be used to traverse the router state tree.
+ * @param state - Represents the state of the router at a moment in time.
+ * @returns true - If the user's role matches with accepted roles.
+ */
+ 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 ng serve
for a local server, npm run start:dev
for a dev server and npm run start:prod
for a prod 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.
Run ng build
to build the project using local configurations.
+The build artifacts will be stored in the dist/
directory.
Use the npm run build:dev
script for a development build and the npm run build:prod
script for a production build.
The app supports Progressive Web App capabilities.
+Run npm run start:pwa
to run the project in PWA mode.
+PWA mode works using production configurations.
Run ng test
to execute the unit tests via Karma.
Run ng e2e
to execute the end-to-end tests via Protractor.
Default environment variables are located in the src/environments/
directory.
+Custom environment variables are contained in the .env
file. See .env.example
for a template.
Custom environment variables are set via the set-env.ts
file.
+Once loaded they will be populated in the directory src/environments/
.
+It contains environment variables for development on environment.dev.ts
and production on environment.prod.ts
.
The system has automated code formatting using Prettier and TsLint.
+To view the styling rules set, check out .prettierrc
and tslint.json
.
Run npm run format:lint
To perform formatting and linting of the codebase.
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:22
+ |
+ ||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + addTrustedUser + + + + | +||||||
+addTrustedUser(user: Staff)
+ |
+ ||||||
+ Defined in src/app/_services/auth.service.ts:173
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + getChallenge + + + + | +
+getChallenge()
+ |
+
+ Defined in src/app/_services/auth.service.ts:85
+ |
+
+
+
+ Returns :
+ Promise<any>
+
+ |
+
+ + + + getPrivateKey + + + + | +
+getPrivateKey()
+ |
+
+ Defined in src/app/_services/auth.service.ts:203
+ |
+
+
+
+ Returns :
+ any
+
+ |
+
+ + + + getPrivateKeyInfo + + + + | +
+getPrivateKeyInfo()
+ |
+
+ Defined in src/app/_services/auth.service.ts:207
+ |
+
+
+
+ Returns :
+ any
+
+ |
+
+ + + + Async + getPublicKeys + + + + | +
+
+ getPublicKeys()
+ |
+
+ Defined in src/app/_services/auth.service.ts:191
+ |
+
+
+
+ Returns :
+ Promise<any>
+
+ |
+
+ + + + getSessionToken + + + + | +
+getSessionToken()
+ |
+
+ Defined in src/app/_services/auth.service.ts:39
+ |
+
+
+
+ Returns :
+ string
+
+ |
+
+ + + + getTrustedUsers + + + + | +
+getTrustedUsers()
+ |
+
+ Defined in src/app/_services/auth.service.ts:185
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + getWithToken + + + + | +
+getWithToken()
+ |
+
+ Defined in src/app/_services/auth.service.ts:51
+ |
+
+
+
+ Returns :
+ Promise<boolean>
+
+ |
+
+ + + + Async + init + + + + | +
+
+ init()
+ |
+
+ Defined in src/app/_services/auth.service.ts:32
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + Async + login + + + + | +
+
+ login()
+ |
+
+ Defined in src/app/_services/auth.service.ts:94
+ |
+
+
+
+ Returns :
+ Promise<boolean>
+
+ |
+
+ + + + loginView + + + + | +
+loginView()
+ |
+
+ Defined in src/app/_services/auth.service.ts:129
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + logout + + + + | +
+logout()
+ |
+
+ Defined in src/app/_services/auth.service.ts:167
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + sendSignedChallenge + + + + | +||||||
+sendSignedChallenge(hobaResponseEncoded: any)
+ |
+ ||||||
+ Defined in src/app/_services/auth.service.ts:73
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<any>
+
+
+
+
+ |
+
+ + + + Async + setKey + + + + | +||||||
+
+ setKey(privateKeyArmored)
+ |
+ ||||||
+ Defined in src/app/_services/auth.service.ts:139
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+
+ |
+
+ + + + setSessionToken + + + + | +||||
+setSessionToken(token)
+ |
+ ||||
+ Defined in src/app/_services/auth.service.ts:43
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + setState + + + + | +||||
+setState(s)
+ |
+ ||||
+ Defined in src/app/_services/auth.service.ts:47
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + mutableKeyStore + + + | +
+ Type : MutableKeyStore
+
+ |
+
+ Defined in src/app/_services/auth.service.ts:17
+ |
+
+ + + + trustedUsers + + + | +
+ Type : Array<Staff>
+
+ |
+
+ Default value : []
+ |
+
+ Defined in src/app/_services/auth.service.ts:18
+ |
+
+ + + + Private + trustedUsersList + + + | +
+ Type : BehaviorSubject<Array<Staff>>
+
+ |
+
+ Default value : new BehaviorSubject<Array<Staff>>(
+ this.trustedUsers
+ )
+ |
+
+ Defined in src/app/_services/auth.service.ts:19
+ |
+
+ + + + trustedUsersSubject + + + | +
+ Type : Observable<Array<Staff>>
+
+ |
+
+ Default value : this.trustedUsersList.asObservable()
+ |
+
+ Defined in src/app/_services/auth.service.ts:22
+ |
+
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, rejectBody } from '@app/_helpers/global-error-handler';
+import { Staff } from '@app/_models';
+import { BehaviorSubject, Observable } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class AuthService {
+ mutableKeyStore: MutableKeyStore;
+ trustedUsers: Array<Staff> = [];
+ private trustedUsersList: BehaviorSubject<Array<Staff>> = new BehaviorSubject<Array<Staff>>(
+ this.trustedUsers
+ );
+ trustedUsersSubject: Observable<Array<Staff>> = this.trustedUsersList.asObservable();
+
+ constructor(
+ private httpClient: HttpClient,
+ private loggingService: LoggingService,
+ private errorDialogService: ErrorDialogService
+ ) {
+ this.mutableKeyStore = new MutablePgpKeyStore();
+ }
+
+ async init(): Promise<void> {
+ await this.mutableKeyStore.loadKeyring();
+ if (localStorage.getItem(btoa('CICADA_PRIVATE_KEY'))) {
+ await this.mutableKeyStore.importPrivateKey(localStorage.getItem(btoa('CICADA_PRIVATE_KEY')));
+ }
+ }
+
+ getSessionToken(): string {
+ return sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN'));
+ }
+
+ setSessionToken(token): void {
+ sessionStorage.setItem(btoa('CICADA_SESSION_TOKEN'), token);
+ }
+
+ setState(s): void {
+ document.getElementById('state').innerHTML = s;
+ }
+
+ getWithToken(): Promise<boolean> {
+ const headers = {
+ Authorization: 'Bearer ' + this.getSessionToken,
+ 'Content-Type': 'application/json;charset=utf-8',
+ 'x-cic-automerge': 'none',
+ };
+ const options = {
+ headers,
+ };
+ return fetch(environment.cicMetaUrl, options).then((response) => {
+ if (!response.ok) {
+ this.loggingService.sendErrorLevelMessage('failed to get with auth token.', this, {
+ error: '',
+ });
+
+ return false;
+ }
+ return true;
+ });
+ }
+
+ // TODO rename to send signed challenge and set session. Also separate these responsibilities
+ sendSignedChallenge(hobaResponseEncoded: any): Promise<any> {
+ const headers = {
+ Authorization: 'HOBA ' + hobaResponseEncoded,
+ 'Content-Type': 'application/json;charset=utf-8',
+ 'x-cic-automerge': 'none',
+ };
+ const options = {
+ headers,
+ };
+ return fetch(environment.cicMetaUrl, options);
+ }
+
+ getChallenge(): Promise<any> {
+ return fetch(environment.cicMetaUrl).then((response) => {
+ if (response.status === 401) {
+ const authHeader: string = response.headers.get('WWW-Authenticate');
+ return hobaParseChallengeHeader(authHeader);
+ }
+ });
+ }
+
+ async login(): Promise<boolean> {
+ if (this.getSessionToken()) {
+ sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN'));
+ } else {
+ const o = await this.getChallenge();
+
+ const r = await signChallenge(
+ o.challenge,
+ o.realm,
+ environment.cicMetaUrl,
+ this.mutableKeyStore
+ );
+
+ const tokenResponse = await this.sendSignedChallenge(r).then((response) => {
+ const token = response.headers.get('Token');
+ if (token) {
+ return token;
+ }
+ if (response.status === 401) {
+ throw new HttpError('You are not authorized to use this system', response.status);
+ }
+ if (!response.ok) {
+ throw new HttpError('Unknown error from authentication server', response.status);
+ }
+ });
+
+ if (tokenResponse) {
+ this.setSessionToken(tokenResponse);
+ this.setState('Click button to log in');
+ return true;
+ }
+ return 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());
+ }
+
+ /**
+ * @throws
+ * @param privateKeyArmored - Private key.
+ */
+ 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'));
+ localStorage.removeItem(btoa('CICADA_PRIVATE_KEY'));
+ window.location.reload();
+ }
+
+ addTrustedUser(user: Staff): void {
+ const savedIndex = this.trustedUsers.findIndex((staff) => staff.userid === user.userid);
+ if (savedIndex === 0) {
+ return;
+ }
+ if (savedIndex > 0) {
+ this.trustedUsers.splice(savedIndex, 1);
+ }
+ this.trustedUsers.unshift(user);
+ this.trustedUsersList.next(this.trustedUsers);
+ }
+
+ getTrustedUsers(): void {
+ this.mutableKeyStore.getPublicKeys().forEach((key) => {
+ this.addTrustedUser(key.users[0].userId);
+ });
+ }
+
+ async getPublicKeys(): Promise<any> {
+ return new Promise((resolve, reject) => {
+ fetch(environment.publicKeysUrl).then((res) => {
+ if (!res.ok) {
+ // TODO does angular recommend an error interface?
+ return reject(rejectBody(res));
+ }
+ return resolve(res.text());
+ });
+ });
+ }
+
+ getPrivateKey(): any {
+ return this.mutableKeyStore.getPrivateKey();
+ }
+
+ getPrivateKeyInfo(): any {
+ return this.getPrivateKey().users[0].userId;
+ }
+}
+
+ +
+ src/app/_services/block-sync.service.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+ + | +
+constructor(transactionService: TransactionService, loggingService: LoggingService)
+ |
+ |||||||||
+ Defined in src/app/_services/block-sync.service.ts:16
+ |
+ |||||||||
+
+ Parameters :
+
+
|
+
+ + + + Async + blockSync + + + + | +||||||||||||||||
+
+ blockSync(address: string, offset: number, limit: number)
+ |
+ ||||||||||||||||
+ Defined in src/app/_services/block-sync.service.ts:27
+ |
+ ||||||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + fetcher + + + + | +|||||||||
+fetcher(settings: Settings, transactionsInfo: any)
+ |
+ |||||||||
+ Defined in src/app/_services/block-sync.service.ts:109
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + Async + init + + + + | +
+
+ init()
+ |
+
+ Defined in src/app/_services/block-sync.service.ts:23
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + newEvent + + + + | +|||||||||
+newEvent(tx: any, eventType: string)
+ |
+ |||||||||
+ Defined in src/app/_services/block-sync.service.ts:80
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + readyStateProcessor + + + + | +||||||||||||||||||
+readyStateProcessor(settings: Settings, bit: number, address: string, offset: number, limit: number)
+ |
+ ||||||||||||||||||
+ Defined in src/app/_services/block-sync.service.ts:45
+ |
+ ||||||||||||||||||
+
+
+ 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:88
+ |
+ |||||||||||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + readyState + + + | +
+ Type : number
+
+ |
+
+ Default value : 0
+ |
+
+ Defined in src/app/_services/block-sync.service.ts:16
+ |
+
+ + + + readyStateTarget + + + | +
+ Type : number
+
+ |
+
+ Default value : 2
+ |
+
+ Defined in src/app/_services/block-sync.service.ts:15
+ |
+
import { Injectable } from '@angular/core';
+import { Settings } from '@app/_models';
+import { TransactionHelper } from '@cicnet/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';
+import { Web3Service } from '@app/_services/web3.service';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class BlockSyncService {
+ readyStateTarget: number = 2;
+ readyState: number = 0;
+
+ constructor(
+ private transactionService: TransactionService,
+ private loggingService: LoggingService
+ ) {}
+
+ async init(): Promise<void> {
+ await this.transactionService.init();
+ }
+
+ async blockSync(address: string = null, offset: number = 0, limit: number = 100): Promise<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 = Web3Service.getInstance();
+ settings.registry = await RegistryService.getRegistry();
+ settings.txHelper = new TransactionHelper(settings.w3.engine, settings.registry);
+
+ settings.txHelper.ontransfer = async (transaction: any): Promise<void> => {
+ window.dispatchEvent(this.newEvent(transaction, 'cic_transfer'));
+ };
+ settings.txHelper.onconversion = async (transaction: any): Promise<any> => {
+ window.dispatchEvent(this.newEvent(transaction, 'cic_convert'));
+ };
+ this.readyStateProcessor(settings, readyStateElements.network, address, offset, limit);
+ }
+
+ readyStateProcessor(
+ settings: Settings,
+ bit: number,
+ address: string,
+ offset: number,
+ limit: number
+ ): void {
+ // tslint:disable-next-line:no-bitwise
+ 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);
+ });
+ }
+ }
+ }
+
+ newEvent(tx: any, eventType: string): any {
+ return new CustomEvent(eventType, {
+ 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:13
+ |
+ ||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + Public + dialog + + + | +
+ Type : MatDialog
+
+ |
+
+ Defined in src/app/_services/error-dialog.service.ts:11
+ |
+
+ + + + 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
+
+
+
Provides a hook for centralized exception handling.
+ + + ++
+ ErrorHandler
+
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(loggingService: LoggingService, router: Router)
+ |
+ ||||||||||||
+ Defined in src/app/_helpers/global-error-handler.ts:41
+ |
+ ||||||||||||
+ Initialization of the Global Error Handler. +
+ Parameters :
+
+
|
+
+ + + + handleError + + + + | +||||||||
+handleError(error: Error)
+ |
+ ||||||||
+ Defined in src/app/_helpers/global-error-handler.ts:58
+ |
+ ||||||||
+ Handles different types of errors. +
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + Private + isWarning + + + + | +||||||||
+
+ isWarning(errorTraceString: string)
+ |
+ ||||||||
+ Defined in src/app/_helpers/global-error-handler.ts:84
+ |
+ ||||||||
+ Checks if an error is of type warning. +
+ Parameters :
+
+
+
+
+ Returns :
+ boolean
+
+
+
+ true - If the error is of type warning. + + |
+
+ + + + logError + + + + | +||||||||
+logError(error: any)
+ |
+ ||||||||
+ Defined in src/app/_helpers/global-error-handler.ts:104
+ |
+ ||||||||
+ Write appropriate logs according to the type of error. +
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + Private + sentencesForWarningLogging + + + | +
+ Type : Array<string>
+
+ |
+
+ Default value : []
+ |
+
+ Defined in src/app/_helpers/global-error-handler.ts:41
+ |
+
+ An array of sentence sections that denote warnings. + |
+
import { HttpErrorResponse } from '@angular/common/http';
+import { ErrorHandler, Injectable } from '@angular/core';
+import { Router } from '@angular/router';
+
+// Application imports
+import { LoggingService } from '@app/_services/logging.service';
+
+/**
+ * A generalized http response error.
+ *
+ * @extends Error
+ */
+export class HttpError extends Error {
+ /** The error's status code. */
+ public status: number;
+
+ /**
+ * Initialize the HttpError class.
+ *
+ * @param message - The message given by the error.
+ * @param status - The status code given by the error.
+ */
+ constructor(message: string, status: number) {
+ super(message);
+ this.status = status;
+ this.name = 'HttpError';
+ }
+}
+
+/**
+ * Provides a hook for centralized exception handling.
+ *
+ * @extends ErrorHandler
+ */
+@Injectable()
+export class GlobalErrorHandler extends ErrorHandler {
+ /**
+ * An array of sentence sections that denote warnings.
+ */
+ private sentencesForWarningLogging: Array<string> = [];
+
+ /**
+ * Initialization of the Global Error Handler.
+ *
+ * @param loggingService - A service that provides logging capabilities.
+ * @param router - A service that provides navigation among views and URL manipulation capabilities.
+ */
+ constructor(private loggingService: LoggingService, private router: Router) {
+ super();
+ }
+
+ /**
+ * Handles different types of errors.
+ *
+ * @param error - An error objects thrown when a runtime errors occurs.
+ */
+ 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;
+ }
+
+ /**
+ * Checks if an error is of type warning.
+ *
+ * @param errorTraceString - A description of the error and it's stack trace.
+ * @returns true - If the error is of type warning.
+ */
+ 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;
+ }
+
+ /**
+ * Write appropriate logs according to the type of error.
+ *
+ * @param error - An error objects thrown when a runtime errors occurs.
+ */
+ 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 }
+ );
+ }
+ }
+}
+
+export function rejectBody(error): { status: any; statusText: any } {
+ return {
+ status: error.status,
+ statusText: error.statusText,
+ };
+}
+
+ +
+ 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:17
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getAreaNames + + + + | +
+getAreaNames()
+ |
+
+ Defined in src/app/_services/location.service.ts:13
+ |
+
+
+
+ Returns :
+ Observable<any>
+
+ |
+
+ + + + getAreaTypeByArea + + + + | +||||||
+getAreaTypeByArea(area: string)
+ |
+ ||||||
+ Defined in src/app/_services/location.service.ts:25
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getAreaTypes + + + + | +
+getAreaTypes()
+ |
+
+ Defined in src/app/_services/location.service.ts:21
+ |
+
+
+
+ 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:12
+ |
+
+ + + + Static + Async + getRegistry + + + + | +
+
+ getRegistry()
+ |
+
+ Defined in src/app/_services/registry.service.ts:16
+ |
+
+
+
+ Returns :
+ Promise<CICRegistry>
+
+ |
+
+ + + + Static + fileGetter + + + | +
+ Type : FileGetter
+
+ |
+
+ Default value : new HttpGetter()
+ |
+
+ Defined in src/app/_services/registry.service.ts:11
+ |
+
+ + + + Private + Static + registry + + + | +
+ Type : CICRegistry
+
+ |
+
+ Defined in src/app/_services/registry.service.ts:12
+ |
+
import { Injectable } from '@angular/core';
+import { environment } from '@src/environments/environment';
+import { CICRegistry, FileGetter } from '@cicnet/cic-client';
+import { HttpGetter } from '@app/_helpers';
+import { Web3Service } from '@app/_services/web3.service';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class RegistryService {
+ static fileGetter: FileGetter = new HttpGetter();
+ private static registry: CICRegistry;
+
+ constructor() {}
+
+ public static async getRegistry(): Promise<CICRegistry> {
+ if (!RegistryService.registry) {
+ RegistryService.registry = new CICRegistry(
+ Web3Service.getInstance(),
+ environment.registryAddress,
+ 'Registry',
+ RegistryService.fileGetter,
+ ['../../assets/js/block-sync/data']
+ );
+ RegistryService.registry.declaratorHelper.addTrust(environment.trustedDeclaratorAddress);
+ await RegistryService.registry.load();
+ }
+ return RegistryService.registry;
+ }
+}
+
+ +
+ src/app/_services/token.service.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(httpClient: HttpClient)
+ |
+ ||||||
+ Defined in src/app/_services/token.service.ts:20
+ |
+ ||||||
+
+ Parameters :
+
+
|
+
+ + + + addToken + + + + | +||||||
+addToken(token: Token)
+ |
+ ||||||
+ Defined in src/app/_services/token.service.ts:32
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + Async + getTokenBalance + + + + | +||||||
+
+ getTokenBalance(address: string)
+ |
+ ||||||
+ Defined in src/app/_services/token.service.ts:73
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<Promise<number>>
+
+
+
+
+ |
+
+ + + + Async + getTokenByAddress + + + + | +||||||
+
+ getTokenByAddress(address: string)
+ |
+ ||||||
+ Defined in src/app/_services/token.service.ts:52
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<Token>
+
+
+
+
+ |
+
+ + + + Async + getTokenBySymbol + + + + | +||||||
+
+ getTokenBySymbol(symbol: string)
+ |
+ ||||||
+ Defined in src/app/_services/token.service.ts:63
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<Observable<Token>>
+
+
+
+
+ |
+
+ + + + Async + getTokenName + + + + | +
+
+ getTokenName()
+ |
+
+ Defined in src/app/_services/token.service.ts:78
+ |
+
+
+
+ Returns :
+ Promise<string>
+
+ |
+
+ + + + Async + getTokens + + + + | +
+
+ getTokens()
+ |
+
+ Defined in src/app/_services/token.service.ts:44
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + Async + getTokenSymbol + + + + | +
+
+ getTokenSymbol()
+ |
+
+ Defined in src/app/_services/token.service.ts:83
+ |
+
+
+
+ Returns :
+ Promise<string>
+
+ |
+
+ + + + Async + init + + + + | +
+
+ init()
+ |
+
+ Defined in src/app/_services/token.service.ts:24
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + load + + + | +
+ Type : BehaviorSubject<any>
+
+ |
+
+ Default value : new BehaviorSubject<any>(false)
+ |
+
+ Defined in src/app/_services/token.service.ts:20
+ |
+
+ + + + registry + + + | +
+ Type : CICRegistry
+
+ |
+
+ Defined in src/app/_services/token.service.ts:13
+ |
+
+ + + + tokenRegistry + + + | +
+ Type : TokenRegistry
+
+ |
+
+ Defined in src/app/_services/token.service.ts:14
+ |
+
+ + + + tokens + + + | +
+ Type : Array<Token>
+
+ |
+
+ Default value : []
+ |
+
+ Defined in src/app/_services/token.service.ts:15
+ |
+
+ + + + Private + tokensList + + + | +
+ Type : BehaviorSubject<Array<Token>>
+
+ |
+
+ Default value : new BehaviorSubject<Array<Token>>(
+ this.tokens
+ )
+ |
+
+ Defined in src/app/_services/token.service.ts:16
+ |
+
+ + + + tokensSubject + + + | +
+ Type : Observable<Array<Token>>
+
+ |
+
+ Default value : this.tokensList.asObservable()
+ |
+
+ Defined in src/app/_services/token.service.ts:19
+ |
+
import { Injectable } from '@angular/core';
+import { CICRegistry } from '@cicnet/cic-client';
+import { TokenRegistry } from '@app/_eth';
+import { HttpClient } from '@angular/common/http';
+import { RegistryService } from '@app/_services/registry.service';
+import { Token } from '@app/_models';
+import { BehaviorSubject, Observable, Subject } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class TokenService {
+ registry: CICRegistry;
+ tokenRegistry: TokenRegistry;
+ tokens: Array<Token> = [];
+ private tokensList: BehaviorSubject<Array<Token>> = new BehaviorSubject<Array<Token>>(
+ this.tokens
+ );
+ tokensSubject: Observable<Array<Token>> = this.tokensList.asObservable();
+ load: BehaviorSubject<any> = new BehaviorSubject<any>(false);
+
+ constructor(private httpClient: HttpClient) {}
+
+ async init(): Promise<void> {
+ this.registry = await RegistryService.getRegistry();
+ this.tokenRegistry = new TokenRegistry(
+ await this.registry.getContractAddressByName('TokenRegistry')
+ );
+ this.load.next(true);
+ }
+
+ addToken(token: Token): void {
+ const savedIndex = this.tokens.findIndex((tk) => tk.address === token.address);
+ if (savedIndex === 0) {
+ return;
+ }
+ if (savedIndex > 0) {
+ this.tokens.splice(savedIndex, 1);
+ }
+ this.tokens.unshift(token);
+ this.tokensList.next(this.tokens);
+ }
+
+ async getTokens(): Promise<void> {
+ const count: number = await this.tokenRegistry.totalTokens();
+ for (let i = 0; i < count; i++) {
+ const token: Token = await this.getTokenByAddress(await this.tokenRegistry.entry(i));
+ this.addToken(token);
+ }
+ }
+
+ async getTokenByAddress(address: string): Promise<Token> {
+ const token: any = {};
+ const tokenContract = await this.registry.addToken(address);
+ token.address = address;
+ token.name = await tokenContract.methods.name().call();
+ token.symbol = await tokenContract.methods.symbol().call();
+ token.supply = await tokenContract.methods.totalSupply().call();
+ token.decimals = await tokenContract.methods.decimals().call();
+ return token;
+ }
+
+ async getTokenBySymbol(symbol: string): Promise<Observable<Token>> {
+ const tokenSubject: Subject<Token> = new Subject<Token>();
+ await this.getTokens();
+ this.tokensSubject.subscribe((tokens) => {
+ const queriedToken = tokens.find((token) => token.symbol === symbol);
+ tokenSubject.next(queriedToken);
+ });
+ return tokenSubject.asObservable();
+ }
+
+ async getTokenBalance(address: string): Promise<(address: string) => Promise<number>> {
+ const token = await this.registry.addToken(await this.tokenRegistry.entry(0));
+ return await token.methods.balanceOf(address).call();
+ }
+
+ async getTokenName(): Promise<string> {
+ const token = await this.registry.addToken(await this.tokenRegistry.entry(0));
+ return await token.methods.name().call();
+ }
+
+ async getTokenSymbol(): Promise<string> {
+ const token = await this.registry.addToken(await this.tokenRegistry.entry(0));
+ return await token.methods.symbol().call();
+ }
+}
+
+ +
+ src/app/_services/transaction.service.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor(httpClient: HttpClient, authService: AuthService, userService: UserService, loggingService: LoggingService)
+ |
+ |||||||||||||||
+ Defined in src/app/_services/transaction.service.ts:32
+ |
+ |||||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + addTransaction + + + + | +|||||||||
+addTransaction(transaction, cacheSize: number)
+ |
+ |||||||||
+ Defined in src/app/_services/transaction.service.ts:118
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + getAccountInfo + + + + | +||||||||||||
+getAccountInfo(account: string, cacheSize: number)
+ |
+ ||||||||||||
+ Defined in src/app/_services/transaction.service.ts:138
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + getAddressTransactions + + + + | +||||||||||||
+getAddressTransactions(address: string, offset: number, limit: number)
+ |
+ ||||||||||||
+ Defined in src/app/_services/transaction.service.ts:53
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getAllTransactions + + + + | +|||||||||
+getAllTransactions(offset: number, limit: number)
+ |
+ |||||||||
+ Defined in src/app/_services/transaction.service.ts:49
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + Async + init + + + + | +
+
+ init()
+ |
+
+ Defined in src/app/_services/transaction.service.ts:43
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + resetTransactionsList + + + + | +
+resetTransactionsList()
+ |
+
+ Defined in src/app/_services/transaction.service.ts:133
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + Async + setConversion + + + + | +||||||
+
+ setConversion(conversion, cacheSize)
+ |
+ ||||||
+ Defined in src/app/_services/transaction.service.ts:93
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + Async + setTransaction + + + + | +|||||||||
+
+ setTransaction(transaction, cacheSize: number)
+ |
+ |||||||||
+ Defined in src/app/_services/transaction.service.ts:57
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + Async + transferRequest + + + + | +|||||||||||||||
+
+ transferRequest(tokenAddress: string, senderAddress: string, recipientAddress: string, value: number)
+ |
+ |||||||||||||||
+ Defined in src/app/_services/transaction.service.ts:145
+ |
+ |||||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<any>
+
+
+
+
+ |
+
+ + + + registry + + + | +
+ Type : CICRegistry
+
+ |
+
+ Defined in src/app/_services/transaction.service.ts:32
+ |
+
+ + + + Private + transactionList + + + | +
+ Default value : new BehaviorSubject<any[]>(this.transactions)
+ |
+
+ Defined in src/app/_services/transaction.service.ts:28
+ |
+
+ + + + transactions + + + | +
+ Type : any[]
+
+ |
+
+ Default value : []
+ |
+
+ Defined in src/app/_services/transaction.service.ts:27
+ |
+
+ + + + transactionsSubject + + + | +
+ Default value : this.transactionList.asObservable()
+ |
+
+ Defined in src/app/_services/transaction.service.ts:29
+ |
+
+ + + + userInfo + + + | +
+ Type : any
+
+ |
+
+ Defined in src/app/_services/transaction.service.ts:30
+ |
+
+ + + + web3 + + + | +
+ Type : Web3
+
+ |
+
+ Defined in src/app/_services/transaction.service.ts:31
+ |
+
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 '@cicnet/cic-client';
+import { RegistryService } from '@app/_services/registry.service';
+import Web3 from 'web3';
+import { Web3Service } from '@app/_services/web3.service';
+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
+ ) {
+ this.web3 = Web3Service.getInstance();
+ }
+
+ async init(): Promise<void> {
+ await this.authService.init();
+ await this.userService.init();
+ this.registry = await RegistryService.getRegistry();
+ }
+
+ 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/user/${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, cacheSize);
+ },
+ (error) => {
+ transaction.sender = defaultAccount;
+ this.userService.addAccount(defaultAccount, cacheSize);
+ }
+ );
+ this.userService
+ .getAccountDetailsFromMeta(await User.toKey(transaction.to))
+ .pipe(first())
+ .subscribe(
+ (res) => {
+ transaction.recipient = this.getAccountInfo(res, cacheSize);
+ },
+ (error) => {
+ transaction.recipient = defaultAccount;
+ this.userService.addAccount(defaultAccount, cacheSize);
+ }
+ );
+ } 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);
+ },
+ (error) => {
+ conversion.sender = conversion.recipient = defaultAccount;
+ this.userService.addAccount(defaultAccount, cacheSize);
+ }
+ );
+ } finally {
+ this.addTransaction(conversion, cacheSize);
+ }
+ }
+
+ addTransaction(transaction, cacheSize: number): void {
+ const savedIndex = this.transactions.findIndex((tx) => tx.tx.txHash === transaction.tx.txHash);
+ if (savedIndex === 0) {
+ return;
+ }
+ if (savedIndex > 0) {
+ this.transactions.splice(savedIndex, 1);
+ }
+ this.transactions.unshift(transaction);
+ if (this.transactions.length > cacheSize) {
+ this.transactions.length = Math.min(this.transactions.length, cacheSize);
+ }
+ this.transactionList.next(this.transactions);
+ }
+
+ resetTransactionsList(): void {
+ this.transactions = [];
+ this.transactionList.next(this.transactions);
+ }
+
+ getAccountInfo(account: string, cacheSize: number = 100): any {
+ const accountInfo = Envelope.fromJSON(JSON.stringify(account)).unwrap().m.data;
+ accountInfo.vcard = vCard.parse(atob(accountInfo.vcard));
+ this.userService.addAccount(accountInfo, cacheSize);
+ 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, authService: AuthService)
+ |
+ |||||||||||||||
+ Defined in src/app/_services/user.service.ts:36
+ |
+ |||||||||||||||
+
+ Parameters :
+
+
|
+
+ + + + addAccount + + + + | +|||||||||
+addAccount(account: AccountDetails, cacheSize: number)
+ |
+ |||||||||
+ Defined in src/app/_services/user.service.ts:266
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + approveAction + + + + | +||||||
+approveAction(id: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:154
+ |
+ ||||||
+
+
+ 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:67
+ |
+ ||||||||||||||||||||||||||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<any>
+
+
+
+
+ |
+
+ + + + Async + getAccountByAddress + + + + | +||||||||||||
+
+ getAccountByAddress(accountAddress: string, limit: number)
+ |
+ ||||||||||||
+ Defined in src/app/_services/user.service.ts:193
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<Observable<AccountDetails>>
+
+
+
+
+ |
+
+ + + + Async + getAccountByPhone + + + + | +||||||||||||
+
+ getAccountByPhone(phoneNumber: string, limit: number)
+ |
+ ||||||||||||
+ Defined in src/app/_services/user.service.ts:219
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<Observable<AccountDetails>>
+
+
+
+
+ |
+
+ + + + getAccountDetailsFromMeta + + + + | +||||||
+getAccountDetailsFromMeta(userKey: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:162
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getAccountStatus + + + + | +||||||
+getAccountStatus(phone: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:58
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getAccountTypes + + + + | +
+getAccountTypes()
+ |
+
+ Defined in src/app/_services/user.service.ts:254
+ |
+
+
+
+ Returns :
+ Observable<any>
+
+ |
+
+ + + + getActionById + + + + | +||||||
+getActionById(id: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:150
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getActions + + + + | +
+getActions()
+ |
+
+ Defined in src/app/_services/user.service.ts:143
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + getCategories + + + + | +
+getCategories()
+ |
+
+ Defined in src/app/_services/user.service.ts:246
+ |
+
+
+
+ Returns :
+ Observable<any>
+
+ |
+
+ + + + getCategoryByProduct + + + + | +||||||
+getCategoryByProduct(product: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:250
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getGenders + + + + | +
+getGenders()
+ |
+
+ Defined in src/app/_services/user.service.ts:262
+ |
+
+
+
+ Returns :
+ Observable<any>
+
+ |
+
+ + + + getLockedAccounts + + + + | +|||||||||
+getLockedAccounts(offset: number, limit: number)
+ |
+ |||||||||
+ Defined in src/app/_services/user.service.ts:63
+ |
+ |||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + getTransactionTypes + + + + | +
+getTransactionTypes()
+ |
+
+ Defined in src/app/_services/user.service.ts:258
+ |
+
+
+
+ Returns :
+ Observable<any>
+
+ |
+
+ + + + Async + init + + + + | +
+
+ init()
+ |
+
+ Defined in src/app/_services/user.service.ts:45
+ |
+
+
+
+ Returns :
+ Promise<void>
+
+ |
+
+ + + + Async + loadAccounts + + + + | +||||||||||||
+
+ loadAccounts(limit: number, offset: number)
+ |
+ ||||||||||||
+ Defined in src/app/_services/user.service.ts:180
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + resetAccountsList + + + + | +
+resetAccountsList()
+ |
+
+ Defined in src/app/_services/user.service.ts:237
+ |
+
+
+
+ Returns :
+ void
+
+ |
+
+ + + + resetPin + + + + | +||||||
+resetPin(phone: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:53
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + revokeAction + + + + | +||||||
+revokeAction(id: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:158
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<any>
+
+
+
+
+ |
+
+ + + + searchAccountByName + + + + | +||||||
+searchAccountByName(name: string)
+ |
+ ||||||
+ Defined in src/app/_services/user.service.ts:242
+ |
+ ||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+
+ |
+
+ + + + Async + updateMeta + + + + | +||||||||||||
+
+ updateMeta(syncableAccount: Syncable, accountKey: string, headers: HttpHeaders)
+ |
+ ||||||||||||
+ Defined in src/app/_services/user.service.ts:128
+ |
+ ||||||||||||
+
+
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<any>
+
+
+
+
+ |
+
+ + + + wrap + + + + | +|||||||||
+wrap(syncable: Syncable, signer: Signer)
+ |
+ |||||||||
+ Defined in src/app/_services/user.service.ts:166
+ |
+ |||||||||
+
+
+ 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:32
+ |
+
+ + + + actions + + + | +
+ Type : Array<any>
+
+ |
+
+ Default value : []
+ |
+
+ Defined in src/app/_services/user.service.ts:34
+ |
+
+ + + + Private + actionsList + + + | +
+ Type : BehaviorSubject<any>
+
+ |
+
+ Default value : new BehaviorSubject<any>(this.actions)
+ |
+
+ Defined in src/app/_services/user.service.ts:35
+ |
+
+ + + + actionsSubject + + + | +
+ Type : Observable<Array<any>>
+
+ |
+
+ Default value : this.actionsList.asObservable()
+ |
+
+ Defined in src/app/_services/user.service.ts:36
+ |
+
+ + + + 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 '@cicnet/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 authService: AuthService
+ ) {}
+
+ async init(): Promise<void> {
+ await this.authService.init();
+ await this.tokenService.init();
+ this.keystore = this.authService.mutableKeyStore;
+ this.signer = new PGPSigner(this.keystore);
+ this.registry = await RegistryService.getRegistry();
+ }
+
+ resetPin(phone: string): Observable<any> {
+ const params: HttpParams = new HttpParams().set('phoneNumber', phone);
+ return this.httpClient.put(`${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 of Object.keys(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(
+ 'Cannot 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(limit);
+ 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>> {
+ const 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);
+ this.tokenService.load.subscribe(async (status: boolean) => {
+ if (status) {
+ 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.addAccount(accountInfo, limit);
+ accountSubject.next(accountInfo);
+ });
+ return accountSubject.asObservable();
+ }
+
+ async getAccountByPhone(
+ phoneNumber: string,
+ limit: number = 100
+ ): Promise<Observable<AccountDetails>> {
+ const 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`);
+ }
+
+ addAccount(account: AccountDetails, cacheSize: number): void {
+ const savedIndex = this.accounts.findIndex(
+ (acc) =>
+ acc.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0] ===
+ account.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
+ );
+ if (savedIndex === 0) {
+ return;
+ }
+ if (savedIndex > 0) {
+ this.accounts.splice(savedIndex, 1);
+ }
+ this.accounts.unshift(account);
+ if (this.accounts.length > cacheSize) {
+ this.accounts.length = Math.min(this.accounts.length, cacheSize);
+ }
+ this.accountsList.next(this.accounts);
+ }
+}
+
+ +
+ src/app/_services/web3.service.ts
+
+ Properties+ |
+
+
|
+
+ Methods+ |
+
+
|
+
+constructor()
+ |
+
+ Defined in src/app/_services/web3.service.ts:9
+ |
+
+ + + + Static + getInstance + + + + | +
+
+ getInstance()
+ |
+
+ Defined in src/app/_services/web3.service.ts:13
+ |
+
+
+
+ Returns :
+ Web3
+
+ |
+
+ + + + Private + Static + web3 + + + | +
+ Type : Web3
+
+ |
+
+ Defined in src/app/_services/web3.service.ts:9
+ |
+
import { Injectable } from '@angular/core';
+import Web3 from 'web3';
+import { environment } from '@src/environments/environment';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class Web3Service {
+ private static web3: Web3;
+
+ constructor() {}
+
+ public static getInstance(): Web3 {
+ if (!Web3Service.web3) {
+ Web3Service.web3 = new Web3(environment.web3Provider);
+ }
+ return Web3Service.web3;
+ }
+}
+
+ +
+ src/app/_interceptors/error.interceptor.ts
+
+
+
Intercepts and handles errors from outgoing HTTP request.
+ + + + +
+ Methods+ |
+
+
|
+
+constructor(errorDialogService: ErrorDialogService, loggingService: LoggingService, router: Router)
+ |
+ ||||||||||||||||
+ + | +||||||||||||||||
+ Initialization of the error interceptor. +
+ Parameters :
+
+
|
+
+ + + + intercept + + + + | +||||||||||||
+intercept(request: HttpRequest
+ |
+ ||||||||||||
+ + | +||||||||||||
+ Intercepts HTTP requests. +
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<HttpEvent<unknown>>
+
+
+
+ The error caught from the request. + + |
+
import {
+ HttpErrorResponse,
+ HttpEvent,
+ HttpHandler,
+ HttpInterceptor,
+ HttpRequest,
+} from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Router } from '@angular/router';
+
+// Third party imports
+import { Observable, throwError } from 'rxjs';
+import { catchError } from 'rxjs/operators';
+
+// Application imports
+import { ErrorDialogService, LoggingService } from '@app/_services';
+
+/** Intercepts and handles errors from outgoing HTTP request. */
+@Injectable()
+export class ErrorInterceptor implements HttpInterceptor {
+ /**
+ * Initialization of the error interceptor.
+ *
+ * @param errorDialogService - A service that provides a dialog box for displaying errors to the user.
+ * @param loggingService - A service that provides logging capabilities.
+ * @param router - A service that provides navigation among views and URL manipulation capabilities.
+ */
+ constructor(
+ private errorDialogService: ErrorDialogService,
+ private loggingService: LoggingService,
+ private router: Router
+ ) {}
+
+ /**
+ * Intercepts HTTP requests.
+ *
+ * @param request - An outgoing HTTP request with an optional typed body.
+ * @param next - The next HTTP handler or the outgoing request dispatcher.
+ * @returns The error caught from the request.
+ */
+ 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
+
+
+
Intercepts and handles setting of configurations to outgoing HTTP request.
+ + + + +
+ Methods+ |
+
+
|
+
+constructor()
+ |
+
+ + | +
+ Initialization of http config interceptor. + |
+
+ + + + intercept + + + + | +||||||||||||
+intercept(request: HttpRequest
+ |
+ ||||||||||||
+ + | +||||||||||||
+ Intercepts HTTP requests. +
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<HttpEvent<unknown>>
+
+
+
+ The forwarded request. + + |
+
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+
+// Third party imports
+import { Observable } from 'rxjs';
+
+/** Intercepts and handles setting of configurations to outgoing HTTP request. */
+@Injectable()
+export class HttpConfigInterceptor implements HttpInterceptor {
+ /** Initialization of http config interceptor. */
+ constructor() {}
+
+ /**
+ * Intercepts HTTP requests.
+ *
+ * @param request - An outgoing HTTP request with an optional typed body.
+ * @param next - The next HTTP handler or the outgoing request dispatcher.
+ * @returns The forwarded request.
+ */
+ 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
+
+
+
Intercepts and handles of events from outgoing HTTP request.
+ + + + +
+ Methods+ |
+
+
|
+
+constructor(loggingService: LoggingService)
+ |
+ ||||||||
+ + | +||||||||
+ Initialization of the logging interceptor. +
+ Parameters :
+
+
|
+
+ + + + intercept + + + + | +||||||||||||
+intercept(request: HttpRequest
+ |
+ ||||||||||||
+ + | +||||||||||||
+ Intercepts HTTP requests. +
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<HttpEvent<unknown>>
+
+
+
+ The forwarded request. + + |
+
import {
+ HttpEvent,
+ HttpHandler,
+ HttpInterceptor,
+ HttpRequest,
+ HttpResponse,
+} from '@angular/common/http';
+import { Injectable } from '@angular/core';
+
+// Third party imports
+import { Observable } from 'rxjs';
+import { finalize, tap } from 'rxjs/operators';
+
+// Application imports
+import { LoggingService } from '@app/_services/logging.service';
+
+/** Intercepts and handles of events from outgoing HTTP request. */
+@Injectable()
+export class LoggingInterceptor implements HttpInterceptor {
+ /**
+ * Initialization of the logging interceptor.
+ *
+ * @param loggingService - A service that provides logging capabilities.
+ */
+ constructor(private loggingService: LoggingService) {}
+
+ /**
+ * Intercepts HTTP requests.
+ *
+ * @param request - An outgoing HTTP request with an optional typed body.
+ * @param next - The next HTTP handler or the outgoing request dispatcher.
+ * @returns The forwarded request.
+ */
+ 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
+
+
+
Intercepts HTTP requests and handles some specified requests internally. +Provides a backend that can handle requests for certain data items.
+ + + + +
+ Methods+ |
+
+
|
+
+ + + + intercept + + + + | +||||||||||||
+intercept(request: HttpRequest
+ |
+ ||||||||||||
+ Defined in src/app/_helpers/mock-backend.ts:1097
+ |
+ ||||||||||||
+ Intercepts HTTP requests. +
+ Parameters :
+
+
+
+
+ Returns :
+ Observable<HttpEvent<any>>
+
+
+
+ The response from the resolved request. + + |
+
import {
+ HTTP_INTERCEPTORS,
+ HttpEvent,
+ HttpHandler,
+ HttpInterceptor,
+ HttpRequest,
+ HttpResponse,
+} from '@angular/common/http';
+import { Injectable } from '@angular/core';
+
+// Third party imports
+import { Observable, of, throwError } from 'rxjs';
+import { delay, dematerialize, materialize, mergeMap } from 'rxjs/operators';
+
+// Application imports
+import { Action, AreaName, AreaType, Category, Token } from '@app/_models';
+
+/** A mock of the curated account types. */
+const accountTypes: Array<string> = ['user', 'cashier', 'vendor', 'tokenagent', 'group'];
+
+/** A mock of actions made by the admin staff. */
+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 },
+];
+
+/** A mock of curated area names. */
+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'],
+ },
+];
+
+/** A mock of curated area types. */
+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'],
+ },
+];
+
+/** A mock of the user's business categories */
+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'],
+ },
+];
+
+/** A mock of curated genders */
+const genders: Array<string> = ['male', 'female', 'other'];
+
+/** A mock of the tokens in the system. */
+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',
+ },
+];
+
+/** A mock of curated transaction types. */
+const transactionTypes: Array<string> = [
+ 'transactions',
+ 'conversions',
+ 'disbursements',
+ 'rewards',
+ 'reclamation',
+];
+
+/**
+ * Intercepts HTTP requests and handles some specified requests internally.
+ * Provides a backend that can handle requests for certain data items.
+ */
+@Injectable()
+export class MockBackendInterceptor implements HttpInterceptor {
+ /**
+ * Intercepts HTTP requests.
+ *
+ * @param request - An outgoing HTTP request with an optional typed body.
+ * @param next - The next HTTP handler or the outgoing request dispatcher.
+ * @returns The response from the resolved request.
+ */
+ 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());
+
+ /** Forward requests from select routes to their internal handlers. */
+ function handleRoute(): Observable<any> {
+ switch (true) {
+ case url.endsWith('/accounttypes') && method === 'GET':
+ return getAccountTypes();
+ 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('/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('/categories') && method === 'GET':
+ return getCategories();
+ case url.match(/\/categories\/\w+$/) && method === 'GET':
+ return getCategoryByProduct();
+ case url.endsWith('/genders') && method === 'GET':
+ return getGenders();
+ case url.endsWith('/tokens') && method === 'GET':
+ return getTokens();
+ case url.match(/\/tokens\/\w+$/) && method === 'GET':
+ return getTokenBySymbol();
+ case url.endsWith('/transactiontypes') && method === 'GET':
+ return getTransactionTypes();
+ default:
+ // pass through any requests not handled above
+ return next.handle(request);
+ }
+ }
+
+ // route functions
+
+ 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 getAccountTypes(): Observable<HttpResponse<any>> {
+ return ok(accountTypes);
+ }
+
+ 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 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 || 'other');
+ }
+
+ 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 || 'other');
+ }
+
+ 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 || 'other');
+ }
+
+ function getGenders(): Observable<HttpResponse<any>> {
+ return ok(genders);
+ }
+
+ 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 getTransactionTypes(): Observable<HttpResponse<any>> {
+ return ok(transactionTypes);
+ }
+
+ // helper functions
+
+ 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 ok(responseBody: any): Observable<HttpResponse<any>> {
+ return of(new HttpResponse({ status: 200, body: responseBody }));
+ }
+
+ function stringFromUrl(): string {
+ const urlParts: Array<string> = url.split('/');
+ return urlParts[urlParts.length - 1];
+ }
+ }
+}
+
+/** Exports the MockBackendInterceptor as an Angular provider. */
+export const MockBackendProvider = {
+ provide: HTTP_INTERCEPTORS,
+ useClass: MockBackendInterceptor,
+ multi: true,
+};
+
+ +
+ src/app/_models/account.ts
+
+
+
Account data interface
+ + + + +
+ Properties+ |
+
+
|
+
+ + age + | +
+ age:
+ |
+
+ Type : string
+
+ |
+
+ Optional + | +
+ Age of user + |
+
+ + balance + | +
+ balance:
+ |
+
+ Type : number
+
+ |
+
+ Optional + | +
+ Token balance on account + |
+
+ + category + | +
+ category:
+ |
+
+ Type : string
+
+ |
+
+ Optional + | +
+ Business category of user. + |
+
+ + date_registered + | +
+ date_registered:
+ |
+
+ Type : number
+
+ |
+
+ Account registration day + |
+
+ + gender + | +
+ gender:
+ |
+
+ Type : string
+
+ |
+
+ User's gender + |
+
+ + identities + | +
+ identities:
+ |
+
+ Type : literal type
+
+ |
+
+ Account identifiers + |
+
+ + location + | +
+ location:
+ |
+
+ Type : literal type
+
+ |
+
+ User's location + |
+
+ + products + | +
+ products:
+ |
+
+ Type : string[]
+
+ |
+
+ Products or services provided by user. + |
+
+ + type + | +
+ type:
+ |
+
+ Type : string
+
+ |
+
+ Optional + | +
+ Type of account + |
+
+ + vcard + | +
+ vcard:
+ |
+
+ Type : literal type
+
+ |
+
+ Personal identifying information of user + |
+
interface AccountDetails {
+ /** Age of user */
+ age?: string;
+ /** Token balance on account */
+ balance?: number;
+ /** Business category of user. */
+ category?: string;
+ /** Account registration day */
+ date_registered: number;
+ /** User's gender */
+ gender: string;
+ /** Account identifiers */
+ identities: {
+ evm: {
+ 'bloxberg:8996': string[];
+ 'oldchain:1': string[];
+ };
+ latitude: number;
+ longitude: number;
+ };
+ /** User's location */
+ location: {
+ area?: string;
+ area_name: string;
+ area_type?: string;
+ };
+ /** Products or services provided by user. */
+ products: string[];
+ /** Type of account */
+ type?: string;
+ /** Personal identifying information of user */
+ vcard: {
+ email: [
+ {
+ value: string;
+ }
+ ];
+ fn: [
+ {
+ value: string;
+ }
+ ];
+ n: [
+ {
+ value: string[];
+ }
+ ];
+ tel: [
+ {
+ meta: {
+ TYP: string[];
+ };
+ value: string;
+ }
+ ];
+ version: [
+ {
+ value: string;
+ }
+ ];
+ };
+}
+
+/** Meta signature interface */
+interface Signature {
+ /** Algorithm used */
+ algo: string;
+ /** Data that was signed. */
+ data: string;
+ /** Message digest */
+ digest: string;
+ /** Encryption engine used. */
+ engine: string;
+}
+
+/** Meta object interface */
+interface Meta {
+ /** Account details */
+ data: AccountDetails;
+ /** Meta store id */
+ id: string;
+ /** Signature used during write. */
+ signature: Signature;
+}
+
+/** Meta response interface */
+interface MetaResponse {
+ /** Meta store id */
+ id: string;
+ /** Meta object */
+ m: Meta;
+}
+
+/** Default account data object */
+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',
+ },
+ ],
+ },
+};
+
+/** @exports */
+export { AccountDetails, Meta, MetaResponse, Signature, defaultAccount };
+
+ +
+ src/app/_models/mappings.ts
+
+
+
Action object interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + action + | +
+ action:
+ |
+
+ Type : string
+
+ |
+
+ Action performed + |
+
+ + approval + | +
+ approval:
+ |
+
+ Type : boolean
+
+ |
+
+ Action approval status. + |
+
+ + id + | +
+ id:
+ |
+
+ Type : number
+
+ |
+
+ Action ID + |
+
+ + role + | +
+ role:
+ |
+
+ Type : string
+
+ |
+
+ Admin's role in the system + |
+
+ + user + | +
+ user:
+ |
+
+ Type : string
+
+ |
+
+ Admin who initialized the action. + |
+
interface Action {
+ /** Action performed */
+ action: string;
+ /** Action approval status. */
+ approval: boolean;
+ /** Action ID */
+ id: number;
+ /** Admin's role in the system */
+ role: string;
+ /** Admin who initialized the action. */
+ user: string;
+}
+
+/** Area name object interface */
+interface AreaName {
+ /** Locations that map to that area name. */
+ locations: Array<string>;
+ /** Name of area */
+ name: string;
+}
+
+/** Area type object interface */
+interface AreaType {
+ /** Areas that map to that area type. */
+ area: Array<string>;
+ /** Type of area */
+ name: string;
+}
+
+/** Category object interface */
+interface Category {
+ /** Business category */
+ name: string;
+ /** Products that map to that category. */
+ products: Array<string>;
+}
+
+/** @exports */
+export { Action, AreaName, AreaType, Category };
+
+ +
+ src/app/_models/mappings.ts
+
+
+
Area name object interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + locations + | +
+ locations:
+ |
+
+ Type : Array<string>
+
+ |
+
+ Locations that map to that area name. + |
+
+ + name + | +
+ name:
+ |
+
+ Type : string
+
+ |
+
+ Name of area + |
+
interface Action {
+ /** Action performed */
+ action: string;
+ /** Action approval status. */
+ approval: boolean;
+ /** Action ID */
+ id: number;
+ /** Admin's role in the system */
+ role: string;
+ /** Admin who initialized the action. */
+ user: string;
+}
+
+/** Area name object interface */
+interface AreaName {
+ /** Locations that map to that area name. */
+ locations: Array<string>;
+ /** Name of area */
+ name: string;
+}
+
+/** Area type object interface */
+interface AreaType {
+ /** Areas that map to that area type. */
+ area: Array<string>;
+ /** Type of area */
+ name: string;
+}
+
+/** Category object interface */
+interface Category {
+ /** Business category */
+ name: string;
+ /** Products that map to that category. */
+ products: Array<string>;
+}
+
+/** @exports */
+export { Action, AreaName, AreaType, Category };
+
+ +
+ src/app/_models/mappings.ts
+
+
+
Area type object interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + area + | +
+ area:
+ |
+
+ Type : Array<string>
+
+ |
+
+ Areas that map to that area type. + |
+
+ + name + | +
+ name:
+ |
+
+ Type : string
+
+ |
+
+ Type of area + |
+
interface Action {
+ /** Action performed */
+ action: string;
+ /** Action approval status. */
+ approval: boolean;
+ /** Action ID */
+ id: number;
+ /** Admin's role in the system */
+ role: string;
+ /** Admin who initialized the action. */
+ user: string;
+}
+
+/** Area name object interface */
+interface AreaName {
+ /** Locations that map to that area name. */
+ locations: Array<string>;
+ /** Name of area */
+ name: string;
+}
+
+/** Area type object interface */
+interface AreaType {
+ /** Areas that map to that area type. */
+ area: Array<string>;
+ /** Type of area */
+ name: string;
+}
+
+/** Category object interface */
+interface Category {
+ /** Business category */
+ name: string;
+ /** Products that map to that category. */
+ products: Array<string>;
+}
+
+/** @exports */
+export { Action, AreaName, AreaType, Category };
+
+ +
+ src/app/_models/transaction.ts
+
+
+
BlocksBloom object interface
+ + + + +
+ Properties+ |
+
+
|
+
+ + alg + | +
+ alg:
+ |
+
+ Type : string
+
+ |
+
+ + blockFilter + | +
+ blockFilter:
+ |
+
+ Type : string
+
+ |
+
+ + blocktxFilter + | +
+ blocktxFilter:
+ |
+
+ Type : string
+
+ |
+
+ + filterRounds + | +
+ filterRounds:
+ |
+
+ Type : number
+
+ |
+
+ + low + | +
+ low:
+ |
+
+ Type : number
+
+ |
+
import { AccountDetails } from '@app/_models/account';
+
+/** BlocksBloom object interface */
+interface BlocksBloom {
+ low: number;
+ blockFilter: string;
+ blocktxFilter: string;
+ alg: string;
+ filterRounds: number;
+}
+
+/** Conversion object interface */
+interface Conversion {
+ /** Final transaction token information. */
+ destinationToken: TxToken;
+ /** Initial transaction token amount. */
+ fromValue: number;
+ /** Initial transaction token information. */
+ sourceToken: TxToken;
+ /** Final transaction token amount. */
+ toValue: number;
+ /** Address of the initiator of the conversion. */
+ trader: string;
+ /** Account information of the initiator of the conversion. */
+ user: AccountDetails;
+ /** Conversion mining information. */
+ tx: Tx;
+}
+
+/** Transaction object interface */
+interface Transaction {
+ /** Address of the transaction sender. */
+ from: string;
+ /** Account information of the transaction sender. */
+ sender: AccountDetails;
+ /** Address of the transaction recipient. */
+ to: string;
+ /** Account information of the transaction recipient. */
+ recipient: AccountDetails;
+ /** Transaction token information. */
+ token: TxToken;
+ /** Transaction mining information. */
+ tx: Tx;
+ /** Amount of tokens transacted. */
+ value: number;
+ /** Type of transaction. */
+ type?: string;
+}
+
+/** Transaction data interface */
+interface Tx {
+ /** Transaction block number. */
+ block: number;
+ /** Transaction mining status. */
+ success: boolean;
+ /** Time transaction was mined. */
+ timestamp: number;
+ /** Hash generated by transaction. */
+ txHash: string;
+ /** Index of transaction in block. */
+ txIndex: number;
+}
+
+/** Transaction token object interface */
+interface TxToken {
+ /** Address of the deployed token contract. */
+ address: string;
+ /** Name of the token. */
+ name: string;
+ /** The unique token symbol. */
+ symbol: string;
+}
+
+/** @exports */
+export { BlocksBloom, Conversion, Transaction, Tx, TxToken };
+
+ +
+ src/app/_models/mappings.ts
+
+
+
Category object interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + name + | +
+ name:
+ |
+
+ Type : string
+
+ |
+
+ Business category + |
+
+ + products + | +
+ products:
+ |
+
+ Type : Array<string>
+
+ |
+
+ Products that map to that category. + |
+
interface Action {
+ /** Action performed */
+ action: string;
+ /** Action approval status. */
+ approval: boolean;
+ /** Action ID */
+ id: number;
+ /** Admin's role in the system */
+ role: string;
+ /** Admin who initialized the action. */
+ user: string;
+}
+
+/** Area name object interface */
+interface AreaName {
+ /** Locations that map to that area name. */
+ locations: Array<string>;
+ /** Name of area */
+ name: string;
+}
+
+/** Area type object interface */
+interface AreaType {
+ /** Areas that map to that area type. */
+ area: Array<string>;
+ /** Type of area */
+ name: string;
+}
+
+/** Category object interface */
+interface Category {
+ /** Business category */
+ name: string;
+ /** Products that map to that category. */
+ products: Array<string>;
+}
+
+/** @exports */
+export { Action, AreaName, AreaType, Category };
+
+ +
+ src/app/_models/transaction.ts
+
+
+
Conversion object interface
+ + + + +
+ Properties+ |
+
+
|
+
+ + destinationToken + | +
+ destinationToken:
+ |
+
+ Type : TxToken
+
+ |
+
+ Final transaction token information. + |
+
+ + fromValue + | +
+ fromValue:
+ |
+
+ Type : number
+
+ |
+
+ Initial transaction token amount. + |
+
+ + sourceToken + | +
+ sourceToken:
+ |
+
+ Type : TxToken
+
+ |
+
+ Initial transaction token information. + |
+
+ + toValue + | +
+ toValue:
+ |
+
+ Type : number
+
+ |
+
+ Final transaction token amount. + |
+
+ + trader + | +
+ trader:
+ |
+
+ Type : string
+
+ |
+
+ Address of the initiator of the conversion. + |
+
+ + tx + | +
+ tx:
+ |
+
+ Type : Tx
+
+ |
+
+ Conversion mining information. + |
+
+ + user + | +
+ user:
+ |
+
+ Type : AccountDetails
+
+ |
+
+ Account information of the initiator of the conversion. + |
+
import { AccountDetails } from '@app/_models/account';
+
+/** Conversion object interface */
+interface Conversion {
+ /** Final transaction token information. */
+ destinationToken: TxToken;
+ /** Initial transaction token amount. */
+ fromValue: number;
+ /** Initial transaction token information. */
+ sourceToken: TxToken;
+ /** Final transaction token amount. */
+ toValue: number;
+ /** Address of the initiator of the conversion. */
+ trader: string;
+ /** Conversion mining information. */
+ tx: Tx;
+ /** Account information of the initiator of the conversion. */
+ user: AccountDetails;
+}
+
+/** Transaction object interface */
+interface Transaction {
+ /** Address of the transaction sender. */
+ from: string;
+ /** Account information of the transaction recipient. */
+ recipient: AccountDetails;
+ /** Account information of the transaction sender. */
+ sender: AccountDetails;
+ /** Address of the transaction recipient. */
+ to: string;
+ /** Transaction token information. */
+ token: TxToken;
+ /** Transaction mining information. */
+ tx: Tx;
+ /** Type of transaction. */
+ type?: string;
+ /** Amount of tokens transacted. */
+ value: number;
+}
+
+/** Transaction data interface */
+interface Tx {
+ /** Transaction block number. */
+ block: number;
+ /** Transaction mining status. */
+ success: boolean;
+ /** Time transaction was mined. */
+ timestamp: number;
+ /** Hash generated by transaction. */
+ txHash: string;
+ /** Index of transaction in block. */
+ txIndex: number;
+}
+
+/** Transaction token object interface */
+interface TxToken {
+ /** Address of the deployed token contract. */
+ address: string;
+ /** Name of the token. */
+ name: string;
+ /** The unique token symbol. */
+ symbol: string;
+}
+
+/** @exports */
+export { Conversion, Transaction, Tx, TxToken };
+
+ +
+ src/app/_models/account.ts
+
+
+
Meta object interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + data + | +
+ data:
+ |
+
+ Type : AccountDetails
+
+ |
+
+ Account details + |
+
+ + id + | +
+ id:
+ |
+
+ Type : string
+
+ |
+
+ Meta store id + |
+
+ + signature + | +
+ signature:
+ |
+
+ Type : Signature
+
+ |
+
+ Signature used during write. + |
+
interface AccountDetails {
+ /** Age of user */
+ age?: string;
+ /** Token balance on account */
+ balance?: number;
+ /** Business category of user. */
+ category?: string;
+ /** Account registration day */
+ date_registered: number;
+ /** User's gender */
+ gender: string;
+ /** Account identifiers */
+ identities: {
+ evm: {
+ 'bloxberg:8996': string[];
+ 'oldchain:1': string[];
+ };
+ latitude: number;
+ longitude: number;
+ };
+ /** User's location */
+ location: {
+ area?: string;
+ area_name: string;
+ area_type?: string;
+ };
+ /** Products or services provided by user. */
+ products: string[];
+ /** Type of account */
+ type?: string;
+ /** Personal identifying information of user */
+ vcard: {
+ email: [
+ {
+ value: string;
+ }
+ ];
+ fn: [
+ {
+ value: string;
+ }
+ ];
+ n: [
+ {
+ value: string[];
+ }
+ ];
+ tel: [
+ {
+ meta: {
+ TYP: string[];
+ };
+ value: string;
+ }
+ ];
+ version: [
+ {
+ value: string;
+ }
+ ];
+ };
+}
+
+/** Meta signature interface */
+interface Signature {
+ /** Algorithm used */
+ algo: string;
+ /** Data that was signed. */
+ data: string;
+ /** Message digest */
+ digest: string;
+ /** Encryption engine used. */
+ engine: string;
+}
+
+/** Meta object interface */
+interface Meta {
+ /** Account details */
+ data: AccountDetails;
+ /** Meta store id */
+ id: string;
+ /** Signature used during write. */
+ signature: Signature;
+}
+
+/** Meta response interface */
+interface MetaResponse {
+ /** Meta store id */
+ id: string;
+ /** Meta object */
+ m: Meta;
+}
+
+/** Default account data object */
+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',
+ },
+ ],
+ },
+};
+
+/** @exports */
+export { AccountDetails, Meta, MetaResponse, Signature, defaultAccount };
+
+ +
+ src/app/_models/account.ts
+
+
+
Meta response interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + id + | +
+ id:
+ |
+
+ Type : string
+
+ |
+
+ Meta store id + |
+
+ + m + | +
+ m:
+ |
+
+ Type : Meta
+
+ |
+
+ Meta object + |
+
interface AccountDetails {
+ /** Age of user */
+ age?: string;
+ /** Token balance on account */
+ balance?: number;
+ /** Business category of user. */
+ category?: string;
+ /** Account registration day */
+ date_registered: number;
+ /** User's gender */
+ gender: string;
+ /** Account identifiers */
+ identities: {
+ evm: {
+ 'bloxberg:8996': string[];
+ 'oldchain:1': string[];
+ };
+ latitude: number;
+ longitude: number;
+ };
+ /** User's location */
+ location: {
+ area?: string;
+ area_name: string;
+ area_type?: string;
+ };
+ /** Products or services provided by user. */
+ products: string[];
+ /** Type of account */
+ type?: string;
+ /** Personal identifying information of user */
+ vcard: {
+ email: [
+ {
+ value: string;
+ }
+ ];
+ fn: [
+ {
+ value: string;
+ }
+ ];
+ n: [
+ {
+ value: string[];
+ }
+ ];
+ tel: [
+ {
+ meta: {
+ TYP: string[];
+ };
+ value: string;
+ }
+ ];
+ version: [
+ {
+ value: string;
+ }
+ ];
+ };
+}
+
+/** Meta signature interface */
+interface Signature {
+ /** Algorithm used */
+ algo: string;
+ /** Data that was signed. */
+ data: string;
+ /** Message digest */
+ digest: string;
+ /** Encryption engine used. */
+ engine: string;
+}
+
+/** Meta object interface */
+interface Meta {
+ /** Account details */
+ data: AccountDetails;
+ /** Meta store id */
+ id: string;
+ /** Signature used during write. */
+ signature: Signature;
+}
+
+/** Meta response interface */
+interface MetaResponse {
+ /** Meta store id */
+ id: string;
+ /** Meta object */
+ m: Meta;
+}
+
+/** Default account data object */
+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',
+ },
+ ],
+ },
+};
+
+/** @exports */
+export { AccountDetails, Meta, MetaResponse, Signature, defaultAccount };
+
+ +
+ src/app/_pgp/pgp-key-store.ts
+
+
+
Mutable Key store interface.
+ + + ++
+ KeyStore
+
+ Methods+ |
+
+
|
+
+ + + + clearKeysInKeyring + + + + | +
+clearKeysInKeyring()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:16
+ |
+
+ Remove all keys from the keyring. +
+ Returns :
+ void
+
+ |
+
+ + + + getEncryptKeys + + + + | +
+getEncryptKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:24
+ |
+
+ Get all the encryption keys. +
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of encryption keys. + + |
+
+ + + + getFingerprint + + + + | +
+getFingerprint()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:29
+ |
+
+ Get the first private key's fingerprint. +
+
+
+ Returns :
+ string
+
+
+
+ The first private key's fingerprint. + + |
+
+ + + + getKeyId + + + + | +||||||||
+getKeyId(key: any)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:35
+ |
+ ||||||||
+ Get a key's keyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ string
+
+
+
+ The key's keyId. + + |
+
+ + + + getKeysForId + + + + | +||||||||
+getKeysForId(keyId: string)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:41
+ |
+ ||||||||
+ Get keys from the keyring using their keyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of the keys with that keyId. + + |
+
+ + + + getPrivateKey + + + + | +
+getPrivateKey()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:46
+ |
+
+ Get the first private key. +
+
+
+ Returns :
+ any
+
+
+
+ The first private key. + + |
+
+ + + + getPrivateKeyForId + + + + | +||||||||
+getPrivateKeyForId(keyId: string)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:52
+ |
+ ||||||||
+ Get a private key from the keyring using it's keyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+ The private key with that keyId. + + |
+
+ + + + getPrivateKeyId + + + + | +
+getPrivateKeyId()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:57
+ |
+
+ Get the first private key's keyID. +
+
+
+ Returns :
+ string
+
+
+
+ The first private key's keyId. + + |
+
+ + + + getPrivateKeys + + + + | +
+getPrivateKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:62
+ |
+
+ Get all private keys. +
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of all private keys. + + |
+
+ + + + getPublicKeyForId + + + + | +||||||||
+getPublicKeyForId(keyId: string)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:68
+ |
+ ||||||||
+ Get a public key from the keyring using it's keyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+ The public key with that keyId. + + |
+
+ + + + getPublicKeyForSubkeyId + + + + | +||||||||
+getPublicKeyForSubkeyId(subkeyId: string)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:74
+ |
+ ||||||||
+ Get a public key from the keyring using it's subkeyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+ The public key with that subkeyId. + + |
+
+ + + + getPublicKeys + + + + | +
+getPublicKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:79
+ |
+
+ Get all the public keys. +
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of public keys. + + |
+
+ + + + getPublicKeysForAddress + + + + | +||||||||
+getPublicKeysForAddress(address: string)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:85
+ |
+ ||||||||
+ Get public keys from the keyring using their address. +
+ Parameters :
+
+
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of the public keys with that address. + + |
+
+ + + + getTrustedActiveKeys + + + + | +
+getTrustedActiveKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:90
+ |
+
+ Get all the trusted active keys. +
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of trusted active keys. + + |
+
+ + + + getTrustedKeys + + + + | +
+getTrustedKeys()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:95
+ |
+
+ Get all the trusted keys. +
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of trusted keys. + + |
+
+ + + + importKeyPair + + + + | +||||||||||||
+importKeyPair(publicKey: any, privateKey: any)
+ |
+ ||||||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:103
+ |
+ ||||||||||||
+ Add a key pair to keyring. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + importPrivateKey + + + + | +||||||||
+importPrivateKey(privateKey: any)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:110
+ |
+ ||||||||
+ Add private key to keyring. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + importPublicKey + + + + | +||||||||
+importPublicKey(publicKey: any)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:117
+ |
+ ||||||||
+ Add public key to keyring. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + isEncryptedPrivateKey + + + + | +||||||||
+isEncryptedPrivateKey(privateKey: any)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:124
+ |
+ ||||||||
+ Verify that a private key is encrypted. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+ true - If private key is encrypted. + + |
+
+ + + + isValidKey + + + + | +||||||||
+isValidKey(key: any)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:131
+ |
+ ||||||||
+ Test if the input is a valid key. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<boolean>
+
+
+
+ true - If the input is a valid key. + + |
+
+ + + + loadKeyring + + + + | +
+loadKeyring()
+ |
+
+ Defined in src/app/_pgp/pgp-key-store.ts:136
+ |
+
+ Instantiate the keyring in the keystore. +
+ Returns :
+ void
+
+ |
+
+ + + + removeKeysForId + + + + | +||||||||
+removeKeysForId(keyId: string)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:142
+ |
+ ||||||||
+ Remove a public key from the keyring using it's keyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ Array<any>
+
+
+
+ An array of the removed keys. + + |
+
+ + + + removePublicKey + + + + | +||||||||
+removePublicKey(publicKey: any)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:148
+ |
+ ||||||||
+ Remove a public key from the keyring. +
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+ The removed public key. + + |
+
+ + + + removePublicKeyForId + + + + | +||||||||
+removePublicKeyForId(keyId: string)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:154
+ |
+ ||||||||
+ Remove a public key from the keyring using it's keyId. +
+ Parameters :
+
+
+
+
+ Returns :
+ any
+
+
+
+ The removed public key. + + |
+
+ + + + sign + + + + | +||||||||
+sign(plainText: string)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-key-store.ts:161
+ |
+ ||||||||
+ Sign message using private key. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<any>
+
+
+
+ The generated signature. + + |
+
import { KeyStore } from 'cic-client-meta';
+// TODO should we put this on the mutable key store object
+import * as openpgp from 'openpgp';
+
+/** An openpgp Keyring instance. */
+const keyring = new openpgp.Keyring();
+
+/**
+ * Mutable Key store interface.
+ *
+ * @extends KeyStore
+ */
+interface MutableKeyStore extends KeyStore {
+ /** Remove all keys from the keyring. */
+ clearKeysInKeyring(): void;
+ /**
+ * Get all the encryption keys.
+ * @returns An array of encryption keys.
+ * @remarks
+ * Current implementation doesn't include encryption keys.
+ * This is included to appease the implemented Keystore interface.
+ */
+ getEncryptKeys(): Array<any>;
+ /**
+ * Get the first private key's fingerprint.
+ * @returns The first private key's fingerprint.
+ */
+ getFingerprint(): string;
+ /**
+ * Get a key's keyId.
+ * @param key - The key to fetch the keyId from.
+ * @returns The key's keyId.
+ */
+ getKeyId(key: any): string;
+ /**
+ * Get keys from the keyring using their keyId.
+ * @param keyId - The keyId of the keys to be fetched from the keyring.
+ * @returns An array of the keys with that keyId.
+ */
+ getKeysForId(keyId: string): Array<any>;
+ /**
+ * Get the first private key.
+ * @returns The first private key.
+ */
+ getPrivateKey(): any;
+ /**
+ * Get a private key from the keyring using it's keyId.
+ * @param keyId - The keyId of the private key to be fetched from the keyring.
+ * @returns The private key with that keyId.
+ */
+ getPrivateKeyForId(keyId: string): any;
+ /**
+ * Get the first private key's keyID.
+ * @returns The first private key's keyId.
+ */
+ getPrivateKeyId(): string;
+ /**
+ * Get all private keys.
+ * @returns An array of all private keys.
+ */
+ getPrivateKeys(): Array<any>;
+ /**
+ * Get a public key from the keyring using it's keyId.
+ * @param keyId - The keyId of the public key to be fetched from the keyring.
+ * @returns The public key with that keyId.
+ */
+ getPublicKeyForId(keyId: string): any;
+ /**
+ * Get a public key from the keyring using it's subkeyId.
+ * @param subkeyId - The subkeyId of the public key to be fetched from the keyring.
+ * @returns The public key with that subkeyId.
+ */
+ getPublicKeyForSubkeyId(subkeyId: string): any;
+ /**
+ * Get all the public keys.
+ * @returns An array of public keys.
+ */
+ getPublicKeys(): Array<any>;
+ /**
+ * Get public keys from the keyring using their address.
+ * @param address - The address of the public keys to be fetched from the keyring.
+ * @returns An array of the public keys with that address.
+ */
+ getPublicKeysForAddress(address: string): Array<any>;
+ /**
+ * Get all the trusted active keys.
+ * @returns An array of trusted active keys.
+ */
+ getTrustedActiveKeys(): Array<any>;
+ /**
+ * Get all the trusted keys.
+ * @returns An array of trusted keys.
+ */
+ getTrustedKeys(): Array<any>;
+ /**
+ * Add a key pair to keyring.
+ * @async
+ * @param publicKey - The public key to be added to the keyring.
+ * @param privateKey - The private key to be added to the keyring.
+ * @throws Error
+ */
+ importKeyPair(publicKey: any, privateKey: any): Promise<void>;
+ /**
+ * Add private key to keyring.
+ * @async
+ * @param privateKey - The private key to be added to the keyring.
+ * @throws Error
+ */
+ importPrivateKey(privateKey: any): Promise<void>;
+ /**
+ * Add public key to keyring.
+ * @async
+ * @param publicKey - The public key to be added to the keyring.
+ * @throws Error
+ */
+ importPublicKey(publicKey: any): Promise<void>;
+ /**
+ * Verify that a private key is encrypted.
+ * @async
+ * @param privateKey - The private key to verify.
+ * @returns true - If private key is encrypted.
+ */
+ isEncryptedPrivateKey(privateKey: any): Promise<boolean>;
+ /**
+ * Test if the input is a valid key.
+ * @async
+ * @param key - The input to be validated.
+ * @returns true - If the input is a valid key.
+ */
+ isValidKey(key: any): Promise<boolean>;
+ /**
+ * Instantiate the keyring in the keystore.
+ * @async
+ */
+ loadKeyring(): void;
+ /**
+ * Remove a public key from the keyring using it's keyId.
+ * @param keyId - The keyId of the keys to be removed from the keyring.
+ * @returns An array of the removed keys.
+ */
+ removeKeysForId(keyId: string): Array<any>;
+ /**
+ * Remove a public key from the keyring.
+ * @param publicKey - The public key to be removed from the keyring.
+ * @returns The removed public key.
+ */
+ removePublicKey(publicKey: any): any;
+ /**
+ * Remove a public key from the keyring using it's keyId.
+ * @param keyId - The keyId of the public key to be removed from the keyring.
+ * @returns The removed public key.
+ */
+ removePublicKeyForId(keyId: string): any;
+ /**
+ * Sign message using private key.
+ * @async
+ * @param plainText - The message to be signed.
+ * @returns The generated signature.
+ */
+ sign(plainText: string): Promise<any>;
+}
+
+/** Provides a keyring for pgp keys. */
+class MutablePgpKeyStore implements MutableKeyStore {
+ /** Remove all keys from the keyring. */
+ clearKeysInKeyring(): void {
+ keyring.clear();
+ }
+
+ /**
+ * Get all the encryption keys.
+ * @returns An array of encryption keys.
+ * @remarks
+ * Current implementation doesn't include encryption keys.
+ * This is included to appease the implemented Keystore interface.
+ */
+ getEncryptKeys(): Array<any> {
+ return [];
+ }
+
+ /**
+ * Get the first private key's fingerprint.
+ * @returns The first private key's fingerprint.
+ */
+ getFingerprint(): string {
+ // TODO Handle multiple keys
+ return (
+ keyring.privateKeys &&
+ keyring.privateKeys.keys[0] &&
+ keyring.privateKeys.keys[0].keyPacket &&
+ keyring.privateKeys.keys[0].keyPacket.fingerprint
+ );
+ }
+
+ /**
+ * Get a key's keyId.
+ * @param key - The key to fetch the keyId from.
+ * @returns The key's keyId.
+ */
+ getKeyId(key: any): string {
+ return key.getKeyId().toHex();
+ }
+
+ /**
+ * Get keys from the keyring using their keyId.
+ * @param keyId - The keyId of the keys to be fetched from the keyring.
+ * @returns An array of the keys with that keyId.
+ */
+ getKeysForId(keyId: string): Array<any> {
+ return keyring.getKeysForId(keyId);
+ }
+
+ /**
+ * Get the first private key.
+ * @returns The first private key.
+ */
+ getPrivateKey(): any {
+ return keyring.privateKeys && keyring.privateKeys.keys[0];
+ }
+
+ /**
+ * Get a private key from the keyring using it's keyId.
+ * @param keyId - The keyId of the private key to be fetched from the keyring.
+ * @returns The private key with that keyId.
+ */
+ getPrivateKeyForId(keyId): any {
+ return keyring.privateKeys && keyring.privateKeys.getForId(keyId);
+ }
+
+ /**
+ * Get the first private key's keyID.
+ * @returns The first private key's keyId.
+ */
+ 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()
+ );
+ }
+
+ /**
+ * Get all private keys.
+ * @returns An array of all private keys.
+ */
+ getPrivateKeys(): Array<any> {
+ return keyring.privateKeys && keyring.privateKeys.keys;
+ }
+
+ /**
+ * Get a public key from the keyring using it's keyId.
+ * @param keyId - The keyId of the public key to be fetched from the keyring.
+ * @returns The public key with that keyId.
+ */
+ getPublicKeyForId(keyId): any {
+ return keyring.publicKeys && keyring.publicKeys.getForId(keyId);
+ }
+
+ /**
+ * Get a public key from the keyring using it's subkeyId.
+ * @param subkeyId - The subkeyId of the public key to be fetched from the keyring.
+ * @returns The public key with that subkeyId.
+ */
+ getPublicKeyForSubkeyId(subkeyId): any {
+ return keyring.publicKeys && keyring.publicKeys.getForId(subkeyId, true);
+ }
+
+ /**
+ * Get all the public keys.
+ * @returns An array of public keys.
+ */
+ getPublicKeys(): Array<any> {
+ return keyring.publicKeys && keyring.publicKeys.keys;
+ }
+
+ /**
+ * Get public keys from the keyring using their address.
+ * @param address - The address of the public keys to be fetched from the keyring.
+ * @returns An array of the public keys with that address.
+ */
+ getPublicKeysForAddress(address): Array<any> {
+ return keyring.publicKeys && keyring.publicKeys.getForAddress(address);
+ }
+
+ /**
+ * Get all the trusted active keys.
+ * @returns An array of trusted active keys.
+ */
+ getTrustedActiveKeys(): Array<any> {
+ return keyring.publicKeys && keyring.publicKeys.keys;
+ }
+
+ /**
+ * Get all the trusted keys.
+ * @returns An array of trusted keys.
+ */
+ getTrustedKeys(): Array<any> {
+ return keyring.publicKeys && keyring.publicKeys.keys;
+ }
+
+ /**
+ * Add a key pair to keyring.
+ * @async
+ * @param publicKey - The public key to be added to the keyring.
+ * @param privateKey - The private key to be added to the keyring.
+ * @throws Error
+ */
+ async importKeyPair(publicKey: any, privateKey: any): Promise<void> {
+ try {
+ await keyring.publicKeys.importKey(publicKey);
+ await keyring.privateKeys.importKey(privateKey);
+ } catch (error) {
+ throw error;
+ }
+ }
+
+ /**
+ * Add private key to keyring.
+ * @async
+ * @param privateKey - The private key to be added to the keyring.
+ * @throws Error
+ */
+ async importPrivateKey(privateKey: any): Promise<void> {
+ try {
+ await keyring.privateKeys.importKey(privateKey);
+ } catch (error) {
+ throw error;
+ }
+ }
+
+ /**
+ * Add public key to keyring.
+ * @async
+ * @param publicKey - The public key to be added to the keyring.
+ * @throws Error
+ */
+ async importPublicKey(publicKey: any): Promise<void> {
+ try {
+ await keyring.publicKeys.importKey(publicKey);
+ } catch (error) {
+ throw error;
+ }
+ }
+
+ /**
+ * Verify that a private key is encrypted.
+ * @async
+ * @param privateKey - The private key to verify.
+ * @returns true - If private key is encrypted.
+ */
+ 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;
+ }
+
+ /**
+ * Test if the input is a valid key.
+ * @async
+ * @param key - The input to be validated.
+ * @returns true - If the input is a valid key.
+ */
+ async isValidKey(key): Promise<boolean> {
+ // There is supposed to be an openpgp.readKey() method but I can't find it?
+ const testKey = await openpgp.key.readArmored(key);
+ return !testKey.err;
+ }
+
+ /**
+ * Instantiate the keyring in the keystore.
+ * @async
+ */
+ async loadKeyring(): Promise<void> {
+ await keyring.load();
+ await keyring.store();
+ }
+
+ /**
+ * Remove a public key from the keyring using it's keyId.
+ * @param keyId - The keyId of the keys to be removed from the keyring.
+ * @returns An array of the removed keys.
+ */
+ removeKeysForId(keyId): Array<any> {
+ return keyring.removeKeysForId(keyId);
+ }
+
+ /**
+ * Remove a public key from the keyring.
+ * @param publicKey - The public key to be removed from the keyring.
+ * @returns The removed public key.
+ */
+ removePublicKey(publicKey: any): any {
+ const keyId = publicKey.getKeyId().toHex();
+ return keyring.publicKeys && keyring.publicKeys.removeForId(keyId);
+ }
+
+ /**
+ * Remove a public key from the keyring using it's keyId.
+ * @param keyId - The keyId of the public key to be removed from the keyring.
+ * @returns The removed public key.
+ */
+ removePublicKeyForId(keyId): any {
+ return keyring.publicKeys && keyring.publicKeys.removeForId(keyId);
+ }
+
+ /**
+ * Sign message using private key.
+ * @async
+ * @param plainText - The message to be signed.
+ * @returns The generated signature.
+ */
+ 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;
+ }
+}
+
+/** @exports */
+export { MutableKeyStore, MutablePgpKeyStore };
+
+ +
+ src/app/_pgp/pgp-signer.ts
+
+
+
Signable object interface
+ + + + +
+ Methods+ |
+
+
|
+
+ + + + digest + + + + | +
+digest()
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:11
+ |
+
+ The message to be signed. +
+ Returns :
+ string
+
+ |
+
import * as openpgp from 'openpgp';
+
+// Application imports
+import { MutableKeyStore } from '@app/_pgp/pgp-key-store';
+import { LoggingService } from '@app/_services/logging.service';
+
+/** Signable object interface */
+interface Signable {
+ /** The message to be signed. */
+ digest(): string;
+}
+
+/** Signature object interface */
+interface Signature {
+ /** Encryption algorithm used */
+ algo: string;
+ /** Data to be signed. */
+ data: string;
+ /** Message digest */
+ digest: string;
+ /** Encryption engine used. */
+ engine: string;
+}
+
+/** Signer interface */
+interface Signer {
+ /**
+ * Get the private key fingerprint.
+ * @returns A private key fingerprint.
+ */
+ fingerprint(): string;
+ /** Event triggered on successful signing of message. */
+ onsign(signature: Signature): void;
+ /** Event triggered on successful verification of a signature. */
+ onverify(flag: boolean): void;
+ /**
+ * Load the message digest.
+ * @param material - A signable object.
+ * @returns true - If digest has been loaded successfully.
+ */
+ prepare(material: Signable): boolean;
+ /**
+ * Signs a message using a private key.
+ * @async
+ * @param digest - The message to be signed.
+ */
+ sign(digest: string): Promise<void>;
+ /**
+ * Verify that signature is valid.
+ * @param digest - The message that was signed.
+ * @param signature - The generated signature.
+ */
+ verify(digest: string, signature: Signature): void;
+}
+
+/** Provides functionality for signing and verifying signed messages. */
+class PGPSigner implements Signer {
+ /** Encryption algorithm used */
+ algo = 'sha256';
+ /** Message digest */
+ dgst: string;
+ /** Encryption engine used. */
+ engine = 'pgp';
+ /** A keystore holding pgp keys. */
+ keyStore: MutableKeyStore;
+ /** A service that provides logging capabilities. */
+ loggingService: LoggingService;
+ /** Event triggered on successful signing of message. */
+ onsign: (signature: Signature) => void;
+ /** Event triggered on successful verification of a signature. */
+ onverify: (flag: boolean) => void;
+ /** Generated signature */
+ signature: Signature;
+
+ /**
+ * Initializing the Signer.
+ * @param keyStore - A keystore holding pgp keys.
+ */
+ constructor(keyStore: MutableKeyStore) {
+ this.keyStore = keyStore;
+ this.onsign = (signature: Signature) => {};
+ this.onverify = (flag: boolean) => {};
+ }
+
+ /**
+ * Get the private key fingerprint.
+ * @returns A private key fingerprint.
+ */
+ public fingerprint(): string {
+ return this.keyStore.getFingerprint();
+ }
+
+ /**
+ * Load the message digest.
+ * @param material - A signable object.
+ * @returns true - If digest has been loaded successfully.
+ */
+ public prepare(material: Signable): boolean {
+ this.dgst = material.digest();
+ return true;
+ }
+
+ /**
+ * Signs a message using a private key.
+ * @async
+ * @param digest - The message to be signed.
+ */
+ 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);
+ });
+ }
+
+ /**
+ * Verify that signature is valid.
+ * @param digest - The message that was signed.
+ * @param signature - The generated signature.
+ */
+ 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);
+ });
+ }
+}
+
+/** @exports */
+export { PGPSigner, Signable, Signature, Signer };
+
+ +
+ src/app/_pgp/pgp-signer.ts
+
+
+
Signature object interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + algo + | +
+ algo:
+ |
+
+ Type : string
+
+ |
+
+ Encryption algorithm used + |
+
+ + data + | +
+ data:
+ |
+
+ Type : string
+
+ |
+
+ Data to be signed. + |
+
+ + digest + | +
+ digest:
+ |
+
+ Type : string
+
+ |
+
+ Message digest + |
+
+ + engine + | +
+ engine:
+ |
+
+ Type : string
+
+ |
+
+ Encryption engine used. + |
+
import * as openpgp from 'openpgp';
+
+// Application imports
+import { MutableKeyStore } from '@app/_pgp/pgp-key-store';
+import { LoggingService } from '@app/_services/logging.service';
+
+/** Signable object interface */
+interface Signable {
+ /** The message to be signed. */
+ digest(): string;
+}
+
+/** Signature object interface */
+interface Signature {
+ /** Encryption algorithm used */
+ algo: string;
+ /** Data to be signed. */
+ data: string;
+ /** Message digest */
+ digest: string;
+ /** Encryption engine used. */
+ engine: string;
+}
+
+/** Signer interface */
+interface Signer {
+ /**
+ * Get the private key fingerprint.
+ * @returns A private key fingerprint.
+ */
+ fingerprint(): string;
+ /** Event triggered on successful signing of message. */
+ onsign(signature: Signature): void;
+ /** Event triggered on successful verification of a signature. */
+ onverify(flag: boolean): void;
+ /**
+ * Load the message digest.
+ * @param material - A signable object.
+ * @returns true - If digest has been loaded successfully.
+ */
+ prepare(material: Signable): boolean;
+ /**
+ * Signs a message using a private key.
+ * @async
+ * @param digest - The message to be signed.
+ */
+ sign(digest: string): Promise<void>;
+ /**
+ * Verify that signature is valid.
+ * @param digest - The message that was signed.
+ * @param signature - The generated signature.
+ */
+ verify(digest: string, signature: Signature): void;
+}
+
+/** Provides functionality for signing and verifying signed messages. */
+class PGPSigner implements Signer {
+ /** Encryption algorithm used */
+ algo = 'sha256';
+ /** Message digest */
+ dgst: string;
+ /** Encryption engine used. */
+ engine = 'pgp';
+ /** A keystore holding pgp keys. */
+ keyStore: MutableKeyStore;
+ /** A service that provides logging capabilities. */
+ loggingService: LoggingService;
+ /** Event triggered on successful signing of message. */
+ onsign: (signature: Signature) => void;
+ /** Event triggered on successful verification of a signature. */
+ onverify: (flag: boolean) => void;
+ /** Generated signature */
+ signature: Signature;
+
+ /**
+ * Initializing the Signer.
+ * @param keyStore - A keystore holding pgp keys.
+ */
+ constructor(keyStore: MutableKeyStore) {
+ this.keyStore = keyStore;
+ this.onsign = (signature: Signature) => {};
+ this.onverify = (flag: boolean) => {};
+ }
+
+ /**
+ * Get the private key fingerprint.
+ * @returns A private key fingerprint.
+ */
+ public fingerprint(): string {
+ return this.keyStore.getFingerprint();
+ }
+
+ /**
+ * Load the message digest.
+ * @param material - A signable object.
+ * @returns true - If digest has been loaded successfully.
+ */
+ public prepare(material: Signable): boolean {
+ this.dgst = material.digest();
+ return true;
+ }
+
+ /**
+ * Signs a message using a private key.
+ * @async
+ * @param digest - The message to be signed.
+ */
+ 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);
+ });
+ }
+
+ /**
+ * Verify that signature is valid.
+ * @param digest - The message that was signed.
+ * @param signature - The generated signature.
+ */
+ 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);
+ });
+ }
+}
+
+/** @exports */
+export { PGPSigner, Signable, Signature, Signer };
+
+ +
+ src/app/_models/account.ts
+
+
+
Meta signature interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + algo + | +
+ algo:
+ |
+
+ Type : string
+
+ |
+
+ Algorithm used + |
+
+ + data + | +
+ data:
+ |
+
+ Type : string
+
+ |
+
+ Data that was signed. + |
+
+ + digest + | +
+ digest:
+ |
+
+ Type : string
+
+ |
+
+ Message digest + |
+
+ + engine + | +
+ engine:
+ |
+
+ Type : string
+
+ |
+
+ Encryption engine used. + |
+
interface AccountDetails {
+ /** Age of user */
+ age?: string;
+ /** Token balance on account */
+ balance?: number;
+ /** Business category of user. */
+ category?: string;
+ /** Account registration day */
+ date_registered: number;
+ /** User's gender */
+ gender: string;
+ /** Account identifiers */
+ identities: {
+ evm: {
+ 'bloxberg:8996': string[];
+ 'oldchain:1': string[];
+ };
+ latitude: number;
+ longitude: number;
+ };
+ /** User's location */
+ location: {
+ area?: string;
+ area_name: string;
+ area_type?: string;
+ };
+ /** Products or services provided by user. */
+ products: string[];
+ /** Type of account */
+ type?: string;
+ /** Personal identifying information of user */
+ vcard: {
+ email: [
+ {
+ value: string;
+ }
+ ];
+ fn: [
+ {
+ value: string;
+ }
+ ];
+ n: [
+ {
+ value: string[];
+ }
+ ];
+ tel: [
+ {
+ meta: {
+ TYP: string[];
+ };
+ value: string;
+ }
+ ];
+ version: [
+ {
+ value: string;
+ }
+ ];
+ };
+}
+
+/** Meta signature interface */
+interface Signature {
+ /** Algorithm used */
+ algo: string;
+ /** Data that was signed. */
+ data: string;
+ /** Message digest */
+ digest: string;
+ /** Encryption engine used. */
+ engine: string;
+}
+
+/** Meta object interface */
+interface Meta {
+ /** Account details */
+ data: AccountDetails;
+ /** Meta store id */
+ id: string;
+ /** Signature used during write. */
+ signature: Signature;
+}
+
+/** Meta response interface */
+interface MetaResponse {
+ /** Meta store id */
+ id: string;
+ /** Meta object */
+ m: Meta;
+}
+
+/** Default account data object */
+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',
+ },
+ ],
+ },
+};
+
+/** @exports */
+export { AccountDetails, Meta, MetaResponse, Signature, defaultAccount };
+
+ +
+ src/app/_pgp/pgp-signer.ts
+
+
+
Signer interface
+ + + + +
+ Methods+ |
+
+ + | +
+ + + + fingerprint + + + + | +
+fingerprint()
+ |
+
+ Defined in src/app/_pgp/pgp-signer.ts:32
+ |
+
+ Get the private key fingerprint. +
+
+
+ Returns :
+ string
+
+
+
+ A private key fingerprint. + + |
+
+ + + + onsign + + + + | +||||||
+onsign(signature: Signature)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-signer.ts:34
+ |
+ ||||||
+ Event triggered on successful signing of message. +
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + onverify + + + + | +||||||
+onverify(flag: boolean)
+ |
+ ||||||
+ Defined in src/app/_pgp/pgp-signer.ts:36
+ |
+ ||||||
+ Event triggered on successful verification of a signature. +
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
+ + + + prepare + + + + | +||||||||
+prepare(material: Signable)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-signer.ts:42
+ |
+ ||||||||
+ Load the message digest. +
+ Parameters :
+
+
+
+
+ Returns :
+ boolean
+
+
+
+ true - If digest has been loaded successfully. + + |
+
+ + + + sign + + + + | +||||||||
+sign(digest: string)
+ |
+ ||||||||
+ Defined in src/app/_pgp/pgp-signer.ts:48
+ |
+ ||||||||
+ Signs a message using a private key. +
+ Parameters :
+
+
+
+
+ Returns :
+ Promise<void>
+
+
+
+
+ |
+
+ + + + verify + + + + | +||||||||||||
+verify(digest: string, signature: Signature)
+ |
+ ||||||||||||
+ Defined in src/app/_pgp/pgp-signer.ts:54
+ |
+ ||||||||||||
+ Verify that signature is valid. +
+ Parameters :
+
+
+
+
+ Returns :
+ void
+
+
+
+
+ |
+
import * as openpgp from 'openpgp';
+
+// Application imports
+import { MutableKeyStore } from '@app/_pgp/pgp-key-store';
+import { LoggingService } from '@app/_services/logging.service';
+
+/** Signable object interface */
+interface Signable {
+ /** The message to be signed. */
+ digest(): string;
+}
+
+/** Signature object interface */
+interface Signature {
+ /** Encryption algorithm used */
+ algo: string;
+ /** Data to be signed. */
+ data: string;
+ /** Message digest */
+ digest: string;
+ /** Encryption engine used. */
+ engine: string;
+}
+
+/** Signer interface */
+interface Signer {
+ /**
+ * Get the private key fingerprint.
+ * @returns A private key fingerprint.
+ */
+ fingerprint(): string;
+ /** Event triggered on successful signing of message. */
+ onsign(signature: Signature): void;
+ /** Event triggered on successful verification of a signature. */
+ onverify(flag: boolean): void;
+ /**
+ * Load the message digest.
+ * @param material - A signable object.
+ * @returns true - If digest has been loaded successfully.
+ */
+ prepare(material: Signable): boolean;
+ /**
+ * Signs a message using a private key.
+ * @async
+ * @param digest - The message to be signed.
+ */
+ sign(digest: string): Promise<void>;
+ /**
+ * Verify that signature is valid.
+ * @param digest - The message that was signed.
+ * @param signature - The generated signature.
+ */
+ verify(digest: string, signature: Signature): void;
+}
+
+/** Provides functionality for signing and verifying signed messages. */
+class PGPSigner implements Signer {
+ /** Encryption algorithm used */
+ algo = 'sha256';
+ /** Message digest */
+ dgst: string;
+ /** Encryption engine used. */
+ engine = 'pgp';
+ /** A keystore holding pgp keys. */
+ keyStore: MutableKeyStore;
+ /** A service that provides logging capabilities. */
+ loggingService: LoggingService;
+ /** Event triggered on successful signing of message. */
+ onsign: (signature: Signature) => void;
+ /** Event triggered on successful verification of a signature. */
+ onverify: (flag: boolean) => void;
+ /** Generated signature */
+ signature: Signature;
+
+ /**
+ * Initializing the Signer.
+ * @param keyStore - A keystore holding pgp keys.
+ */
+ constructor(keyStore: MutableKeyStore) {
+ this.keyStore = keyStore;
+ this.onsign = (signature: Signature) => {};
+ this.onverify = (flag: boolean) => {};
+ }
+
+ /**
+ * Get the private key fingerprint.
+ * @returns A private key fingerprint.
+ */
+ public fingerprint(): string {
+ return this.keyStore.getFingerprint();
+ }
+
+ /**
+ * Load the message digest.
+ * @param material - A signable object.
+ * @returns true - If digest has been loaded successfully.
+ */
+ public prepare(material: Signable): boolean {
+ this.dgst = material.digest();
+ return true;
+ }
+
+ /**
+ * Signs a message using a private key.
+ * @async
+ * @param digest - The message to be signed.
+ */
+ 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);
+ });
+ }
+
+ /**
+ * Verify that signature is valid.
+ * @param digest - The message that was signed.
+ * @param signature - The generated signature.
+ */
+ 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);
+ });
+ }
+}
+
+/** @exports */
+export { PGPSigner, Signable, Signature, Signer };
+
+ +
+ src/app/_models/staff.ts
+
+
+
Staff object interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + comment + | +
+ comment:
+ |
+
+ Type : string
+
+ |
+
+ Comment made on the public key. + |
+
+ + email + | +
+ email:
+ |
+
+ Type : string
+
+ |
+
+ Email used to create the public key. + |
+
+ + name + | +
+ name:
+ |
+
+ Type : string
+
+ |
+
+ Name of the owner of the public key + |
+
+ + tag + | +
+ tag:
+ |
+
+ Type : number
+
+ |
+
+ Tags added to the public key. + |
+
+ + userid + | +
+ userid:
+ |
+
+ Type : string
+
+ |
+
+ User ID of the owner of the public key. + |
+
interface Staff {
+ /** Comment made on the public key. */
+ comment: string;
+ /** Email used to create the public key. */
+ email: string;
+ /** Name of the owner of the public key */
+ name: string;
+ /** Tags added to the public key. */
+ tag: number;
+ /** User ID of the owner of the public key. */
+ userid: string;
+}
+
+/** @exports */
+export { Staff };
+
+ +
+ src/app/_models/token.ts
+
+
+
Token object interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + address + | +
+ address:
+ |
+
+ Type : string
+
+ |
+
+ Address of the deployed token contract. + |
+
+ + decimals + | +
+ decimals:
+ |
+
+ Type : string
+
+ |
+
+ Number of decimals to convert to smallest denomination of the token. + |
+
+ + name + | +
+ name:
+ |
+
+ Type : string
+
+ |
+
+ Name of the token. + |
+
+ + owner + | +
+ owner:
+ |
+
+ Type : string
+
+ |
+
+ Optional + | +
+ Address of account that deployed token. + |
+
+ + reserveRatio + | +
+ reserveRatio:
+ |
+
+ Type : string
+
+ |
+
+ Optional + | +
+ Token reserve to token minting ratio. + |
+
+ + reserves + | +
+ reserves:
+ |
+
+ Type : literal type
+
+ |
+
+ Optional + | +
+ Token reserve information + |
+
+ + supply + | +
+ supply:
+ |
+
+ Type : string
+
+ |
+
+ Total token supply. + |
+
+ + symbol + | +
+ symbol:
+ |
+
+ Type : string
+
+ |
+
+ The unique token symbol. + |
+
interface Token {
+ /** Address of the deployed token contract. */
+ address: string;
+ /** Number of decimals to convert to smallest denomination of the token. */
+ decimals: string;
+ /** Name of the token. */
+ name: string;
+ /** Address of account that deployed token. */
+ owner?: string;
+ /** Token reserve to token minting ratio. */
+ reserveRatio?: string;
+ /** Token reserve information */
+ reserves?: {
+ '0xa686005CE37Dce7738436256982C3903f2E4ea8E'?: {
+ weight: string;
+ balance: string;
+ };
+ };
+ /** Total token supply. */
+ supply: string;
+ /** The unique token symbol. */
+ symbol: string;
+}
+
+/** @exports */
+export { Token };
+
+ +
+ src/app/_models/transaction.ts
+
+
+
Transaction object interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + from + | +
+ from:
+ |
+
+ Type : string
+
+ |
+
+ Address of the transaction sender. + |
+
+ + recipient + | +
+ recipient:
+ |
+
+ Type : AccountDetails
+
+ |
+
+ Account information of the transaction recipient. + |
+
+ + sender + | +
+ sender:
+ |
+
+ Type : AccountDetails
+
+ |
+
+ Account information of the transaction sender. + |
+
+ + to + | +
+ to:
+ |
+
+ Type : string
+
+ |
+
+ Address of the transaction recipient. + |
+
+ + token + | +
+ token:
+ |
+
+ Type : TxToken
+
+ |
+
+ Transaction token information. + |
+
+ + tx + | +
+ tx:
+ |
+
+ Type : Tx
+
+ |
+
+ Transaction mining information. + |
+
+ + type + | +
+ type:
+ |
+
+ Type : string
+
+ |
+
+ Optional + | +
+ Type of transaction. + |
+
+ + value + | +
+ value:
+ |
+
+ Type : number
+
+ |
+
+ Amount of tokens transacted. + |
+
import { AccountDetails } from '@app/_models/account';
+
+/** Conversion object interface */
+interface Conversion {
+ /** Final transaction token information. */
+ destinationToken: TxToken;
+ /** Initial transaction token amount. */
+ fromValue: number;
+ /** Initial transaction token information. */
+ sourceToken: TxToken;
+ /** Final transaction token amount. */
+ toValue: number;
+ /** Address of the initiator of the conversion. */
+ trader: string;
+ /** Conversion mining information. */
+ tx: Tx;
+ /** Account information of the initiator of the conversion. */
+ user: AccountDetails;
+}
+
+/** Transaction object interface */
+interface Transaction {
+ /** Address of the transaction sender. */
+ from: string;
+ /** Account information of the transaction recipient. */
+ recipient: AccountDetails;
+ /** Account information of the transaction sender. */
+ sender: AccountDetails;
+ /** Address of the transaction recipient. */
+ to: string;
+ /** Transaction token information. */
+ token: TxToken;
+ /** Transaction mining information. */
+ tx: Tx;
+ /** Type of transaction. */
+ type?: string;
+ /** Amount of tokens transacted. */
+ value: number;
+}
+
+/** Transaction data interface */
+interface Tx {
+ /** Transaction block number. */
+ block: number;
+ /** Transaction mining status. */
+ success: boolean;
+ /** Time transaction was mined. */
+ timestamp: number;
+ /** Hash generated by transaction. */
+ txHash: string;
+ /** Index of transaction in block. */
+ txIndex: number;
+}
+
+/** Transaction token object interface */
+interface TxToken {
+ /** Address of the deployed token contract. */
+ address: string;
+ /** Name of the token. */
+ name: string;
+ /** The unique token symbol. */
+ symbol: string;
+}
+
+/** @exports */
+export { Conversion, Transaction, Tx, TxToken };
+
+ +
+ src/app/_models/transaction.ts
+
+
+
Transaction data interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + block + | +
+ block:
+ |
+
+ Type : number
+
+ |
+
+ Transaction block number. + |
+
+ + success + | +
+ success:
+ |
+
+ Type : boolean
+
+ |
+
+ Transaction mining status. + |
+
+ + timestamp + | +
+ timestamp:
+ |
+
+ Type : number
+
+ |
+
+ Time transaction was mined. + |
+
+ + txHash + | +
+ txHash:
+ |
+
+ Type : string
+
+ |
+
+ Hash generated by transaction. + |
+
+ + txIndex + | +
+ txIndex:
+ |
+
+ Type : number
+
+ |
+
+ Index of transaction in block. + |
+
import { AccountDetails } from '@app/_models/account';
+
+/** Conversion object interface */
+interface Conversion {
+ /** Final transaction token information. */
+ destinationToken: TxToken;
+ /** Initial transaction token amount. */
+ fromValue: number;
+ /** Initial transaction token information. */
+ sourceToken: TxToken;
+ /** Final transaction token amount. */
+ toValue: number;
+ /** Address of the initiator of the conversion. */
+ trader: string;
+ /** Conversion mining information. */
+ tx: Tx;
+ /** Account information of the initiator of the conversion. */
+ user: AccountDetails;
+}
+
+/** Transaction object interface */
+interface Transaction {
+ /** Address of the transaction sender. */
+ from: string;
+ /** Account information of the transaction recipient. */
+ recipient: AccountDetails;
+ /** Account information of the transaction sender. */
+ sender: AccountDetails;
+ /** Address of the transaction recipient. */
+ to: string;
+ /** Transaction token information. */
+ token: TxToken;
+ /** Transaction mining information. */
+ tx: Tx;
+ /** Type of transaction. */
+ type?: string;
+ /** Amount of tokens transacted. */
+ value: number;
+}
+
+/** Transaction data interface */
+interface Tx {
+ /** Transaction block number. */
+ block: number;
+ /** Transaction mining status. */
+ success: boolean;
+ /** Time transaction was mined. */
+ timestamp: number;
+ /** Hash generated by transaction. */
+ txHash: string;
+ /** Index of transaction in block. */
+ txIndex: number;
+}
+
+/** Transaction token object interface */
+interface TxToken {
+ /** Address of the deployed token contract. */
+ address: string;
+ /** Name of the token. */
+ name: string;
+ /** The unique token symbol. */
+ symbol: string;
+}
+
+/** @exports */
+export { Conversion, Transaction, Tx, TxToken };
+
+ +
+ src/app/_models/transaction.ts
+
+
+
Transaction token object interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + address + | +
+ address:
+ |
+
+ Type : string
+
+ |
+
+ Address of the deployed token contract. + |
+
+ + name + | +
+ name:
+ |
+
+ Type : string
+
+ |
+
+ Name of the token. + |
+
+ + symbol + | +
+ symbol:
+ |
+
+ Type : string
+
+ |
+
+ The unique token symbol. + |
+
import { AccountDetails } from '@app/_models/account';
+
+/** Conversion object interface */
+interface Conversion {
+ /** Final transaction token information. */
+ destinationToken: TxToken;
+ /** Initial transaction token amount. */
+ fromValue: number;
+ /** Initial transaction token information. */
+ sourceToken: TxToken;
+ /** Final transaction token amount. */
+ toValue: number;
+ /** Address of the initiator of the conversion. */
+ trader: string;
+ /** Conversion mining information. */
+ tx: Tx;
+ /** Account information of the initiator of the conversion. */
+ user: AccountDetails;
+}
+
+/** Transaction object interface */
+interface Transaction {
+ /** Address of the transaction sender. */
+ from: string;
+ /** Account information of the transaction recipient. */
+ recipient: AccountDetails;
+ /** Account information of the transaction sender. */
+ sender: AccountDetails;
+ /** Address of the transaction recipient. */
+ to: string;
+ /** Transaction token information. */
+ token: TxToken;
+ /** Transaction mining information. */
+ tx: Tx;
+ /** Type of transaction. */
+ type?: string;
+ /** Amount of tokens transacted. */
+ value: number;
+}
+
+/** Transaction data interface */
+interface Tx {
+ /** Transaction block number. */
+ block: number;
+ /** Transaction mining status. */
+ success: boolean;
+ /** Time transaction was mined. */
+ timestamp: number;
+ /** Hash generated by transaction. */
+ txHash: string;
+ /** Index of transaction in block. */
+ txIndex: number;
+}
+
+/** Transaction token object interface */
+interface TxToken {
+ /** Address of the deployed token contract. */
+ address: string;
+ /** Name of the token. */
+ name: string;
+ /** The unique token symbol. */
+ symbol: string;
+}
+
+/** @exports */
+export { Conversion, Transaction, Tx, TxToken };
+
+ +
+ src/app/_models/settings.ts
+
+
+
Web3 object interface
+ + + + +
+ Properties+ |
+
+ + | +
+ + engine + | +
+ engine:
+ |
+
+ Type : any
+
+ |
+
+ An active web3 instance connected to the blockchain network. + |
+
+ + provider + | +
+ provider:
+ |
+
+ Type : any
+
+ |
+
+ The connection socket to the blockchain network. + |
+
class Settings {
+ /** CIC Registry instance */
+ registry: any;
+ /** A resource for searching through blocks on the blockchain network. */
+ scanFilter: any;
+ /** Transaction Helper instance */
+ txHelper: any;
+ /** Web3 Object */
+ w3: W3 = {
+ engine: undefined,
+ provider: undefined,
+ };
+
+ /**
+ * Initialize the settings.
+ *
+ * @param scanFilter - A resource for searching through blocks on the blockchain network.
+ */
+ constructor(scanFilter: any) {
+ this.scanFilter = scanFilter;
+ }
+}
+
+/** Web3 object interface */
+interface W3 {
+ /** An active web3 instance connected to the blockchain network. */
+ engine: any;
+ /** The connection socket to the blockchain network. */
+ provider: any;
+}
+
+/** @exports */
+export { Settings, W3 };
+
+