1 Commits

Author SHA1 Message Date
Spencer Ofwiti
9e293b822c Fix httpgeter. 2021-05-18 18:01:52 +03:00
50 changed files with 481 additions and 637 deletions

View File

@@ -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 = Web3Service.getInstance();
const web3: Web3 = new Web3(environment.web3Provider);
export class AccountIndex {
contractAddress: string;

View File

@@ -1,8 +1,8 @@
import Web3 from 'web3';
import { Web3Service } from '@app/_services/web3.service';
import { environment } from '@src/environments/environment';
const abi: Array<any> = require('@src/assets/js/block-sync/data/TokenUniqueSymbolIndex.json');
const web3: Web3 = Web3Service.getInstance();
const web3: Web3 = new Web3(environment.web3Provider);
export class TokenRegistry {
contractAddress: string;

View File

@@ -1151,7 +1151,7 @@ export class MockBackendInterceptor implements HttpInterceptor {
const queriedCategory: Category = categories.find((category) =>
category.products.includes(stringFromUrl())
);
return ok(queriedCategory.name || 'other');
return ok(queriedCategory.name);
}
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 || 'other');
return ok(queriedAreaName.name);
}
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 || 'other');
return ok(queriedAreaType.name);
}
function getAccountTypes(): Observable<HttpResponse<any>> {

View File

@@ -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}`, person, error));
personValidationErrors.map((error) => console.error(`${error.message}`));
}
}
@@ -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}`, vcard, error));
vcardValidationErrors.map((error) => console.error(`${error.message}`));
}
}

View File

