Merge branch 'spencer/fix-account-index' into 'master'
Fix account index out of index error. See merge request grassrootseconomics/cic-staff-client!23
This commit is contained in:
		
						commit
						1dc751f15f
					
				@ -1,8 +1,8 @@
 | 
			
		||||
import { environment } from '@src/environments/environment';
 | 
			
		||||
import Web3 from 'web3';
 | 
			
		||||
import { Web3Service } from '@app/_services/web3.service';
 | 
			
		||||
 | 
			
		||||
const abi: Array<any> = require('@src/assets/js/block-sync/data/AccountsIndex.json');
 | 
			
		||||
const web3: Web3 = new Web3(environment.web3Provider);
 | 
			
		||||
const web3: Web3 = Web3Service.getInstance();
 | 
			
		||||
 | 
			
		||||
export class AccountIndex {
 | 
			
		||||
  contractAddress: string;
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
import Web3 from 'web3';
 | 
			
		||||
import { environment } from '@src/environments/environment';
 | 
			
		||||
import { Web3Service } from '@app/_services/web3.service';
 | 
			
		||||
 | 
			
		||||
const abi: Array<any> = require('@src/assets/js/block-sync/data/TokenUniqueSymbolIndex.json');
 | 
			
		||||
const web3: Web3 = new Web3(environment.web3Provider);
 | 
			
		||||
const web3: Web3 = Web3Service.getInstance();
 | 
			
		||||
 | 
			
		||||
export class TokenRegistry {
 | 
			
		||||
  contractAddress: string;
 | 
			
		||||
 | 
			