@@ -4,7 +4,7 @@ interface Token {
address: string;
supply: string;
decimals: string;
reserves?: {
reserves: {
'0xa686005CE37Dce7738436256982C3903f2E4ea8E'?: {
weight: string;
balance: string;

View File

@@ -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): Promise<void>;
importPublicKey(publicKey: any): 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);
}
async importPublicKey(publicKey: any): Promise<void> {
await keyring.publicKeys.importKey(publicKey);
importPublicKey(publicKey: any): void {
keyring.publicKeys.importKey(publicKey);
}
async importPrivateKey(privateKey: any): Promise<void> {

View File

@@ -7,19 +7,13 @@ 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 {
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,
@@ -31,46 +25,41 @@ export class AuthService {
async init(): Promise<void> {
await this.mutableKeyStore.loadKeyring();
// TODO setting these together should be atomic
if (sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN'))) {
this.sessionToken = sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN'));
}
if (localStorage.getItem(btoa('CICADA_PRIVATE_KEY'))) {
await this.mutableKeyStore.importPrivateKey(localStorage.getItem(btoa('CICADA_PRIVATE_KEY')));
}
}
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> {
return new Promise((resolve, reject) => {
const headers = {
Authorization: 'Bearer ' + this.getSessionToken,
Authorization: 'Bearer ' + this.sessionToken,
'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;
fetch(environment.cicMetaUrl, options).then((response) => {
if (response.status === 401) {
return reject(rejectBody(response));
}
return true;
return resolve(true);
});
});
}
// TODO rename to send signed challenge and set session. Also separate these responsibilities
sendSignedChallenge(hobaResponseEncoded: any): Promise<any> {
sendResponse(hobaResponseEncoded: any): Promise<boolean> {
return new Promise((resolve, reject) => {
const headers = {
Authorization: 'HOBA ' + hobaResponseEncoded,
'Content-Type': 'application/json;charset=utf-8',
@@ -79,51 +68,85 @@ export class AuthService {
const options = {
headers,
};
return fetch(environment.cicMetaUrl, options);
fetch(environment.cicMetaUrl, options).then((response) => {
if (response.status === 401) {
return reject(rejectBody(response));
}
this.sessionToken = response.headers.get('Token');
sessionStorage.setItem(btoa('CICADA_SESSION_TOKEN'), this.sessionToken);
this.setState('Click button to log in');
return resolve(true);
});
});
}
getChallenge(): Promise<any> {
return fetch(environment.cicMetaUrl).then((response) => {
return new Promise((resolve, reject) => {
fetch(environment.cicMetaUrl).then(async (response) => {
if (response.status === 401) {
const authHeader: string = response.headers.get('WWW-Authenticate');
return hobaParseChallengeHeader(authHeader);
return resolve(hobaParseChallengeHeader(authHeader));
}
if (!response.ok) {
return reject(rejectBody(response));
}
});
});
}
async login(): Promise<boolean> {
if (this.getSessionToken()) {
sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN'));
if (this.sessionToken !== undefined) {
try {
const response: boolean = await this.getWithToken();
return response === true;
} catch (e) {
this.loggingService.sendErrorLevelMessage('Login token failed', this, { error: e });
}
} else {
try {
const o = await this.getChallenge();
const response: boolean = await this.loginResponse(o);
return response === true;
} catch (e) {
this.loggingService.sendErrorLevelMessage('Login challenge failed', this, { error: e });
}
}
return false;
}
async loginResponse(o: { challenge: string; realm: any }): Promise<any> {
return new Promise(async (resolve, reject) => {
try {
const r = await signChallenge(
o.challenge,
o.realm,
environment.cicMetaUrl,
this.mutableKeyStore
);
const tokenResponse = await this.sendSignedChallenge(r).then((response) => {
const token = response.headers.get('Token');
if (token) {
return token;
const response: boolean = await this.sendResponse(r);
resolve(response);
} catch (error) {
if (error instanceof HttpError) {
if (error.status === 403) {
this.errorDialogService.openDialog({
message: 'You are not authorized to use this system',
});
} else if (error.status === 401) {
this.errorDialogService.openDialog({
message:
'Unable to authenticate with the service. ' +
'Please speak with the staff at Grassroots ' +
'Economics for requesting access ' +
'staff@grassrootseconomics.net.',
});
}
if (response.status === 401) {
throw new HttpError('You are not authorized to use this system', response.status);
} else {
// TODO define this error
this.errorDialogService.openDialog({ message: 'Incorrect key passphrase.' });
}
if (!response.ok) {
throw new HttpError('Unknown error from authentication server', response.status);
resolve(false);
}
});
if (tokenResponse) {
this.setSessionToken(tokenResponse);
this.setState('Click button to log in');
return true;
}
return false;
}
}
loginView(): void {
@@ -141,7 +164,7 @@ export class AuthService {
// TODO leaving this out for now.
// const isEncryptedKeyCheck = await this.mutableKeyStore.isEncryptedPrivateKey(privateKeyArmored);
// if (!isEncryptedKeyCheck) {
// throw Error('The private key does not have a password!');
// throw Error('The private key doesn\'t have a password!');
// }
const key = await this.mutableKeyStore.importPrivateKey(privateKeyArmored);
localStorage.setItem(btoa('CICADA_PRIVATE_KEY'), privateKeyArmored);
@@ -163,25 +186,14 @@ export class AuthService {
logout(): void {
sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN'));
localStorage.removeItem(btoa('CICADA_PRIVATE_KEY'));
this.sessionToken = undefined;
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);
});
getTrustedUsers(): any {
const trustedUsers: Array<any> = [];
this.mutableKeyStore.getPublicKeys().forEach((key) => trustedUsers.push(key.users[0].userId));
return trustedUsers;
}
async getPublicKeys(): Promise<any> {
@@ -199,8 +211,4 @@ export class AuthService {
getPrivateKey(): any {
return this.mutableKeyStore.getPrivateKey();
}
getPrivateKeyInfo(): any {
return this.getPrivateKey().users[0].userId;
}
}

View File

@@ -6,7 +6,6 @@ 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',
@@ -17,33 +16,31 @@ export class BlockSyncService {
constructor(
private transactionService: TransactionService,
private loggingService: LoggingService
private loggingService: LoggingService,
private registryService: RegistryService
) {}
async init(): Promise<void> {
await this.transactionService.init();
}
async blockSync(address: string = null, offset: number = 0, limit: number = 100): Promise<void> {
blockSync(address: string = null, offset: number = 0, limit: number = 100): void {
this.transactionService.resetTransactionsList();
const settings: Settings = new Settings(this.scan);
const readyStateElements: { network: number } = { network: 2 };
settings.w3.provider = environment.web3Provider;
settings.w3.engine = Web3Service.getInstance();
settings.registry = await RegistryService.getRegistry();
settings.w3.engine = this.registryService.getWeb3();
settings.registry = this.registryService.getRegistry();
settings.txHelper = new TransactionHelper(settings.w3.engine, settings.registry);
settings.txHelper.ontransfer = async (transaction: any): Promise<void> => {
window.dispatchEvent(this.newEvent(transaction, 'cic_transfer'));
window.dispatchEvent(this.newTransferEvent(transaction));
};
settings.txHelper.onconversion = async (transaction: any): Promise<any> => {
window.dispatchEvent(this.newEvent(transaction, 'cic_convert'));
window.dispatchEvent(this.newConversionEvent(transaction));
};
// settings.registry.onload = (addressReturned: string): void => {
// this.loggingService.sendInfoLevelMessage(`Loaded network contracts ${addressReturned}`);
// this.readyStateProcessor(settings, readyStateElements.network, address, offset, limit);
// };
settings.registry.onload = (addressReturned: number): void => {
this.loggingService.sendInfoLevelMessage(`Loaded network contracts ${addressReturned}`);
this.readyStateProcessor(settings, readyStateElements.network, address, offset, limit);
};
settings.registry.load();
}
readyStateProcessor(
@@ -81,8 +78,16 @@ export class BlockSyncService {
}
}
newEvent(tx: any, eventType: string): any {
return new CustomEvent(eventType, {
newTransferEvent(tx: any): any {
return new CustomEvent('cic_transfer', {
detail: {
tx,
},
});
}
newConversionEvent(tx: any): any {
return new CustomEvent('cic_convert', {
detail: {
tx,
},

View File

@@ -6,4 +6,3 @@ 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';

View File

@@ -1,30 +1,33 @@
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 {
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(),
web3: Web3 = new Web3(environment.web3Provider);
fileGetter: FileGetter = new HttpGetter();
registry: CICRegistry = new CICRegistry(
this.web3,
environment.registryAddress,
'Registry',
RegistryService.fileGetter,
this.fileGetter,
['../../assets/js/block-sync/data']
);
RegistryService.registry.declaratorHelper.addTrust(environment.trustedDeclaratorAddress);
await RegistryService.registry.load();
constructor() {
this.registry.declaratorHelper.addTrust(environment.trustedDeclaratorAddress);
this.registry.load();
}
return RegistryService.registry;
getRegistry(): any {
return this.registry;
}
getWeb3(): any {
return this.web3;
}
}

View File

@@ -1,10 +1,10 @@
import { Injectable } from '@angular/core';
import { EventEmitter, Injectable } from '@angular/core';
import { environment } from '@src/environments/environment';
import { BehaviorSubject, Observable } from 'rxjs';
import { CICRegistry } from 'cic-client';
import { TokenRegistry } from '@app/_eth';
import { HttpClient } from '@angular/common/http';
import { RegistryService } from '@app/_services/registry.service';
import { Token } from '@app/_models';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
@Injectable({
providedIn: 'root',
@@ -12,67 +12,29 @@ import { BehaviorSubject, Observable, Subject } from 'rxjs';
export class TokenService {
registry: CICRegistry;
tokenRegistry: TokenRegistry;
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();
LoadEvent: EventEmitter<number> = new EventEmitter<number>();
constructor(private httpClient: HttpClient) {}
async init(): Promise<void> {
this.registry = await RegistryService.getRegistry();
constructor(private httpClient: HttpClient, private registryService: RegistryService) {
this.registry = registryService.getRegistry();
this.registry.load();
this.registry.onload = async (address: string): Promise<void> => {
this.tokenRegistry = new TokenRegistry(
await this.registry.getContractAddressByName('TokenRegistry')
);
this.onload(this.tokenRegistry !== undefined);
this.LoadEvent.next(Date.now());
};
}
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> {
async getTokens(): Promise<Array<Promise<string>>> {
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);
}
return Array.from({ length: count }, async (v, i) => await this.tokenRegistry.entry(i));
}
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;
getTokenBySymbol(symbol: string): Observable<any> {
return this.httpClient.get(`${environment.cicCacheUrl}/tokens/${symbol}`);
}
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>> {
async getTokenBalance(address: string): Promise<number> {
const sarafuToken = await this.registry.addToken(await this.tokenRegistry.entry(0));
return await sarafuToken.methods.balanceOf(address).call();
}

View File

@@ -17,7 +17,6 @@ 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({
@@ -35,15 +34,12 @@ export class TransactionService {
private httpClient: HttpClient,
private authService: AuthService,
private userService: UserService,
private loggingService: LoggingService
private loggingService: LoggingService,
private registryService: RegistryService
) {
this.web3 = Web3Service.getInstance();
}
async init(): Promise<void> {
await this.authService.init();
await this.userService.init();
this.registry = await RegistryService.getRegistry();
this.web3 = this.registryService.getWeb3();
this.registry = registryService.getRegistry();
this.registry.load();
}
getAllTransactions(offset: number, limit: number): Observable<any> {
@@ -51,7 +47,7 @@ export class TransactionService {
}
getAddressTransactions(address: string, offset: number, limit: number): Observable<any> {
return this.httpClient.get(`${environment.cicCacheUrl}/tx/user/${address}/${offset}/${limit}`);
return this.httpClient.get(`${environment.cicCacheUrl}/tx/${address}/${offset}/${limit}`);
}
async setTransaction(transaction, cacheSize: number): Promise<void> {
@@ -66,11 +62,10 @@ export class TransactionService {
.pipe(first())
.subscribe(
(res) => {
transaction.sender = this.getAccountInfo(res, cacheSize);
transaction.sender = this.getAccountInfo(res.body);
},
(error) => {
transaction.sender = defaultAccount;
this.userService.addAccount(defaultAccount, cacheSize);
}
);
this.userService
@@ -78,11 +73,10 @@ export class TransactionService {
.pipe(first())
.subscribe(
(res) => {
transaction.recipient = this.getAccountInfo(res, cacheSize);
transaction.recipient = this.getAccountInfo(res.body);
},
(error) => {
transaction.recipient = defaultAccount;
this.userService.addAccount(defaultAccount, cacheSize);
}
);
} finally {
@@ -103,11 +97,10 @@ export class TransactionService {
.pipe(first())
.subscribe(
(res) => {
conversion.sender = conversion.recipient = this.getAccountInfo(res);
conversion.sender = conversion.recipient = this.getAccountInfo(res.body);
},
(error) => {
conversion.sender = conversion.recipient = defaultAccount;
this.userService.addAccount(defaultAccount, cacheSize);
}
);
} finally {
@@ -116,16 +109,9 @@ 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 = Math.min(this.transactions.length, cacheSize);
this.transactions.length = cacheSize;
}
this.transactionList.next(this.transactions);
}
@@ -135,10 +121,9 @@ export class TransactionService {
this.transactionList.next(this.transactions);
}
getAccountInfo(account: string, cacheSize: number = 100): any {
getAccountInfo(account: string): 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;
}
@@ -148,7 +133,6 @@ export class TransactionService {
recipientAddress: string,
value: number
): Promise<any> {
this.registry.onload = async (addressReturned: string): Promise<void> => {
const transferAuthAddress = await this.registry.getContractAddressByName(
'TransferAuthorization'
);
@@ -185,6 +169,5 @@ export class TransactionService {
this.loggingService.sendInfoLevelMessage(`Result: ${result}`);
const transaction = await this.web3.eth.getTransaction(result.transactionHash);
this.loggingService.sendInfoLevelMessage(`Transaction: ${transaction}`);
};
}
}

View File

@@ -39,20 +39,20 @@ export class UserService {
private httpClient: HttpClient,
private loggingService: LoggingService,
private tokenService: TokenService,
private registryService: RegistryService,
private authService: AuthService
) {}
async init(): Promise<void> {
await this.authService.init();
await this.tokenService.init();
this.keystore = this.authService.mutableKeyStore;
) {
this.authService.init().then(() => {
this.keystore = authService.mutableKeyStore;
this.signer = new PGPSigner(this.keystore);
this.registry = await RegistryService.getRegistry();
});
this.registry = registryService.getRegistry();
this.registry.load();
}
resetPin(phone: string): Observable<any> {
const params: HttpParams = new HttpParams().set('phoneNumber', phone);
return this.httpClient.put(`${environment.cicUssdUrl}/pin`, { params });
return this.httpClient.get(`${environment.cicUssdUrl}/pin`, { params });
}
getAccountStatus(phone: string): Observable<any> {
@@ -183,7 +183,9 @@ export class UserService {
'AccountRegistry'
);
const accountIndexQuery = new AccountIndex(accountIndexAddress);
const accountAddresses: Array<string> = await accountIndexQuery.last(limit);
const accountAddresses: Array<string> = await accountIndexQuery.last(
await accountIndexQuery.totalAccounts()
);
this.loggingService.sendInfoLevelMessage(accountAddresses);
for (const accountAddress of accountAddresses.slice(offset, offset + limit)) {
await this.getAccountByAddress(accountAddress, limit);
@@ -201,14 +203,16 @@ export class UserService {
const account: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
const accountInfo = account.m.data;
await personValidation(accountInfo);
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.addAccount(accountInfo, limit);
this.accounts.unshift(accountInfo);
if (this.accounts.length > limit) {
this.accounts.length = limit;
}
this.accountsList.next(this.accounts);
accountSubject.next(accountInfo);
});
return accountSubject.asObservable();
@@ -260,23 +264,4 @@ 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);
}
}

View File

@@ -1,16 +0,0 @@
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();
});
});

View File

@@ -1,19 +0,0 @@
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;
}
}

View File

@@ -1 +1,2 @@
<app-network-status></app-network-status>
<router-outlet (activate)="onResize(mediaQuery)"></router-outlet>

View File

@@ -27,23 +27,28 @@ export class AppComponent implements OnInit {
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();
(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);
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 🦗?
}
})();
this.mediaQuery.addEventListener('change', this.onResize);
this.onResize(this.mediaQuery);
}
ngOnInit(): void {
if (!this.swUpdate.isEnabled) {
this.swUpdate.available.subscribe(() => {
if (confirm('New Version available. Load New Version?')) {

View File

@@ -1,9 +1,8 @@
<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">
<mat-card-title class="card-header pt-4 pb-4 text-center bg-dark">
<a routerLink="/">
<h1 class="text-white">CICADA</h1>
</a>

View File

@@ -2,8 +2,6 @@ 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({
@@ -21,17 +19,18 @@ export class AuthComponent implements OnInit {
constructor(
private authService: AuthService,
private formBuilder: FormBuilder,
private router: Router,
private errorDialogService: ErrorDialogService
private router: Router
) {}
async ngOnInit(): Promise<void> {
this.keyForm = this.formBuilder.group({
key: ['', Validators.required],
});
if (this.authService.getPrivateKey()) {
this.authService.loginView();
}
await this.authService.init();
// if (this.authService.privateKey !== undefined) {
// const setKey = await this.authService.setKey(this.authService.privateKey);
// }
// }
}
get keyFormStub(): any {
@@ -50,20 +49,19 @@ export class AuthComponent implements OnInit {
this.loading = false;
}
async login(): Promise<void> {
try {
const loginResult = await this.authService.login();
if (loginResult) {
login(): void {
// TODO check if we have privatekey
// Send us to home if we have a private key
// talk to meta somehow
// in the error interceptor if 401/403 handle it
// if 200 go /home
if (this.authService.getPrivateKey()) {
this.router.navigate(['/home']);
}
} catch (HttpError) {
this.errorDialogService.openDialog({
message: HttpError.message,
});
}
}
switchWindows(): void {
this.authService.sessionToken = undefined;
const divOne: HTMLElement = document.getElementById('one');
const divTwo: HTMLElement = document.getElementById('two');
this.toggleDisplay(divOne);

View File

@@ -10,7 +10,6 @@ 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],
@@ -23,7 +22,6 @@ import { SharedModule } from '@app/shared/shared.module';
MatInputModule,
MatButtonModule,
MatRippleModule,
SharedModule,
],
})
export class AuthModule {}

View File

@@ -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 | unixDate}}</span>
<span class="ml-2"><strong>Created:</strong> {{account?.date_registered | date}}</span>
<span class="ml-2"><strong>Address:</strong>
<a href="{{bloxbergLink}}" target="_blank"> {{accountAddress}} </a>
<img src="assets/images/checklist.svg" class="ml-2" height="20rem" (click)="copyAddress()" alt="Copy">
@@ -48,19 +48,10 @@
<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-label>Name(s): *</mat-label>
<input matInput type="text" id="givenNames" placeholder="{{account?.vcard?.fn[0].value}}"
value="{{account?.vcard?.fn[0].value}}" formControlName="name" [errorStateMatcher]="matcher">
<mat-error *ngIf="submitted && accountInfoFormStub.name.errors">Name is required.</mat-error>
</mat-form-field>
</div>
@@ -219,9 +210,12 @@
<tr>
<td>{{account?.vcard?.fn[0].value}}</td>
<td>{{account?.balance | tokenRatio}}</td>
<td>{{account?.date_registered | unixDate}}</td>
<td>{{account?.date_registered | date}}</td>
<td>
<span class="badge badge-success badge-pill">
<span *ngIf="accountStatus === 'active'" class="badge badge-success badge-pill">
{{accountStatus}}
</span>
<span *ngIf="accountStatus === 'blocked'" class="badge badge-danger badge-pill">
{{accountStatus}}
</span>
</td>
@@ -234,7 +228,7 @@
<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>
<app-transaction-details [transaction]="transaction"></app-transaction-details>
<div class="card mt-1">
<div class="card-header">
<div class="row">
@@ -258,17 +252,17 @@
<mat-icon matSuffix>search</mat-icon>
</mat-form-field>
<table mat-table class="mat-elevation-z10" [dataSource]="transactionsDataSource" matSort matSortActive="created"
<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>
<td mat-cell *matCellDef="let transaction"> {{transaction?.sender?.vcard.fn[0].value}} </td>
</ng-container>
<ng-container matColumnDef="recipient">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Recipient </th>
<td mat-cell *matCellDef="let transaction"> {{transaction?.recipient?.vcard.fn[0].value || transaction.to}} </td>
<td mat-cell *matCellDef="let transaction"> {{transaction?.recipient?.vcard.fn[0].value}} </td>
</ng-container>
<ng-container matColumnDef="value">
@@ -281,7 +275,7 @@
<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>
<td mat-cell *matCellDef="let transaction"> {{transaction?.tx.timestamp | date}} </td>
</ng-container>
<ng-container matColumnDef="type">
@@ -291,10 +285,10 @@
</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-header-row *matHeaderRowDef="transactionsDisplayedColumns"></mat-header-row>
<mat-row *matRowDef="let transaction; columns: transactionsDisplayedColumns" matRipple
(click)="viewTransaction(transaction)"></mat-row>
</mat-table>
<mat-paginator #TransactionTablePaginator="matPaginator" [pageSize]="transactionsDefaultPageSize"
[pageSizeOptions]="transactionsPageSizeOptions" showFirstLastButtons></mat-paginator>
@@ -343,7 +337,7 @@
<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>
<mat-cell *matCellDef="let user"> {{user?.date_registered | date}} </mat-cell>
</ng-container>
<ng-container matColumnDef="balance">

View File

@@ -78,17 +78,8 @@ 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({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
name: ['', Validators.required],
phoneNumber: ['', Validators.required],
age: ['', Validators.required],
type: ['', Validators.required],
@@ -99,71 +90,36 @@ export class AccountDetailsComponent implements OnInit {
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();
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);
const fullName = this.account.vcard?.fn[0].value.split(' ');
// this.userService.getAccountStatus(this.account.vcard?.tel[0].value).pipe(first())
// .subscribe(response => this.accountStatus = response);
this.accountInfoForm.patchValue({
firstName: fullName[0],
lastName: fullName.slice(1).join(' '),
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 ||
this.userService.getCategoryByProduct(this.account.products[0]),
businessCategory: this.account.category,
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;
}),
location: this.account.location.area,
locationType: this.account.location.area_type,
});
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.blockSyncService.blockSync(this.accountAddress);
});
this.userService
.getCategories()
@@ -191,6 +147,22 @@ 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();
}
@@ -220,7 +192,7 @@ export class AccountDetailsComponent implements OnInit {
}
const accountKey = await this.userService.changeAccountInfo(
this.accountAddress,
this.accountInfoFormStub.firstName.value + ' ' + this.accountInfoFormStub.lastName.value,
this.accountInfoFormStub.name.value,
this.accountInfoFormStub.phoneNumber.value,
this.accountInfoFormStub.age.value,
this.accountInfoFormStub.type.value,

View File

@@ -30,8 +30,7 @@ export class AccountSearchComponent implements OnInit {
private router: Router
) {}
async ngOnInit(): Promise<void> {
await this.userService.init();
ngOnInit(): void {
this.nameSearchForm = this.formBuilder.group({
name: ['', Validators.required],
});

View File

@@ -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 | unixDate}} </mat-cell>
<mat-cell *matCellDef="let user"> {{user?.date_registered | date}} </mat-cell>
</ng-container>
<ng-container matColumnDef="balance">

View File

@@ -32,26 +32,28 @@ export class AccountsComponent implements OnInit {
private userService: UserService,
private loggingService: LoggingService,
private router: Router
) {}
async ngOnInit(): Promise<void> {
await this.userService.init();
) {
(async () => {
try {
// TODO it feels like this should be in the onInit handler
await this.userService.loadAccounts(100);
} catch (error) {
this.loggingService.sendErrorLevelMessage('Failed to load accounts', this, { error });
}
})();
this.userService
.getAccountTypes()
.pipe(first())
.subscribe((res) => (this.accountTypes = res));
}
ngOnInit(): void {
this.userService.accountsSubject.subscribe((accounts) => {
this.dataSource = new MatTableDataSource<any>(accounts);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
this.accounts = accounts;
});
this.userService
.getAccountTypes()
.pipe(first())
.subscribe((res) => (this.accountTypes = res));
}
doFilter(value: string): void {

View File

@@ -26,8 +26,7 @@ export class CreateAccountComponent implements OnInit {
private userService: UserService
) {}
async ngOnInit(): Promise<void> {
await this.userService.init();
ngOnInit(): void {
this.createForm = this.formBuilder.group({
accountType: ['', Validators.required],
idNumber: ['', Validators.required],

View File

@@ -30,10 +30,7 @@ export class AdminComponent implements OnInit {
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
constructor(private userService: UserService, private loggingService: LoggingService) {}
async ngOnInit(): Promise<void> {
await this.userService.init();
constructor(private userService: UserService, private loggingService: LoggingService) {
this.userService.getActions();
this.userService.actionsSubject.subscribe((actions) => {
this.dataSource = new MatTableDataSource<any>(actions);
@@ -43,6 +40,8 @@ export class AdminComponent implements OnInit {
});
}
ngOnInit(): void {}
doFilter(value: string): void {
this.dataSource.filter = value.trim().toLocaleLowerCase();
}

View File

@@ -23,10 +23,9 @@
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>
<h4>Kobo Toolbox Credentials</h4>
<span><strong>Username: </strong> admin_reserve </span><br>
<span><strong>Password: </strong> ******** </span>
</div>
<hr>
<div class="card-body">

View File

@@ -17,22 +17,19 @@ 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) {}
async ngOnInit(): Promise<void> {
await this.authService.init();
this.authService.trustedUsersSubject.subscribe((users) => {
this.dataSource = new MatTableDataSource<any>(users);
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;
this.trustedUsers = users;
});
this.userInfo = this.authService.getPrivateKeyInfo();
}
doFilter(value: string): void {

View File

@@ -1,36 +1,60 @@
<div *ngIf="token" class="mb-3 mt-1">
<div class="card text-center">
<!-- 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">
<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>
Token
</mat-card-title>
<div class="card-body">
<div>
<span><strong>Name:</strong> {{token?.name}}</span>
<span><strong>Name:</strong> {{token.name}}</span>
</div>
<div>
<span><strong>Symbol:</strong> {{token?.symbol}}</span>
<span><strong>Symbol:</strong> {{token.symbol}}</span>
</div>
<div>
<span><strong>Address:</strong> {{token?.address}}</span>
<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>
<span><strong>Supply:</strong> {{token.supply | tokenRatio}}</span>
</div><br>
<div>
<h2>Reserve</h2>
<div>
<span><strong>Weight:</strong> {{token?.reserveRatio}}</span>
<span><strong>Weight:</strong> {{token.reserveRatio}}</span>
</div>
<div>
<span><strong>Owner:</strong> {{token?.owner}}</span>
<span><strong>Owner:</strong> {{token.owner}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<app-footer appMenuSelection></app-footer>
</div>
<!-- ============================================================== -->
<!-- End Page content -->
<!-- ============================================================== -->
</div>

View File

@@ -1,12 +1,8 @@
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Input,
OnInit,
Output,
} from '@angular/core';
import { Token } from '@app/_models';
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { TokenService } from '@app/_services';
import { first } from 'rxjs/operators';
import { Token } from '../../../_models';
@Component({
selector: 'app-token-details',
@@ -15,16 +11,18 @@ import { Token } from '@app/_models';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TokenDetailsComponent implements OnInit {
@Input() token: Token;
token: Token;
@Output() closeWindow: EventEmitter<any> = new EventEmitter<any>();
constructor() {}
constructor(private route: ActivatedRoute, private tokenService: TokenService) {
this.route.paramMap.subscribe((params: Params) => {
this.tokenService
.getTokenBySymbol(params.get('id'))
.pipe(first())
.subscribe((res) => {
this.token = res;
});
});
}
ngOnInit(): void {}
close(): void {
this.token = null;
this.closeWindow.emit(this.token);
}
}

View File

@@ -24,9 +24,6 @@
</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">

View File

@@ -5,7 +5,8 @@ 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';
import { TokenRegistry } from '../../_eth';
import { Token } from '../../_models';
@Component({
selector: 'app-tokens',
@@ -18,8 +19,7 @@ export class TokensComponent implements OnInit {
columnsToDisplay: Array<string> = ['name', 'symbol', 'address', 'supply'];
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
tokens: Array<Token>;
token: Token;
tokens: Array<Promise<string>>;
constructor(
private tokenService: TokenService,
@@ -28,25 +28,22 @@ export class TokensComponent implements OnInit {
) {}
async ngOnInit(): Promise<void> {
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.tokenService.LoadEvent.subscribe(async () => {
this.tokens = await this.tokenService.getTokens();
});
this.tokens = await this.tokenService.getTokens();
this.loggingService.sendInfoLevelMessage(this.tokens);
this.dataSource = new MatTableDataSource(this.tokens);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
this.tokens = tokens;
});
}
doFilter(value: string): void {
this.dataSource.filter = value.trim().toLocaleLowerCase();
}
viewToken(token): void {
this.token = token;
async viewToken(token): Promise<void> {
await this.router.navigateByUrl(`/tokens/${token.symbol}`);
}
downloadCsv(): void {

View File

@@ -1,9 +1,9 @@
<div *ngIf="transaction" class="mb-3 mt-1">
<div *ngIf="transaction | async" class="mb-3 mt-1">
<div class="card text-center">
<mat-card-title class="card-header">
<div class="row">
TRANSACTION DETAILS
<button mat-raised-button type="button" class="btn btn-outline-secondary ml-auto mr-2" (click)="close()"> CLOSE </button>
<button mat-raised-button type="button" class="btn btn-outline-secondary ml-auto mr-2" (click)="transaction = null"> CLOSE </button>
</div>
</mat-card-title>
<div *ngIf="transaction.type == 'transaction'" class="card-body">
@@ -66,7 +66,7 @@
<span>Success: {{transaction.tx.success}}</span>
</li>
<li class="list-group-item">
<span>Timestamp: {{transaction.tx.timestamp | unixDate}}</span>
<span>Timestamp: {{transaction.tx.timestamp | date}}</span>
</li>
</ul><br>
<div class="mb-3">

View File

@@ -1,11 +1,4 @@
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Input,
OnInit,
Output,
} from '@angular/core';
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { TransactionService } from '@app/_services';
import { copyToClipboard } from '@app/_helpers';
@@ -20,9 +13,6 @@ 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;
@@ -33,8 +23,7 @@ export class TransactionDetailsComponent implements OnInit {
private snackBar: MatSnackBar
) {}
async ngOnInit(): Promise<void> {
await this.transactionService.init();
ngOnInit(): void {
if (this.transaction?.type === 'conversion') {
this.traderBloxbergLink =
'https://blockexplorer.bloxberg.org/address/' + this.transaction?.trader + '/transactions';
@@ -72,9 +61,4 @@ export class TransactionDetailsComponent implements OnInit {
this.snackBar.open(address + ' copied successfully!', 'Close', { duration: 3000 });
}
}
close(): void {
this.transaction = null;
this.closeWindow.emit(this.transaction);
}
}

View File

@@ -22,7 +22,7 @@
</mat-card-title>
<div class="card-body">
<app-transaction-details [transaction]="transaction" (closeWindow)="transaction = $event"></app-transaction-details>
<app-transaction-details [transaction]="transaction"></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 || transaction.from}} </td>
<td mat-cell *matCellDef="let transaction"> {{transaction?.sender?.vcard.fn[0].value}} </td>
</ng-container>
<ng-container matColumnDef="recipient">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Recipient </th>
<td mat-cell *matCellDef="let transaction"> {{transaction?.recipient?.vcard.fn[0].value || transaction.to}} </td>
<td mat-cell *matCellDef="let transaction"> {{transaction?.recipient?.vcard.fn[0].value}} </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 | unixDate}} </td>
<td mat-cell *matCellDef="let transaction"> {{transaction?.tx.timestamp | date}} </td>
</ng-container>
<ng-container matColumnDef="type">

View File

@@ -36,19 +36,17 @@ export class TransactionsComponent implements OnInit, AfterViewInit {
private blockSyncService: BlockSyncService,
private transactionService: TransactionService,
private userService: UserService
) {}
) {
this.blockSyncService.blockSync();
}
async ngOnInit(): Promise<void> {
ngOnInit(): void {
this.transactionService.transactionsSubject.subscribe((transactions) => {
this.transactionDataSource = new MatTableDataSource<any>(transactions);
this.transactionDataSource.paginator = this.paginator;
this.transactionDataSource.sort = this.sort;
this.transactions = transactions;
});
await this.blockSyncService.init();
await this.transactionService.init();
await this.userService.init();
await this.blockSyncService.blockSync();
this.userService
.getTransactionTypes()
.pipe(first())

View File

@@ -1,8 +0,0 @@
import { UnixDatePipe } from './unix-date.pipe';
describe('UnixDatePipe', () => {
it('create an instance', () => {
const pipe = new UnixDatePipe();
expect(pipe).toBeTruthy();
});
});

View File

@@ -1,10 +0,0 @@
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');
}
}

View File

@@ -1,8 +1,5 @@
<!-- 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>
2020 © Grassroots Economics
</footer>
<!-- end Footer -->

View File

@@ -7,7 +7,6 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FooterComponent implements OnInit {
currentYear = new Date().getFullYear();
constructor() {}
ngOnInit(): void {}

View File

@@ -1,4 +1,4 @@
<nav class="navbar navbar-dark background-dark">
<nav class="navbar navbar-dark bg-dark">
<h1 class="navbar-brand">
<div *ngIf="noInternetConnection; then offlineBlock else onlineBlock"></div>
<ng-template #offlineBlock>

View File

@@ -12,7 +12,6 @@ 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: [
@@ -25,7 +24,6 @@ import { UnixDatePipe } from './_pipes/unix-date.pipe';
ErrorDialogComponent,
SafePipe,
NetworkStatusComponent,
UnixDatePipe,
],
exports: [
TopbarComponent,
@@ -35,7 +33,6 @@ import { UnixDatePipe } from './_pipes/unix-date.pipe';
TokenRatioPipe,
SafePipe,
NetworkStatusComponent,
UnixDatePipe,
],
imports: [CommonModule, RouterModule, MatIconModule, MatDialogModule],
})

View File

@@ -1,6 +1,5 @@
<!-- ========== Left Sidebar Start ========== -->
<div id="sidebar">
<app-network-status></app-network-status>
<nav>
<div class="sidebar-header">

View File

@@ -42,7 +42,7 @@ Driver.prototype.sync = function (n) {
const processor = async (b, t) => {
return await self.process(b, t);
};
self.syncer(self.lo, self.hi, self.filters[0], self.filters[1], countGetter, processor);
self.syncer(self, self.lo, self.hi, self.filters[0], self.filters[1], countGetter, processor);
};
Driver.prototype.process = function (b, t) {

View File

@@ -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://user.dev.grassrootseconomics.net',
cicUssdUrl: 'https://ussd.dev.grassrootseconomics.net',
registryAddress: '0xea6225212005e86a4490018ded4bf37f3e772161',
trustedDeclaratorAddress: '0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C',
};

View File

@@ -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://user.dev.grassrootseconomics.net',
cicUssdUrl: 'https://ussd.dev.grassrootseconomics.net',
registryAddress: '0xea6225212005e86a4490018ded4bf37f3e772161',
trustedDeclaratorAddress: '0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C',
};

View File

@@ -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://user.dev.grassrootseconomics.net',
cicUssdUrl: 'https://ussd.dev.grassrootseconomics.net',
registryAddress: '0xea6225212005e86a4490018ded4bf37f3e772161',
trustedDeclaratorAddress: '0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C',
};

View File

@@ -18,8 +18,8 @@ body {
background: #fafafa;
}
.background-dark {
background: #313a46 !important;
.bg-dark {
background: #313a46;
}
p {