		||||
@ -1151,7 +1151,7 @@ export class MockBackendInterceptor implements HttpInterceptor {
 | 
			
		||||
      const queriedCategory: Category = categories.find((category) =>
 | 
			
		||||
        category.products.includes(stringFromUrl())
 | 
			
		||||
      );
 | 
			
		||||
      return ok(queriedCategory.name);
 | 
			
		||||
      return ok(queriedCategory.name || 'other');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getAreaNames(): Observable<HttpResponse<any>> {
 | 
			
		||||
@ -1163,7 +1163,7 @@ export class MockBackendInterceptor implements HttpInterceptor {
 | 
			
		||||
      const queriedAreaName: AreaName = areaNames.find((areaName) =>
 | 
			
		||||
        areaName.locations.includes(stringFromUrl())
 | 
			
		||||
      );
 | 
			
		||||
      return ok(queriedAreaName.name);
 | 
			
		||||
      return ok(queriedAreaName.name || 'other');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getAreaTypes(): Observable<HttpResponse<any>> {
 | 
			
		||||
@ -1175,7 +1175,7 @@ export class MockBackendInterceptor implements HttpInterceptor {
 | 
			
		||||
      const queriedAreaType: AreaType = areaTypes.find((areaType) =>
 | 
			
		||||
        areaType.area.includes(stringFromUrl())
 | 
			
		||||
      );
 | 
			
		||||
      return ok(queriedAreaType.name);
 | 
			
		||||
      return ok(queriedAreaType.name || 'other');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getAccountTypes(): Observable<HttpResponse<any>> {
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ async function personValidation(person: any): Promise<void> {
 | 
			
		||||
  const personValidationErrors: any = await validatePerson(person);
 | 
			
		||||
 | 
			
		||||
  if (personValidationErrors) {
 | 
			
		||||
    personValidationErrors.map((error) => console.error(`${error.message}`));
 | 
			
		||||
    personValidationErrors.map((error) => console.error(`${error.message}`, person, error));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ async function vcardValidation(vcard: any): Promise<void> {
 | 
			
		||||
  const vcardValidationErrors: any = await validateVcard(vcard);
 | 
			
		||||
 | 
			
		||||
  if (vcardValidationErrors) {
 | 
			
		||||
    vcardValidationErrors.map((error) => console.error(`${error.message}`));
 | 
			
		||||
    vcardValidationErrors.map((error) => console.error(`${error.message}`, vcard, error));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ interface Token {
 | 
			
		||||
  address: string;
 | 
			
		||||
  supply: string;
 | 
			
		||||
  decimals: string;
 | 
			
		||||
  reserves: {
 | 
			
		||||
  reserves?: {
 | 
			
		||||
    '0xa686005CE37Dce7738436256982C3903f2E4ea8E'?: {
 | 
			
		||||
      weight: string;
 | 
			
		||||
      balance: string;
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ const keyring = new openpgp.Keyring();
 | 
			
		||||
interface MutableKeyStore extends KeyStore {
 | 
			
		||||
  loadKeyring(): void;
 | 
			
		||||
  importKeyPair(publicKey: any, privateKey: any): Promise<void>;
 | 
			
		||||
  importPublicKey(publicKey: any): void;
 | 
			
		||||
  importPublicKey(publicKey: any): Promise<void>;
 | 
			
		||||
  importPrivateKey(privateKey: any): Promise<void>;
 | 
			
		||||
  getPublicKeys(): Array<any>;
 | 
			
		||||
  getTrustedKeys(): Array<any>;
 | 
			
		||||
@ -42,8 +42,8 @@ class MutablePgpKeyStore implements MutableKeyStore {
 | 
			
		||||
    await keyring.privateKeys.importKey(privateKey);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  importPublicKey(publicKey: any): void {
 | 
			
		||||
    keyring.publicKeys.importKey(publicKey);
 | 
			
		||||
  async importPublicKey(publicKey: any): Promise<void> {
 | 
			
		||||
    await keyring.publicKeys.importKey(publicKey);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async importPrivateKey(privateKey: any): Promise<void> {
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,8 @@ 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',
 | 
			
		||||
@ -14,6 +16,11 @@ import { HttpError, rejectBody } from '@app/_helpers/global-error-handler';
 | 
			
		||||
export class AuthService {
 | 
			
		||||
  sessionToken: any;
 | 
			
		||||
  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,
 | 
			
		||||
@ -190,10 +197,22 @@ export class AuthService {
 | 
			
		||||
    window.location.reload();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getTrustedUsers(): any {
 | 
			
		||||
    const trustedUsers: Array<any> = [];
 | 
			
		||||
    this.mutableKeyStore.getPublicKeys().forEach((key) => trustedUsers.push(key.users[0].userId));
 | 
			
		||||
    return trustedUsers;
 | 
			
		||||
  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> {
 | 
			
		||||
@ -211,4 +230,8 @@ export class AuthService {
 | 
			
		||||
  getPrivateKey(): any {
 | 
			
		||||
    return this.mutableKeyStore.getPrivateKey();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getPrivateKeyInfo(): any {
 | 
			
		||||
    return this.getPrivateKey().users[0].userId;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ 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',
 | 
			
		||||
@ -16,31 +17,33 @@ export class BlockSyncService {
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private transactionService: TransactionService,
 | 
			
		||||
    private loggingService: LoggingService,
 | 
			
		||||
    private registryService: RegistryService
 | 
			
		||||
    private loggingService: LoggingService
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  blockSync(address: string = null, offset: number = 0, limit: number = 100): void {
 | 
			
		||||
  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 = this.registryService.getWeb3();
 | 
			
		||||
    settings.registry = this.registryService.getRegistry();
 | 
			
		||||
    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.newTransferEvent(transaction));
 | 
			
		||||
      window.dispatchEvent(this.newEvent(transaction, 'cic_transfer'));
 | 
			
		||||
    };
 | 
			
		||||
    settings.txHelper.onconversion = async (transaction: any): Promise<any> => {
 | 
			
		||||
      window.dispatchEvent(this.newConversionEvent(transaction));
 | 
			
		||||
      window.dispatchEvent(this.newEvent(transaction, 'cic_convert'));
 | 
			
		||||
    };
 | 
			
		||||
    settings.registry.onload = (addressReturned: number): void => {
 | 
			
		||||
      this.loggingService.sendInfoLevelMessage(`Loaded network contracts ${addressReturned}`);
 | 
			
		||||
      this.readyStateProcessor(settings, readyStateElements.network, address, offset, limit);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    settings.registry.load();
 | 
			
		||||
    // settings.registry.onload = (addressReturned: string): void => {
 | 
			
		||||
    //   this.loggingService.sendInfoLevelMessage(`Loaded network contracts ${addressReturned}`);
 | 
			
		||||
    //   this.readyStateProcessor(settings, readyStateElements.network, address, offset, limit);
 | 
			
		||||
    // };
 | 
			
		||||
    this.readyStateProcessor(settings, readyStateElements.network, address, offset, limit);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  readyStateProcessor(
 | 
			
		||||
@ -78,16 +81,8 @@ export class BlockSyncService {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  newTransferEvent(tx: any): any {
 | 
			
		||||
    return new CustomEvent('cic_transfer', {
 | 
			
		||||
      detail: {
 | 
			
		||||
        tx,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  newConversionEvent(tx: any): any {
 | 
			
		||||
    return new CustomEvent('cic_convert', {
 | 
			
		||||
  newEvent(tx: any, eventType: string): any {
 | 
			
		||||
    return new CustomEvent(eventType, {
 | 
			
		||||
      detail: {
 | 
			
		||||
        tx,
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
@ -6,3 +6,4 @@ export * from '@app/_services/block-sync.service';
 | 
			
		||||
export * from '@app/_services/location.service';
 | 
			
		||||
export * from '@app/_services/logging.service';
 | 
			
		||||
export * from '@app/_services/error-dialog.service';
 | 
			
		||||
export * from '@app/_services/web3.service';
 | 
			
		||||
 | 
			
		||||
@ -1,33 +1,30 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import Web3 from 'web3';
 | 
			
		||||
import { environment } from '@src/environments/environment';
 | 
			
		||||
import { CICRegistry, FileGetter } from 'cic-client';
 | 
			
		||||
import { HttpGetter } from '@app/_helpers';
 | 
			
		||||
import { Web3Service } from '@app/_services/web3.service';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root',
 | 
			
		||||
})
 | 
			
		||||
export class RegistryService {
 | 
			
		||||
  web3: Web3 = new Web3(environment.web3Provider);
 | 
			
		||||
  fileGetter: FileGetter = new HttpGetter();
 | 
			
		||||
  registry: CICRegistry = new CICRegistry(
 | 
			
		||||
    this.web3,
 | 
			
		||||
    environment.registryAddress,
 | 
			
		||||
    'Registry',
 | 
			
		||||
    this.fileGetter,
 | 
			
		||||
    ['../../assets/js/block-sync/data']
 | 
			
		||||
  );
 | 
			
		||||
  static fileGetter: FileGetter = new HttpGetter();
 | 
			
		||||
  private static registry: CICRegistry;
 | 
			
		||||
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.registry.declaratorHelper.addTrust(environment.trustedDeclaratorAddress);
 | 
			
		||||
    this.registry.load();
 | 
			
		||||
  }
 | 
			
		||||
  constructor() {}
 | 
			
		||||
 | 
			
		||||
  getRegistry(): any {
 | 
			
		||||
    return this.registry;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getWeb3(): any {
 | 
			
		||||
    return this.web3;
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
import { EventEmitter, Injectable } from '@angular/core';
 | 
			
		||||
import { environment } from '@src/environments/environment';
 | 
			
		||||
import { BehaviorSubject, Observable } from 'rxjs';
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { CICRegistry } from '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',
 | 
			
		||||
@ -12,29 +12,65 @@ import { RegistryService } from '@app/_services/registry.service';
 | 
			
		||||
export class TokenService {
 | 
			
		||||
  registry: CICRegistry;
 | 
			
		||||
  tokenRegistry: TokenRegistry;
 | 
			
		||||
  LoadEvent: EventEmitter<number> = new EventEmitter<number>();
 | 
			
		||||
  onload: (status: boolean) => void;
 | 
			
		||||
  tokens: Array<Token> = [];
 | 
			
		||||
  private tokensList: BehaviorSubject<Array<Token>> = new BehaviorSubject<Array<Token>>(this.tokens);
 | 
			
		||||
  tokensSubject: Observable<Array<Token>> = this.tokensList.asObservable();
 | 
			
		||||
 | 
			
		||||
  constructor(private httpClient: HttpClient, private registryService: RegistryService) {
 | 
			
		||||
    this.registry = registryService.getRegistry();
 | 
			
		||||
    this.registry.load();
 | 
			
		||||
  constructor(private httpClient: HttpClient) {}
 | 
			
		||||
 | 
			
		||||
  async init(): Promise<void> {
 | 
			
		||||
    this.registry = await RegistryService.getRegistry();
 | 
			
		||||
    this.registry.onload = async (address: string): Promise<void> => {
 | 
			
		||||
      this.tokenRegistry = new TokenRegistry(
 | 
			
		||||
        await this.registry.getContractAddressByName('TokenRegistry')
 | 
			
		||||
      );
 | 
			
		||||
      this.LoadEvent.next(Date.now());
 | 
			
		||||
      this.onload(this.tokenRegistry !== undefined);
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async getTokens(): Promise<Array<Promise<string>>> {
 | 
			
		||||
  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();
 | 
			
		||||
    return Array.from({ length: count }, async (v, i) => await this.tokenRegistry.entry(i));
 | 
			
		||||
    for (let i = 0; i < count; i++) {
 | 
			
		||||
      const token: Token = await this.getTokenByAddress(await this.tokenRegistry.entry(i));
 | 
			
		||||
      this.addToken(token);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getTokenBySymbol(symbol: string): Observable<any> {
 | 
			
		||||
    return this.httpClient.get(`${environment.cicCacheUrl}/tokens/${symbol}`);
 | 
			
		||||
  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 getTokenBalance(address: string): Promise<number> {
 | 
			
		||||
  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 sarafuToken = await this.registry.addToken(await this.tokenRegistry.entry(0));
 | 
			
		||||
    return await sarafuToken.methods.balanceOf(address).call();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ import { HttpClient } from '@angular/common/http';
 | 
			
		||||
import { CICRegistry } from '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({
 | 
			
		||||
@ -34,12 +35,15 @@ export class TransactionService {
 | 
			
		||||
    private httpClient: HttpClient,
 | 
			
		||||
    private authService: AuthService,
 | 
			
		||||
    private userService: UserService,
 | 
			
		||||
    private loggingService: LoggingService,
 | 
			
		||||
    private registryService: RegistryService
 | 
			
		||||
    private loggingService: LoggingService
 | 
			
		||||
  ) {
 | 
			
		||||
    this.web3 = this.registryService.getWeb3();
 | 
			
		||||
    this.registry = registryService.getRegistry();
 | 
			
		||||
    this.registry.load();
 | 
			
		||||
    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> {
 | 
			
		||||
@ -47,7 +51,7 @@ export class TransactionService {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getAddressTransactions(address: string, offset: number, limit: number): Observable<any> {
 | 
			
		||||
    return this.httpClient.get(`${environment.cicCacheUrl}/tx/${address}/${offset}/${limit}`);
 | 
			
		||||
    return this.httpClient.get(`${environment.cicCacheUrl}/tx/user/${address}/${offset}/${limit}`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async setTransaction(transaction, cacheSize: number): Promise<void> {
 | 
			
		||||
@ -62,10 +66,11 @@ export class TransactionService {
 | 
			
		||||
        .pipe(first())
 | 
			
		||||
        .subscribe(
 | 
			
		||||
          (res) => {
 | 
			
		||||
            transaction.sender = this.getAccountInfo(res.body);
 | 
			
		||||
            transaction.sender = this.getAccountInfo(res, cacheSize);
 | 
			
		||||
          },
 | 
			
		||||
          (error) => {
 | 
			
		||||
            transaction.sender = defaultAccount;
 | 
			
		||||
            this.userService.addAccount(defaultAccount, cacheSize);
 | 
			
		||||
          }
 | 
			
		||||
        );
 | 
			
		||||
      this.userService
 | 
			
		||||
@ -73,10 +78,11 @@ export class TransactionService {
 | 
			
		||||
        .pipe(first())
 | 
			
		||||
        .subscribe(
 | 
			
		||||
          (res) => {
 | 
			
		||||
            transaction.recipient = this.getAccountInfo(res.body);
 | 
			
		||||
            transaction.recipient = this.getAccountInfo(res, cacheSize);
 | 
			
		||||
          },
 | 
			
		||||
          (error) => {
 | 
			
		||||
            transaction.recipient = defaultAccount;
 | 
			
		||||
            this.userService.addAccount(defaultAccount, cacheSize);
 | 
			
		||||
          }
 | 
			
		||||
        );
 | 
			
		||||
    } finally {
 | 
			
		||||
@ -97,10 +103,11 @@ export class TransactionService {
 | 
			
		||||
        .pipe(first())
 | 
			
		||||
        .subscribe(
 | 
			
		||||
          (res) => {
 | 
			
		||||
            conversion.sender = conversion.recipient = this.getAccountInfo(res.body);
 | 
			
		||||
            conversion.sender = conversion.recipient = this.getAccountInfo(res);
 | 
			
		||||
          },
 | 
			
		||||
          (error) => {
 | 
			
		||||
            conversion.sender = conversion.recipient = defaultAccount;
 | 
			
		||||
            this.userService.addAccount(defaultAccount, cacheSize);
 | 
			
		||||
          }
 | 
			
		||||
        );
 | 
			
		||||
    } finally {
 | 
			
		||||
@ -109,9 +116,16 @@ export class TransactionService {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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 = cacheSize;
 | 
			
		||||
      this.transactions.length = Math.min(this.transactions.length, cacheSize);
 | 
			
		||||
    }
 | 
			
		||||
    this.transactionList.next(this.transactions);
 | 
			
		||||
  }
 | 
			
		||||
@ -121,9 +135,10 @@ export class TransactionService {
 | 
			
		||||
    this.transactionList.next(this.transactions);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getAccountInfo(account: string): any {
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -133,41 +148,43 @@ export class TransactionService {
 | 
			
		||||
    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}`);
 | 
			
		||||
    this.registry.onload = async (addressReturned: string): Promise<void> => {
 | 
			
		||||
      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}`);
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -39,20 +39,20 @@ export class UserService {
 | 
			
		||||
    private httpClient: HttpClient,
 | 
			
		||||
    private loggingService: LoggingService,
 | 
			
		||||
    private tokenService: TokenService,
 | 
			
		||||
    private registryService: RegistryService,
 | 
			
		||||
    private authService: AuthService
 | 
			
		||||
  ) {
 | 
			
		||||
    this.authService.init().then(() => {
 | 
			
		||||
      this.keystore = authService.mutableKeyStore;
 | 
			
		||||
      this.signer = new PGPSigner(this.keystore);
 | 
			
		||||
    });
 | 
			
		||||
    this.registry = registryService.getRegistry();
 | 
			
		||||
    this.registry.load();
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  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.get(`${environment.cicUssdUrl}/pin`, { params });
 | 
			
		||||
    return this.httpClient.put(`${environment.cicUssdUrl}/pin`, { params });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getAccountStatus(phone: string): Observable<any> {
 | 
			
		||||
@ -183,9 +183,7 @@ export class UserService {
 | 
			
		||||
      'AccountRegistry'
 | 
			
		||||
    );
 | 
			
		||||
    const accountIndexQuery = new AccountIndex(accountIndexAddress);
 | 
			
		||||
    const accountAddresses: Array<string> = await accountIndexQuery.last(
 | 
			
		||||
      await accountIndexQuery.totalAccounts()
 | 
			
		||||
    );
 | 
			
		||||
    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);
 | 
			
		||||
@ -203,16 +201,14 @@ export class UserService {
 | 
			
		||||
        const account: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
 | 
			
		||||
        const accountInfo = account.m.data;
 | 
			
		||||
        await personValidation(accountInfo);
 | 
			
		||||
        accountInfo.balance = await this.tokenService.getTokenBalance(
 | 
			
		||||
          accountInfo.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
 | 
			
		||||
        );
 | 
			
		||||
        this.tokenService.onload = async (status: boolean): Promise<void> => {
 | 
			
		||||
          accountInfo.balance = await this.tokenService.getTokenBalance(
 | 
			
		||||
            accountInfo.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
 | 
			
		||||
          );
 | 
			
		||||
        };
 | 
			
		||||
        accountInfo.vcard = vCard.parse(atob(accountInfo.vcard));
 | 
			
		||||
        await vcardValidation(accountInfo.vcard);
 | 
			
		||||
        this.accounts.unshift(accountInfo);
 | 
			
		||||
        if (this.accounts.length > limit) {
 | 
			
		||||
          this.accounts.length = limit;
 | 
			
		||||
        }
 | 
			
		||||
        this.accountsList.next(this.accounts);
 | 
			
		||||
        this.addAccount(accountInfo, limit);
 | 
			
		||||
        accountSubject.next(accountInfo);
 | 
			
		||||
      });
 | 
			
		||||
    return accountSubject.asObservable();
 | 
			
		||||
@ -264,4 +260,23 @@ export class UserService {
 | 
			
		||||
  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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								src/app/_services/web3.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/app/_services/web3.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
import { TestBed } from '@angular/core/testing';
 | 
			
		||||
 | 
			
		||||
import { Web3Service } from './web3.service';
 | 
			
		||||
 | 
			
		||||
describe('Web3Service', () => {
 | 
			
		||||
  let service: Web3Service;
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    TestBed.configureTestingModule({});
 | 
			
		||||
    service = TestBed.inject(Web3Service);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should be created', () => {
 | 
			
		||||
    expect(service).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										19
									
								
								src/app/_services/web3.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/app/_services/web3.service.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,2 +1 @@
 | 
			
		||||
<app-network-status></app-network-status>
 | 
			
		||||
<router-outlet (activate)="onResize(mediaQuery)"></router-outlet>
 | 
			
		||||
 | 
			
		||||
@ -27,28 +27,23 @@ export class AppComponent implements OnInit {
 | 
			
		||||
    private errorDialogService: ErrorDialogService,
 | 
			
		||||
    private swUpdate: SwUpdate
 | 
			
		||||
  ) {
 | 
			
		||||
    (async () => {
 | 
			
		||||
      try {
 | 
			
		||||
        await this.authService.init();
 | 
			
		||||
        // this.authService.getPublicKeys()
 | 
			
		||||
        //   .pipe(catchError(async (error) => {
 | 
			
		||||
        //     this.loggingService.sendErrorLevelMessage('Unable to load trusted public keys.', this, {error});
 | 
			
		||||
        //     this.errorDialogService.openDialog({message: 'Trusted keys endpoint can\'t be reached. Please try again later.'});
 | 
			
		||||
        // })).subscribe(this.authService.mutableKeyStore.importPublicKey);
 | 
			
		||||
        const publicKeys = await this.authService.getPublicKeys();
 | 
			
		||||
        await this.authService.mutableKeyStore.importPublicKey(publicKeys);
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        this.errorDialogService.openDialog({
 | 
			
		||||
          message: 'Trusted keys endpoint cannot be reached. Please try again later.',
 | 
			
		||||
        });
 | 
			
		||||
        // TODO do something to halt user progress...show a sad cicada page 🦗?
 | 
			
		||||
      }
 | 
			
		||||
    })();
 | 
			
		||||
    this.mediaQuery.addEventListener('change', this.onResize);
 | 
			
		||||
    this.onResize(this.mediaQuery);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
  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?')) {
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,9 @@
 | 
			
		||||
<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 bg-dark">
 | 
			
		||||
        <mat-card-title class="card-header pt-4 pb-4 text-center background-dark">
 | 
			
		||||
          <a routerLink="/">
 | 
			
		||||
            <h1 class="text-white">CICADA</h1>
 | 
			
		||||
          </a>
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@ import { MatSelectModule } from '@angular/material/select';
 | 
			
		||||
import { MatInputModule } from '@angular/material/input';
 | 
			
		||||
import { MatButtonModule } from '@angular/material/button';
 | 
			
		||||
import { MatRippleModule } from '@angular/material/core';
 | 
			
		||||
import { SharedModule } from '@app/shared/shared.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [AuthComponent, PasswordToggleDirective],
 | 
			
		||||
@ -22,6 +23,7 @@ import { MatRippleModule } from '@angular/material/core';
 | 
			
		||||
    MatInputModule,
 | 
			
		||||
    MatButtonModule,
 | 
			
		||||
    MatRippleModule,
 | 
			
		||||
    SharedModule,
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class AuthModule {}
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@
 | 
			
		||||
            <strong> {{account?.vcard?.fn[0].value}} </strong>
 | 
			
		||||
          </h3>
 | 
			
		||||
          <span class="ml-auto"><strong>Balance:</strong> {{account?.balance | tokenRatio}} SRF</span>
 | 
			
		||||
          <span class="ml-2"><strong>Created:</strong> {{account?.date_registered | date}}</span>
 | 
			
		||||
          <span class="ml-2"><strong>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">
 | 
			
		||||
@ -48,10 +48,19 @@
 | 
			
		||||
 | 
			
		||||
              <div class="col-md-6 col-lg-4">
 | 
			
		||||
                <mat-form-field appearance="outline">
 | 
			
		||||
                  <mat-label>Name(s): *</mat-label>
 | 
			
		||||
                  <input matInput type="text" id="givenNames" placeholder="{{account?.vcard?.fn[0].value}}"
 | 
			
		||||
                         value="{{account?.vcard?.fn[0].value}}" formControlName="name" [errorStateMatcher]="matcher">
 | 
			
		||||
                  <mat-error *ngIf="submitted && accountInfoFormStub.name.errors">Name is required.</mat-error>
 | 
			
		||||
                  <mat-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>
 | 
			
		||||
 | 
			
		||||
@ -210,12 +219,9 @@
 | 
			
		||||
              <tr>
 | 
			
		||||
                <td>{{account?.vcard?.fn[0].value}}</td>
 | 
			
		||||
                <td>{{account?.balance | tokenRatio}}</td>
 | 
			
		||||
                <td>{{account?.date_registered | date}}</td>
 | 
			
		||||
                <td>{{account?.date_registered | unixDate}}</td>
 | 
			
		||||
                <td>
 | 
			
		||||
                  <span *ngIf="accountStatus === 'active'" class="badge badge-success badge-pill">
 | 
			
		||||
                    {{accountStatus}}
 | 
			
		||||
                  </span>
 | 
			
		||||
                  <span *ngIf="accountStatus === 'blocked'" class="badge badge-danger badge-pill">
 | 
			
		||||
                  <span class="badge badge-success badge-pill">
 | 
			
		||||
                    {{accountStatus}}
 | 
			
		||||
                  </span>
 | 
			
		||||
                </td>
 | 
			
		||||
@ -228,7 +234,7 @@
 | 
			
		||||
 | 
			
		||||
      <mat-tab-group *ngIf="account" dynamicHeight mat-align-tabs="start">
 | 
			
		||||
        <mat-tab label="Transactions">
 | 
			
		||||
          <app-transaction-details [transaction]="transaction"></app-transaction-details>
 | 
			
		||||
          <app-transaction-details [transaction]="transaction" (closeWindow)="transaction = $event"></app-transaction-details>
 | 
			
		||||
          <div class="card mt-1">
 | 
			
		||||
            <div class="card-header">
 | 
			
		||||
              <div class="row">
 | 
			
		||||
@ -252,17 +258,17 @@
 | 
			
		||||
                <mat-icon matSuffix>search</mat-icon>
 | 
			
		||||
              </mat-form-field>
 | 
			
		||||
 | 
			
		||||
              <mat-table class="mat-elevation-z10" [dataSource]="transactionsDataSource" matSort matSortActive="created"
 | 
			
		||||
              <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}} </td>
 | 
			
		||||
                  <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}} </td>
 | 
			
		||||
                  <td mat-cell *matCellDef="let transaction"> {{transaction?.recipient?.vcard.fn[0].value || transaction.to}} </td>
 | 
			
		||||
                </ng-container>
 | 
			
		||||
 | 
			
		||||
                <ng-container matColumnDef="value">
 | 
			
		||||
@ -275,7 +281,7 @@
 | 
			
		||||
 | 
			
		||||
                <ng-container matColumnDef="created">
 | 
			
		||||
                  <th mat-header-cell *matHeaderCellDef mat-sort-header> Created </th>
 | 
			
		||||
                  <td mat-cell *matCellDef="let transaction"> {{transaction?.tx.timestamp | date}} </td>
 | 
			
		||||
                  <td mat-cell *matCellDef="let transaction"> {{transaction?.tx.timestamp | unixDate}} </td>
 | 
			
		||||
                </ng-container>
 | 
			
		||||
 | 
			
		||||
                <ng-container matColumnDef="type">
 | 
			
		||||
@ -285,10 +291,10 @@
 | 
			
		||||
                  </td>
 | 
			
		||||
                </ng-container>
 | 
			
		||||
 | 
			
		||||
                <mat-header-row *matHeaderRowDef="transactionsDisplayedColumns"></mat-header-row>
 | 
			
		||||
                <mat-row *matRowDef="let transaction; columns: transactionsDisplayedColumns" matRipple
 | 
			
		||||
                         (click)="viewTransaction(transaction)"></mat-row>
 | 
			
		||||
              </mat-table>
 | 
			
		||||
                <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>
 | 
			
		||||
@ -337,7 +343,7 @@
 | 
			
		||||
 | 
			
		||||
                <ng-container matColumnDef="created">
 | 
			
		||||
                  <mat-header-cell *matHeaderCellDef mat-sort-header> CREATED </mat-header-cell>
 | 
			
		||||
                  <mat-cell *matCellDef="let user"> {{user?.date_registered | date}} </mat-cell>
 | 
			
		||||
                  <mat-cell *matCellDef="let user"> {{user?.date_registered | unixDate}} </mat-cell>
 | 
			
		||||
                </ng-container>
 | 
			
		||||
 | 
			
		||||
                <ng-container matColumnDef="balance">
 | 
			
		||||
 | 
			
		||||
@ -78,8 +78,17 @@ export class AccountDetailsComponent implements OnInit {
 | 
			
		||||
    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({
 | 
			
		||||
      name: ['', Validators.required],
 | 
			
		||||
      firstName: ['', Validators.required],
 | 
			
		||||
      lastName: ['', Validators.required],
 | 
			
		||||
      phoneNumber: ['', Validators.required],
 | 
			
		||||
      age: ['', Validators.required],
 | 
			
		||||
      type: ['', Validators.required],
 | 
			
		||||
@ -90,36 +99,71 @@ export class AccountDetailsComponent implements OnInit {
 | 
			
		||||
      location: ['', Validators.required],
 | 
			
		||||
      locationType: ['', Validators.required],
 | 
			
		||||
    });
 | 
			
		||||
    this.route.paramMap.subscribe(async (params: Params) => {
 | 
			
		||||
      this.accountAddress = add0x(params.get('id'));
 | 
			
		||||
      this.bloxbergLink =
 | 
			
		||||
        'https://blockexplorer.bloxberg.org/address/' + this.accountAddress + '/transactions';
 | 
			
		||||
      (await this.userService.getAccountByAddress(this.accountAddress, 100)).subscribe(
 | 
			
		||||
        async (res) => {
 | 
			
		||||
          if (res !== undefined) {
 | 
			
		||||
            this.account = res;
 | 
			
		||||
            this.cdr.detectChanges();
 | 
			
		||||
            this.loggingService.sendInfoLevelMessage(this.account);
 | 
			
		||||
            // this.userService.getAccountStatus(this.account.vcard?.tel[0].value).pipe(first())
 | 
			
		||||
            //   .subscribe(response => this.accountStatus = response);
 | 
			
		||||
            this.accountInfoForm.patchValue({
 | 
			
		||||
              name: this.account.vcard?.fn[0].value,
 | 
			
		||||
              phoneNumber: this.account.vcard?.tel[0].value,
 | 
			
		||||
              age: this.account.age,
 | 
			
		||||
              type: this.account.type,
 | 
			
		||||
              bio: this.account.products,
 | 
			
		||||
              gender: this.account.gender,
 | 
			
		||||
              businessCategory: this.account.category,
 | 
			
		||||
              userLocation: this.account.location.area_name,
 | 
			
		||||
              location: this.account.location.area,
 | 
			
		||||
              locationType: this.account.location.area_type,
 | 
			
		||||
            });
 | 
			
		||||
          } else {
 | 
			
		||||
            alert('Account not found!');
 | 
			
		||||
          }
 | 
			
		||||
    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],
 | 
			
		||||
            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.blockSyncService.blockSync(this.accountAddress);
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
    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()
 | 
			
		||||
@ -147,22 +191,6 @@ export class AccountDetailsComponent implements OnInit {
 | 
			
		||||
      .subscribe((res) => (this.genders = res));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.userService.accountsSubject.subscribe((accounts) => {
 | 
			
		||||
      this.userDataSource = new MatTableDataSource<any>(accounts);
 | 
			
		||||
      this.userDataSource.paginator = this.userTablePaginator;
 | 
			
		||||
      this.userDataSource.sort = this.userTableSort;
 | 
			
		||||
      this.accounts = accounts;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.transactionService.transactionsSubject.subscribe((transactions) => {
 | 
			
		||||
      this.transactionsDataSource = new MatTableDataSource<any>(transactions);
 | 
			
		||||
      this.transactionsDataSource.paginator = this.transactionTablePaginator;
 | 
			
		||||
      this.transactionsDataSource.sort = this.transactionTableSort;
 | 
			
		||||
      this.transactions = transactions;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  doTransactionFilter(value: string): void {
 | 
			
		||||
    this.transactionsDataSource.filter = value.trim().toLocaleLowerCase();
 | 
			
		||||
  }
 | 
			
		||||
@ -192,7 +220,7 @@ export class AccountDetailsComponent implements OnInit {
 | 
			
		||||
    }
 | 
			
		||||
    const accountKey = await this.userService.changeAccountInfo(
 | 
			
		||||
      this.accountAddress,
 | 
			
		||||
      this.accountInfoFormStub.name.value,
 | 
			
		||||
      this.accountInfoFormStub.firstName.value + ' ' + this.accountInfoFormStub.lastName.value,
 | 
			
		||||
      this.accountInfoFormStub.phoneNumber.value,
 | 
			
		||||
      this.accountInfoFormStub.age.value,
 | 
			
		||||
      this.accountInfoFormStub.type.value,
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,8 @@ export class AccountSearchComponent implements OnInit {
 | 
			
		||||
    private router: Router
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
  async ngOnInit(): Promise<void> {
 | 
			
		||||
    await this.userService.init();
 | 
			
		||||
    this.nameSearchForm = this.formBuilder.group({
 | 
			
		||||
      name: ['', Validators.required],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -56,7 +56,7 @@
 | 
			
		||||
 | 
			
		||||
            <ng-container matColumnDef="created">
 | 
			
		||||
              <mat-header-cell *matHeaderCellDef mat-sort-header> CREATED </mat-header-cell>
 | 
			
		||||
              <mat-cell *matCellDef="let user"> {{user?.date_registered | date}} </mat-cell>
 | 
			
		||||
              <mat-cell *matCellDef="let user"> {{user?.date_registered | unixDate}} </mat-cell>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
 | 
			
		||||
            <ng-container matColumnDef="balance">
 | 
			
		||||
 | 
			
		||||
@ -32,28 +32,26 @@ export class AccountsComponent implements OnInit {
 | 
			
		||||
    private userService: UserService,
 | 
			
		||||
    private loggingService: LoggingService,
 | 
			
		||||
    private router: Router
 | 
			
		||||
  ) {
 | 
			
		||||
    (async () => {
 | 
			
		||||
      try {
 | 
			
		||||
        // TODO it feels like this should be in the onInit handler
 | 
			
		||||
        await this.userService.loadAccounts(100);
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        this.loggingService.sendErrorLevelMessage('Failed to load accounts', this, { error });
 | 
			
		||||
      }
 | 
			
		||||
    })();
 | 
			
		||||
    this.userService
 | 
			
		||||
      .getAccountTypes()
 | 
			
		||||
      .pipe(first())
 | 
			
		||||
      .subscribe((res) => (this.accountTypes = res));
 | 
			
		||||
  }
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
  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 {
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,8 @@ export class CreateAccountComponent implements OnInit {
 | 
			
		||||
    private userService: UserService
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
  async ngOnInit(): Promise<void> {
 | 
			
		||||
    await this.userService.init();
 | 
			
		||||
    this.createForm = this.formBuilder.group({
 | 
			
		||||
      accountType: ['', Validators.required],
 | 
			
		||||
      idNumber: ['', Validators.required],
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,10 @@ export class AdminComponent implements OnInit {
 | 
			
		||||
  @ViewChild(MatPaginator) paginator: MatPaginator;
 | 
			
		||||
  @ViewChild(MatSort) sort: MatSort;
 | 
			
		||||
 | 
			
		||||
  constructor(private userService: UserService, private loggingService: LoggingService) {
 | 
			
		||||
  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);
 | 
			
		||||
@ -40,8 +43,6 @@ export class AdminComponent implements OnInit {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {}
 | 
			
		||||
 | 
			
		||||
  doFilter(value: string): void {
 | 
			
		||||
    this.dataSource.filter = value.trim().toLocaleLowerCase();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -23,9 +23,10 @@
 | 
			
		||||
              SETTINGS
 | 
			
		||||
            </mat-card-title>
 | 
			
		||||
            <div class="card-body">
 | 
			
		||||
              <h4>Kobo Toolbox Credentials</h4>
 | 
			
		||||
              <span><strong>Username: </strong> admin_reserve </span><br>
 | 
			
		||||
              <span><strong>Password: </strong> ******** </span>
 | 
			
		||||
              <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">
 | 
			
		||||
 | 
			
		||||
@ -17,19 +17,22 @@ export class SettingsComponent implements OnInit {
 | 
			
		||||
  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) {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    const d = new Date();
 | 
			
		||||
    this.date = `${d.getDate()}/${d.getMonth()}/${d.getFullYear()}`;
 | 
			
		||||
    this.trustedUsers = this.authService.getTrustedUsers();
 | 
			
		||||
    this.dataSource = new MatTableDataSource<any>(this.trustedUsers);
 | 
			
		||||
    this.dataSource.paginator = this.paginator;
 | 
			
		||||
    this.dataSource.sort = this.sort;
 | 
			
		||||
  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 {
 | 
			
		||||
 | 
			
		||||
@ -1,60 +1,36 @@
 | 
			
		||||
<!-- Begin page -->
 | 
			
		||||
<div class="wrapper">
 | 
			
		||||
  <app-sidebar></app-sidebar>
 | 
			
		||||
 | 
			
		||||
  <!-- ============================================================== -->
 | 
			
		||||
  <!-- Start Page Content here -->
 | 
			
		||||
  <!-- ============================================================== -->
 | 
			
		||||
 | 
			
		||||
  <div id="content">
 | 
			
		||||
    <app-topbar></app-topbar>
 | 
			
		||||
    <!-- Start Content-->
 | 
			
		||||
    <div class="container-fluid text-center" appMenuSelection>
 | 
			
		||||
      <nav aria-label="breadcrumb">
 | 
			
		||||
        <ol class="breadcrumb">
 | 
			
		||||
          <li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
 | 
			
		||||
          <li class="breadcrumb-item"><a routerLink="/tokens">Tokens</a></li>
 | 
			
		||||
          <li class="breadcrumb-item active" aria-current="page">{{token.name}}</li>
 | 
			
		||||
        </ol>
 | 
			
		||||
      </nav>
 | 
			
		||||
      <div class="col-md-6 center-body">
 | 
			
		||||
        <div class="card">
 | 
			
		||||
          <mat-card-title class="card-header">
 | 
			
		||||
            Token
 | 
			
		||||
          </mat-card-title>
 | 
			
		||||
          <div class="card-body">
 | 
			
		||||
            <div>
 | 
			
		||||
              <span><strong>Name:</strong> {{token.name}}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
              <span><strong>Symbol:</strong> {{token.symbol}}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
              <span><strong>Address:</strong> {{token.address}}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
              <span><strong>Details:</strong> A community inclusive currency for trading among lower to middle income societies.</span>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
              <span><strong>Supply:</strong> {{token.supply | tokenRatio}}</span>
 | 
			
		||||
            </div><br>
 | 
			
		||||
            <div>
 | 
			
		||||
              <h2>Reserve</h2>
 | 
			
		||||
              <div>
 | 
			
		||||
                <span><strong>Weight:</strong> {{token.reserveRatio}}</span>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div>
 | 
			
		||||
                <span><strong>Owner:</strong> {{token.owner}}</span>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
<div *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>
 | 
			
		||||
    <app-footer appMenuSelection></app-footer>
 | 
			
		||||
  </div>
 | 
			
		||||
  <!-- ============================================================== -->
 | 
			
		||||
  <!-- End Page content -->
 | 
			
		||||
  <!-- ============================================================== -->
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,12 @@
 | 
			
		||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
 | 
			
		||||
import { ActivatedRoute, Params } from '@angular/router';
 | 
			
		||||
import { TokenService } from '@app/_services';
 | 
			
		||||
import { first } from 'rxjs/operators';
 | 
			
		||||
import { Token } from '../../../_models';
 | 
			
		||||
import {
 | 
			
		||||
  ChangeDetectionStrategy,
 | 
			
		||||
  Component,
 | 
			
		||||
  EventEmitter,
 | 
			
		||||
  Input,
 | 
			
		||||
  OnInit,
 | 
			
		||||
  Output,
 | 
			
		||||
} from '@angular/core';
 | 
			
		||||
import { Token } from '@app/_models';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-token-details',
 | 
			
		||||
@ -11,18 +15,16 @@ import { Token } from '../../../_models';
 | 
			
		||||
  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
			
		||||
})
 | 
			
		||||
export class TokenDetailsComponent implements OnInit {
 | 
			
		||||
  token: Token;
 | 
			
		||||
  @Input() token: Token;
 | 
			
		||||
 | 
			
		||||
  constructor(private route: ActivatedRoute, private tokenService: TokenService) {
 | 
			
		||||
    this.route.paramMap.subscribe((params: Params) => {
 | 
			
		||||
      this.tokenService
 | 
			
		||||
        .getTokenBySymbol(params.get('id'))
 | 
			
		||||
        .pipe(first())
 | 
			
		||||
        .subscribe((res) => {
 | 
			
		||||
          this.token = res;
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  @Output() closeWindow: EventEmitter<any> = new EventEmitter<any>();
 | 
			
		||||
 | 
			
		||||
  constructor() {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {}
 | 
			
		||||
 | 
			
		||||
  close(): void {
 | 
			
		||||
    this.token = null;
 | 
			
		||||
    this.closeWindow.emit(this.token);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,9 @@
 | 
			
		||||
          </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">
 | 
			
		||||
 | 
			
		||||
@ -5,8 +5,7 @@ import { LoggingService, TokenService } from '@app/_services';
 | 
			
		||||
import { MatTableDataSource } from '@angular/material/table';
 | 
			
		||||
import { Router } from '@angular/router';
 | 
			
		||||
import { exportCsv } from '@app/_helpers';
 | 
			
		||||
import { TokenRegistry } from '../../_eth';
 | 
			
		||||
import { Token } from '../../_models';
 | 
			
		||||
import { Token } from '@app/_models';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-tokens',
 | 
			
		||||
@ -19,7 +18,8 @@ export class TokensComponent implements OnInit {
 | 
			
		||||
  columnsToDisplay: Array<string> = ['name', 'symbol', 'address', 'supply'];
 | 
			
		||||
  @ViewChild(MatPaginator) paginator: MatPaginator;
 | 
			
		||||
  @ViewChild(MatSort) sort: MatSort;
 | 
			
		||||
  tokens: Array<Promise<string>>;
 | 
			
		||||
  tokens: Array<Token>;
 | 
			
		||||
  token: Token;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private tokenService: TokenService,
 | 
			
		||||
@ -28,22 +28,25 @@ export class TokensComponent implements OnInit {
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  async ngOnInit(): Promise<void> {
 | 
			
		||||
    this.tokenService.LoadEvent.subscribe(async () => {
 | 
			
		||||
      this.tokens = await this.tokenService.getTokens();
 | 
			
		||||
    await this.tokenService.init();
 | 
			
		||||
    this.tokenService.onload = async (status: boolean): Promise<void> => {
 | 
			
		||||
      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;
 | 
			
		||||
    });
 | 
			
		||||
    this.tokens = await this.tokenService.getTokens();
 | 
			
		||||
    this.loggingService.sendInfoLevelMessage(this.tokens);
 | 
			
		||||
    this.dataSource = new MatTableDataSource(this.tokens);
 | 
			
		||||
    this.dataSource.paginator = this.paginator;
 | 
			
		||||
    this.dataSource.sort = this.sort;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  doFilter(value: string): void {
 | 
			
		||||
    this.dataSource.filter = value.trim().toLocaleLowerCase();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async viewToken(token): Promise<void> {
 | 
			
		||||
    await this.router.navigateByUrl(`/tokens/${token.symbol}`);
 | 
			
		||||
  viewToken(token): void {
 | 
			
		||||
    this.token = token;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  downloadCsv(): void {
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
<div *ngIf="transaction | async" class="mb-3 mt-1">
 | 
			
		||||
<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)="transaction = null"> CLOSE </button>
 | 
			
		||||
        <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">
 | 
			
		||||
@ -66,7 +66,7 @@
 | 
			
		||||
              <span>Success: {{transaction.tx.success}}</span>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li class="list-group-item">
 | 
			
		||||
              <span>Timestamp: {{transaction.tx.timestamp | date}}</span>
 | 
			
		||||
              <span>Timestamp: {{transaction.tx.timestamp | unixDate}}</span>
 | 
			
		||||
            </li>
 | 
			
		||||
          </ul><br>
 | 
			
		||||
          <div class="mb-3">
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,11 @@
 | 
			
		||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
  ChangeDetectionStrategy,
 | 
			
		||||
  Component,
 | 
			
		||||
  EventEmitter,
 | 
			
		||||
  Input,
 | 
			
		||||
  OnInit,
 | 
			
		||||
  Output,
 | 
			
		||||
} from '@angular/core';
 | 
			
		||||
import { Router } from '@angular/router';
 | 
			
		||||
import { TransactionService } from '@app/_services';
 | 
			
		||||
import { copyToClipboard } from '@app/_helpers';
 | 
			
		||||
@ -13,6 +20,9 @@ import { strip0x } from '@src/assets/js/ethtx/dist/hex';
 | 
			
		||||
})
 | 
			
		||||
export class TransactionDetailsComponent implements OnInit {
 | 
			
		||||
  @Input() transaction;
 | 
			
		||||
 | 
			
		||||
  @Output() closeWindow: EventEmitter<any> = new EventEmitter<any>();
 | 
			
		||||
 | 
			
		||||
  senderBloxbergLink: string;
 | 
			
		||||
  recipientBloxbergLink: string;
 | 
			
		||||
  traderBloxbergLink: string;
 | 
			
		||||
@ -23,7 +33,8 @@ export class TransactionDetailsComponent implements OnInit {
 | 
			
		||||
    private snackBar: MatSnackBar
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
  async ngOnInit(): Promise<void> {
 | 
			
		||||
    await this.transactionService.init();
 | 
			
		||||
    if (this.transaction?.type === 'conversion') {
 | 
			
		||||
      this.traderBloxbergLink =
 | 
			
		||||
        'https://blockexplorer.bloxberg.org/address/' + this.transaction?.trader + '/transactions';
 | 
			
		||||
@ -61,4 +72,9 @@ export class TransactionDetailsComponent implements OnInit {
 | 
			
		||||
      this.snackBar.open(address + ' copied successfully!', 'Close', { duration: 3000 });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  close(): void {
 | 
			
		||||
    this.transaction = null;
 | 
			
		||||
    this.closeWindow.emit(this.transaction);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@
 | 
			
		||||
        </mat-card-title>
 | 
			
		||||
        <div class="card-body">
 | 
			
		||||
 | 
			
		||||
          <app-transaction-details [transaction]="transaction"></app-transaction-details>
 | 
			
		||||
          <app-transaction-details [transaction]="transaction" (closeWindow)="transaction = $event"></app-transaction-details>
 | 
			
		||||
 | 
			
		||||
          <div class="row card-header">
 | 
			
		||||
            <mat-form-field appearance="outline">
 | 
			
		||||
@ -48,12 +48,12 @@
 | 
			
		||||
 | 
			
		||||
            <ng-container matColumnDef="sender">
 | 
			
		||||
              <th mat-header-cell *matHeaderCellDef mat-sort-header> Sender </th>
 | 
			
		||||
              <td mat-cell *matCellDef="let transaction"> {{transaction?.sender?.vcard.fn[0].value}} </td>
 | 
			
		||||
              <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}} </td>
 | 
			
		||||
              <td mat-cell *matCellDef="let transaction"> {{transaction?.recipient?.vcard.fn[0].value || transaction.to}} </td>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
 | 
			
		||||
            <ng-container matColumnDef="value">
 | 
			
		||||
@ -66,7 +66,7 @@
 | 
			
		||||
 | 
			
		||||
            <ng-container matColumnDef="created">
 | 
			
		||||
              <th mat-header-cell *matHeaderCellDef mat-sort-header> Created </th>
 | 
			
		||||
              <td mat-cell *matCellDef="let transaction"> {{transaction?.tx.timestamp | date}} </td>
 | 
			
		||||
              <td mat-cell *matCellDef="let transaction"> {{transaction?.tx.timestamp | unixDate}} </td>
 | 
			
		||||
            </ng-container>
 | 
			
		||||
 | 
			
		||||
            <ng-container matColumnDef="type">
 | 
			
		||||
 | 
			
		||||
@ -36,17 +36,19 @@ export class TransactionsComponent implements OnInit, AfterViewInit {
 | 
			
		||||
    private blockSyncService: BlockSyncService,
 | 
			
		||||
    private transactionService: TransactionService,
 | 
			
		||||
    private userService: UserService
 | 
			
		||||
  ) {
 | 
			
		||||
    this.blockSyncService.blockSync();
 | 
			
		||||
  }
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
  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.transactionService.init();
 | 
			
		||||
    await this.userService.init();
 | 
			
		||||
    await this.blockSyncService.blockSync();
 | 
			
		||||
    this.userService
 | 
			
		||||
      .getTransactionTypes()
 | 
			
		||||
      .pipe(first())
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								src/app/shared/_pipes/unix-date.pipe.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/app/shared/_pipes/unix-date.pipe.spec.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
import { UnixDatePipe } from './unix-date.pipe';
 | 
			
		||||
 | 
			
		||||
describe('UnixDatePipe', () => {
 | 
			
		||||
  it('create an instance', () => {
 | 
			
		||||
    const pipe = new UnixDatePipe();
 | 
			
		||||
    expect(pipe).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										10
									
								
								src/app/shared/_pipes/unix-date.pipe.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/app/shared/_pipes/unix-date.pipe.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
import { Pipe, PipeTransform } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
@Pipe({
 | 
			
		||||
  name: 'unixDate',
 | 
			
		||||
})
 | 
			
		||||
export class UnixDatePipe implements PipeTransform {
 | 
			
		||||
  transform(timestamp: number, ...args: unknown[]): any {
 | 
			
		||||
    return new Date(timestamp * 1000).toLocaleDateString('en-GB');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,8 @@
 | 
			
		||||
<!-- Footer Start -->
 | 
			
		||||
<footer class="footer">
 | 
			
		||||
  2020 © Grassroots Economics
 | 
			
		||||
  <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 -->
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
 | 
			
		||||
  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
			
		||||
})
 | 
			
		||||
export class FooterComponent implements OnInit {
 | 
			
		||||
  currentYear = new Date().getFullYear();
 | 
			
		||||
  constructor() {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {}
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
<nav class="navbar navbar-dark bg-dark">
 | 
			
		||||
<nav class="navbar navbar-dark background-dark">
 | 
			
		||||
  <h1 class="navbar-brand">
 | 
			
		||||
    <div *ngIf="noInternetConnection; then offlineBlock else onlineBlock"></div>
 | 
			
		||||
    <ng-template #offlineBlock>
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ import { ErrorDialogComponent } from '@app/shared/error-dialog/error-dialog.comp
 | 
			
		||||
import { MatDialogModule } from '@angular/material/dialog';
 | 
			
		||||
import { SafePipe } from '@app/shared/_pipes/safe.pipe';
 | 
			
		||||
import { NetworkStatusComponent } from './network-status/network-status.component';
 | 
			
		||||
import { UnixDatePipe } from './_pipes/unix-date.pipe';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [
 | 
			
		||||
@ -24,6 +25,7 @@ import { NetworkStatusComponent } from './network-status/network-status.componen
 | 
			
		||||
    ErrorDialogComponent,
 | 
			
		||||
    SafePipe,
 | 
			
		||||
    NetworkStatusComponent,
 | 
			
		||||
    UnixDatePipe,
 | 
			
		||||
  ],
 | 
			
		||||
  exports: [
 | 
			
		||||
    TopbarComponent,
 | 
			
		||||
@ -33,6 +35,7 @@ import { NetworkStatusComponent } from './network-status/network-status.componen
 | 
			
		||||
    TokenRatioPipe,
 | 
			
		||||
    SafePipe,
 | 
			
		||||
    NetworkStatusComponent,
 | 
			
		||||
    UnixDatePipe,
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [CommonModule, RouterModule, MatIconModule, MatDialogModule],
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
<!-- ========== Left Sidebar Start ========== -->
 | 
			
		||||
<div id="sidebar">
 | 
			
		||||
  <app-network-status></app-network-status>
 | 
			
		||||
  <nav>
 | 
			
		||||
 | 
			
		||||
    <div class="sidebar-header">
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,7 @@ Driver.prototype.sync = function (n) {
 | 
			
		||||
  const processor = async (b, t) => {
 | 
			
		||||
    return await self.process(b, t);
 | 
			
		||||
  };
 | 
			
		||||
  self.syncer(self, self.lo, self.hi, self.filters[0], self.filters[1], countGetter, processor);
 | 
			
		||||
  self.syncer(self.lo, self.hi, self.filters[0], self.filters[1], countGetter, processor);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Driver.prototype.process = function (b, t) {
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ export const environment = {
 | 
			
		||||
  publicKeysUrl: 'https://dev.grassrootseconomics.net/.well-known/publickeys/',
 | 
			
		||||
  cicCacheUrl: 'https://cache.dev.grassrootseconomics.net',
 | 
			
		||||
  web3Provider: 'wss://bloxberg-ws.dev.grassrootseconomics.net',
 | 
			
		||||
  cicUssdUrl: 'https://ussd.dev.grassrootseconomics.net',
 | 
			
		||||
  cicUssdUrl: 'https://user.dev.grassrootseconomics.net',
 | 
			
		||||
  registryAddress: '0xea6225212005e86a4490018ded4bf37f3e772161',
 | 
			
		||||
  trustedDeclaratorAddress: '0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ export const environment = {
 | 
			
		||||
  publicKeysUrl: 'https://dev.grassrootseconomics.net/.well-known/publickeys/',
 | 
			
		||||
  cicCacheUrl: 'https://cache.dev.grassrootseconomics.net',
 | 
			
		||||
  web3Provider: 'wss://bloxberg-ws.dev.grassrootseconomics.net',
 | 
			
		||||
  cicUssdUrl: 'https://ussd.dev.grassrootseconomics.net',
 | 
			
		||||
  cicUssdUrl: 'https://user.dev.grassrootseconomics.net',
 | 
			
		||||
  registryAddress: '0xea6225212005e86a4490018ded4bf37f3e772161',
 | 
			
		||||
  trustedDeclaratorAddress: '0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ export const environment = {
 | 
			
		||||
  publicKeysUrl: 'https://dev.grassrootseconomics.net/.well-known/publickeys/',
 | 
			
		||||
  cicCacheUrl: 'https://cache.dev.grassrootseconomics.net',
 | 
			
		||||
  web3Provider: 'wss://bloxberg-ws.dev.grassrootseconomics.net',
 | 
			
		||||
  cicUssdUrl: 'https://ussd.dev.grassrootseconomics.net',
 | 
			
		||||
  cicUssdUrl: 'https://user.dev.grassrootseconomics.net',
 | 
			
		||||
  registryAddress: '0xea6225212005e86a4490018ded4bf37f3e772161',
 | 
			
		||||
  trustedDeclaratorAddress: '0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -18,8 +18,8 @@ body {
 | 
			
		||||
  background: #fafafa;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.bg-dark {
 | 
			
		||||
  background: #313a46;
 | 
			
		||||
.background-dark {
 | 
			
		||||
  background: #313a46 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
p {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user