From 92f025934f409cc24703dddb77757900bf57d89e Mon Sep 17 00:00:00 2001 From: Jason Dreyzehner Date: Wed, 3 Oct 2018 00:16:32 -0400 Subject: [PATCH] feat(insight): begin building out blocks view, related components --- packages/bitcore-node/src/models/block.ts | 20 +---- packages/bitcore-node/src/services/storage.ts | 24 ++---- packages/bitcore-node/src/types/Block.ts | 18 +++++ packages/bitcore-node/src/types/Query.ts | 12 +++ packages/insight/angular.json | 6 +- packages/insight/package-lock.json | 40 +++++++++- packages/insight/package.json | 1 + packages/insight/src/app/app.component.html | 41 +++++----- packages/insight/src/app/app.component.scss | 5 ++ packages/insight/src/app/app.component.ts | 3 +- .../block-list/block-list.component.html | 9 ++- .../block-list/block-list.component.scss | 3 + .../blocks/block-list/block-list.component.ts | 47 +++++++----- .../src/app/blocks/block/block.component.html | 59 ++++++++++++++ .../src/app/blocks/block/block.component.scss | 72 ++++++++++++++++++ .../app/blocks/block/block.component.spec.ts | 25 ++++++ .../src/app/blocks/block/block.component.ts | 27 +++++++ .../insight/src/app/blocks/blocks.module.ts | 7 +- .../insight/src/app/blocks/blocks.page.html | 21 ++--- .../insight/src/app/blocks/blocks.page.ts | 22 +++++- .../src/app/services/api/api.service.ts | 20 +++-- .../src/app/services/config/config.service.ts | 1 + .../services/network/network.service.spec.ts | 12 +++ .../app/services/network/network.service.ts | 19 +++++ .../currency-value.component.html | 3 + .../currency-value.component.scss | 0 .../currency-value.component.spec.ts | 24 ++++++ .../currency-value.component.ts | 35 +++++++++ .../shared/date-time/date-time.component.html | 4 + .../shared/date-time/date-time.component.scss | 0 .../date-time/date-time.component.spec.ts | 25 ++++++ .../shared/date-time/date-time.component.ts | 17 +++++ .../src/app/shared/shared.module.spec.ts | 13 ++++ .../insight/src/app/shared/shared.module.ts | 12 +++ .../insight/src/app/types/bitcore-node.ts | 6 +- packages/insight/src/assets/icon/favicon.png | Bin 930 -> 1284 bytes .../insight/src/assets/icon/favicon@2x.png | Bin 0 -> 2538 bytes .../insight/src/assets/icon/favicon@4x.png | Bin 0 -> 5696 bytes packages/insight/src/assets/icon/logo.svg | 13 ++++ .../src/environments/environment.prod.ts | 1 + packages/insight/src/index.html | 48 ++++++++---- packages/insight/src/theme/variables.scss | 33 ++++++-- 42 files changed, 624 insertions(+), 124 deletions(-) create mode 100644 packages/bitcore-node/src/types/Block.ts create mode 100644 packages/bitcore-node/src/types/Query.ts create mode 100644 packages/insight/src/app/app.component.scss create mode 100644 packages/insight/src/app/blocks/block/block.component.html create mode 100644 packages/insight/src/app/blocks/block/block.component.scss create mode 100644 packages/insight/src/app/blocks/block/block.component.spec.ts create mode 100644 packages/insight/src/app/blocks/block/block.component.ts create mode 100644 packages/insight/src/app/services/network/network.service.spec.ts create mode 100644 packages/insight/src/app/services/network/network.service.ts create mode 100644 packages/insight/src/app/shared/currency-value/currency-value.component.html create mode 100644 packages/insight/src/app/shared/currency-value/currency-value.component.scss create mode 100644 packages/insight/src/app/shared/currency-value/currency-value.component.spec.ts create mode 100644 packages/insight/src/app/shared/currency-value/currency-value.component.ts create mode 100644 packages/insight/src/app/shared/date-time/date-time.component.html create mode 100644 packages/insight/src/app/shared/date-time/date-time.component.scss create mode 100644 packages/insight/src/app/shared/date-time/date-time.component.spec.ts create mode 100644 packages/insight/src/app/shared/date-time/date-time.component.ts create mode 100644 packages/insight/src/app/shared/shared.module.spec.ts create mode 100644 packages/insight/src/app/shared/shared.module.ts create mode 100644 packages/insight/src/assets/icon/favicon@2x.png create mode 100644 packages/insight/src/assets/icon/favicon@4x.png create mode 100644 packages/insight/src/assets/icon/logo.svg diff --git a/packages/bitcore-node/src/models/block.ts b/packages/bitcore-node/src/models/block.ts index a599936749a..47d170eee05 100644 --- a/packages/bitcore-node/src/models/block.ts +++ b/packages/bitcore-node/src/models/block.ts @@ -5,25 +5,9 @@ import { LoggifyClass } from '../decorators/Loggify'; import { Bitcoin } from '../types/namespaces/Bitcoin'; import { BaseModel, MongoBound } from './base'; import logger from '../logger'; +import { IBlock } from '../types/Block'; -export type IBlock = { - chain: string; - network: string; - height: number; - hash: string; - version: number; - merkleRoot: string; - time: Date; - timeNormalized: Date; - nonce: number; - previousBlockHash: string; - nextBlockHash: string; - transactionCount: number; - size: number; - bits: number; - reward: number; - processed: boolean; -}; +export { IBlock }; @LoggifyClass export class Block extends BaseModel { diff --git a/packages/bitcore-node/src/services/storage.ts b/packages/bitcore-node/src/services/storage.ts index e8669b364e4..0e68c8bbc8d 100644 --- a/packages/bitcore-node/src/services/storage.ts +++ b/packages/bitcore-node/src/services/storage.ts @@ -8,14 +8,10 @@ import { ObjectID } from 'bson'; import { MongoClient, Db, Cursor } from 'mongodb'; import { MongoBound } from '../models/base'; import '../models'; +import { StreamingFindOptions } from '../types/Query'; + +export { StreamingFindOptions }; -export type StreamingFindOptions = Partial<{ - paging: keyof T; - since: T[keyof T]; - sort: any; - direction: 1 | -1; - limit: number; -}>; @LoggifyClass export class StorageService { client?: MongoClient; @@ -88,8 +84,8 @@ export class StorageService { typecastedValue = new Date(oldValue) as any; break; } - // TODO: Micah check this! - } else if (modelKey == "_id") { + // TODO: Micah check this! + } else if (modelKey == '_id') { typecastedValue = new ObjectID(oldValue) as any; } } @@ -123,17 +119,13 @@ export class StorageService { } getFindOptions(model: TransformableModel, originalOptions: StreamingFindOptions) { let options: StreamingFindOptions = {}; - let query: any = {}, since: any; - if ( originalOptions.paging && - this.validPagingProperty(model, originalOptions.paging) - ) { - - + let query: any = {}, + since: any; + if (originalOptions.paging && this.validPagingProperty(model, originalOptions.paging)) { if (originalOptions.since !== undefined) { since = this.typecastForDb(model, originalOptions.paging, originalOptions.since); } - if (originalOptions.direction && Number(originalOptions.direction) === 1) { if (since) { query[originalOptions.paging] = { $gt: since }; diff --git a/packages/bitcore-node/src/types/Block.ts b/packages/bitcore-node/src/types/Block.ts new file mode 100644 index 00000000000..551c866caa2 --- /dev/null +++ b/packages/bitcore-node/src/types/Block.ts @@ -0,0 +1,18 @@ +export type IBlock = { + chain: string; + network: string; + height: number; + hash: string; + version: number; + merkleRoot: string; + time: Date; + timeNormalized: Date; + nonce: number; + previousBlockHash: string; + nextBlockHash: string; + transactionCount: number; + size: number; + bits: number; + reward: number; + processed: boolean; +}; diff --git a/packages/bitcore-node/src/types/Query.ts b/packages/bitcore-node/src/types/Query.ts new file mode 100644 index 00000000000..ccc3f54cdbd --- /dev/null +++ b/packages/bitcore-node/src/types/Query.ts @@ -0,0 +1,12 @@ +const enum Direction { + ascending = 1, + descending = -1 +} + +export type StreamingFindOptions = Partial<{ + paging: keyof T; + since: T[keyof T]; + sort: any; + direction: Direction; + limit: number; +}>; diff --git a/packages/insight/angular.json b/packages/insight/angular.json index 80cbc3fc6c7..1f502349fc6 100644 --- a/packages/insight/angular.json +++ b/packages/insight/angular.json @@ -9,7 +9,11 @@ "sourceRoot": "src", "projectType": "application", "prefix": "app", - "schematics": {}, + "schematics": { + "@schematics/angular:component": { + "styleext": "scss" + } + }, "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", diff --git a/packages/insight/package-lock.json b/packages/insight/package-lock.json index 572c0454152..15045e33970 100644 --- a/packages/insight/package-lock.json +++ b/packages/insight/package-lock.json @@ -1294,6 +1294,14 @@ "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.3.0", "uri-js": "^3.0.2" + }, + "dependencies": { + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + } } }, "ajv-errors": { @@ -3984,10 +3992,9 @@ "dev": true }, "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "fast-json-stable-stringify": { "version": "2.0.0", @@ -5028,6 +5035,14 @@ "fast-deep-equal": "^1.0.0", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.3.0" + }, + "dependencies": { + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + } } } } @@ -6078,6 +6093,14 @@ "fast-deep-equal": "^1.0.0", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.3.0" + }, + "dependencies": { + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + } } }, "schema-utils": { @@ -7291,6 +7314,15 @@ "fast-deep-equal": "^1.0.0", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.3.0" + }, + "dependencies": { + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true, + "optional": true + } } }, "ansi-styles": { diff --git a/packages/insight/package.json b/packages/insight/package.json index 3ff7713e7f0..6ce07704d8c 100644 --- a/packages/insight/package.json +++ b/packages/insight/package.json @@ -53,6 +53,7 @@ "@ionic/angular": "^4.0.0-beta.12", "backoff-rxjs": "0.0.10", "core-js": "^2.5.3", + "fast-deep-equal": "^2.0.1", "ngx-logger": "^3.3.2", "rxjs": "~6.3.3", "zone.js": "^0.8.26" diff --git a/packages/insight/src/app/app.component.html b/packages/insight/src/app/app.component.html index ea28e809a67..6fb0efb5e30 100644 --- a/packages/insight/src/app/app.component.html +++ b/packages/insight/src/app/app.component.html @@ -1,18 +1,25 @@ - - - - - - - - {{ p.title }} - - - - - - - - - \ No newline at end of file + + + + + + + {{ p.title }} + + + + + + + + diff --git a/packages/insight/src/app/app.component.scss b/packages/insight/src/app/app.component.scss new file mode 100644 index 00000000000..a7faad2b7d9 --- /dev/null +++ b/packages/insight/src/app/app.component.scss @@ -0,0 +1,5 @@ +.dark-theme { + --ion-background-color: #3d3d4d; + --ion-item-background-color-active: lighten(--ion-background-color, 15%); + --ion-text-color: rgba(255, 255, 255, 0.9); +} diff --git a/packages/insight/src/app/app.component.ts b/packages/insight/src/app/app.component.ts index d2033f19e47..30befae96d1 100644 --- a/packages/insight/src/app/app.component.ts +++ b/packages/insight/src/app/app.component.ts @@ -6,7 +6,8 @@ import { Platform } from '@ionic/angular'; @Component({ selector: 'app-root', - templateUrl: 'app.component.html' + templateUrl: 'app.component.html', + styleUrls: ['app.component.scss'] }) export class AppComponent { public appPages = [ diff --git a/packages/insight/src/app/blocks/block-list/block-list.component.html b/packages/insight/src/app/blocks/block-list/block-list.component.html index 358de8526d3..ea994cc5407 100644 --- a/packages/insight/src/app/blocks/block-list/block-list.component.html +++ b/packages/insight/src/app/blocks/block-list/block-list.component.html @@ -1,3 +1,6 @@ -

- block-list works! -

+ diff --git a/packages/insight/src/app/blocks/block-list/block-list.component.scss b/packages/insight/src/app/blocks/block-list/block-list.component.scss index e69de29bb2d..3cfb953ebaa 100644 --- a/packages/insight/src/app/blocks/block-list/block-list.component.scss +++ b/packages/insight/src/app/blocks/block-list/block-list.component.scss @@ -0,0 +1,3 @@ +app-block { + cursor: pointer; +} diff --git a/packages/insight/src/app/blocks/block-list/block-list.component.ts b/packages/insight/src/app/blocks/block-list/block-list.component.ts index 612480ec1dd..68d1578ba01 100644 --- a/packages/insight/src/app/blocks/block-list/block-list.component.ts +++ b/packages/insight/src/app/blocks/block-list/block-list.component.ts @@ -1,29 +1,42 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Input, + OnInit +} from '@angular/core'; +import * as equal from 'fast-deep-equal'; +import { combineLatest, Observable } from 'rxjs'; +import { distinctUntilChanged, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api/api.service'; -import { IBlock } from '../../types/bitcore-node'; -import { Network, Ticker } from '../../types/configuration'; +import { IBlock, StreamingFindOptions } from '../../types/bitcore-node'; +import { Chain } from '../../types/configuration'; @Component({ selector: 'app-block-list', templateUrl: './block-list.component.html', - styleUrls: ['./block-list.component.scss'] + styleUrls: ['./block-list.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush }) -export class BlockListComponent { +export class BlockListComponent implements OnInit { @Input() - public ticker: Ticker; + chain: Observable; @Input() - public network: Network; + query: Observable>; @Input() - public blocks: number; - @Input() - public paging: keyof IBlock = 'height'; - - public blockStream = this.apiService.streamBlocks( - { ticker: this.ticker, network: this.network }, - { - paging: this.paging - } - ); + displayValueIn = 'BCH'; + block$: Observable; constructor(private apiService: ApiService) {} + ngOnInit() { + this.block$ = combineLatest(this.chain, this.query).pipe( + distinctUntilChanged((x, y) => equal(x, y)), + switchMap(([chain, query]) => this.apiService.streamBlocks(chain, query)), + distinctUntilChanged((x, y) => equal(x, y)) + ); + } + + goToBlock(block: IBlock) { + // tslint:disable-next-line:no-console + console.log('TODO: navigate to block', block.hash); + } } diff --git a/packages/insight/src/app/blocks/block/block.component.html b/packages/insight/src/app/blocks/block/block.component.html new file mode 100644 index 00000000000..a449b844832 --- /dev/null +++ b/packages/insight/src/app/blocks/block/block.component.html @@ -0,0 +1,59 @@ + + +
+
+
+
+ {{block.height}} +
+ + Tip + +
+ + +
+
+
+
Block Hash
+
{{block.hash}}
+
+
+ Transaction Count + + {{block.transactionCount}} + +
+
+ Reward + + + +
+
+
+ [show details] +
+
diff --git a/packages/insight/src/app/blocks/block/block.component.scss b/packages/insight/src/app/blocks/block/block.component.scss new file mode 100644 index 00000000000..8a1600a2e97 --- /dev/null +++ b/packages/insight/src/app/blocks/block/block.component.scss @@ -0,0 +1,72 @@ +.card { + border-radius: 3px; + max-width: 800px; + margin: 1em auto; + background-color: #fff; + box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.08); +} +.section { + padding: 1em; + & + .section { + border-top: 1px solid #f1f2f5; + } +} + +.header, +.identifiers { + display: flex; + align-items: center; +} + +.identifiers { + width: 150px; +} + +.height { + font-size: 1.3em; + color: var(--ion-color-primary); +} + +.tip-tag { + text-transform: uppercase; + color: #fff; + background-color: #6eb542; + border-radius: 3px; + font-size: 0.7em; + display: inline-block; + padding: 0.1em 0.5em; + margin-left: 0.5em; +} + +.time { + flex-grow: 1; + text-align: right; +} + +.time, +.value { + color: #aaa; +} + +.key, +.property { + margin-bottom: 0.3em; +} + +.time, +.key, +.value { + font-size: 0.9em; +} + +.key { + margin-right: 0.3em; +} + +.link { + color: var(--ion-color-primary); +} + +.reward { + display: inline; +} diff --git a/packages/insight/src/app/blocks/block/block.component.spec.ts b/packages/insight/src/app/blocks/block/block.component.spec.ts new file mode 100644 index 00000000000..cdd4437197d --- /dev/null +++ b/packages/insight/src/app/blocks/block/block.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BlockComponent } from './block.component'; + +describe('BlockComponent', () => { + let component: BlockComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ BlockComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(BlockComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/packages/insight/src/app/blocks/block/block.component.ts b/packages/insight/src/app/blocks/block/block.component.ts new file mode 100644 index 00000000000..7c691e0f3bf --- /dev/null +++ b/packages/insight/src/app/blocks/block/block.component.ts @@ -0,0 +1,27 @@ +import { Component, Input } from '@angular/core'; +import { IBlock } from '../../types/bitcore-node'; + +@Component({ + selector: 'app-block', + templateUrl: './block.component.html', + styleUrls: ['./block.component.scss'] +}) +export class BlockComponent { + @Input() + block: IBlock; + + /** + * The unit in which to display value – can be either a valid denomination for + * this chain or an alternative currency in which to estimate value. + */ + @Input() + displayValueIn = 'BCH'; + + @Input() + summary = true; + + listTransactionsInBlock(block: IBlock) { + // tslint:disable-next-line:no-console + console.log('TODO: jump to transaction listing for block:', block.hash); + } +} diff --git a/packages/insight/src/app/blocks/blocks.module.ts b/packages/insight/src/app/blocks/blocks.module.ts index 80300c9881a..5ee9d745f62 100644 --- a/packages/insight/src/app/blocks/blocks.module.ts +++ b/packages/insight/src/app/blocks/blocks.module.ts @@ -4,7 +4,9 @@ import { FormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { IonicModule } from '@ionic/angular'; +import { SharedModule } from '../shared/shared.module'; import { BlockListComponent } from './block-list/block-list.component'; +import { BlockComponent } from './block/block.component'; import { BlocksPage } from './blocks.page'; @NgModule({ @@ -17,8 +19,9 @@ import { BlocksPage } from './blocks.page'; path: '', component: BlocksPage } - ]) + ]), + SharedModule ], - declarations: [BlocksPage, BlockListComponent] + declarations: [BlocksPage, BlockListComponent, BlockComponent] }) export class BlocksPageModule {} diff --git a/packages/insight/src/app/blocks/blocks.page.html b/packages/insight/src/app/blocks/blocks.page.html index 0201b2a0dd0..b0ae85a823b 100644 --- a/packages/insight/src/app/blocks/blocks.page.html +++ b/packages/insight/src/app/blocks/blocks.page.html @@ -1,14 +1,15 @@ - - - - - - Blocks - - + + + + + Blocks + - - \ No newline at end of file + + diff --git a/packages/insight/src/app/blocks/blocks.page.ts b/packages/insight/src/app/blocks/blocks.page.ts index dbf8152d122..586272c755b 100644 --- a/packages/insight/src/app/blocks/blocks.page.ts +++ b/packages/insight/src/app/blocks/blocks.page.ts @@ -1,4 +1,6 @@ import { Component, OnInit } from '@angular/core'; +import { BehaviorSubject, interval } from 'rxjs'; +import { take } from 'rxjs/operators'; @Component({ selector: 'app-blocks', @@ -6,7 +8,23 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['blocks.page.scss'] }) export class BlocksPage implements OnInit { - constructor() {} + chain$ = new BehaviorSubject({ ticker: 'BTC', network: 'mainnet' }); + query$ = new BehaviorSubject({}); - ngOnInit() {} + constructor() { + this.chain$.subscribe(value => + // tslint:disable-next-line:no-console + console.log(`Chain value: ${JSON.stringify(value)}`) + ); + } + + ngOnInit() { + interval(1 * 1000) + .pipe(take(1)) + .subscribe(() => { + // tslint:disable-next-line:no-console + console.log('Simulating chain switch to BCH:'); + this.chain$.next({ ticker: 'BCH', network: 'mainnet' }); + }); + } } diff --git a/packages/insight/src/app/services/api/api.service.ts b/packages/insight/src/app/services/api/api.service.ts index 399b556fdac..900970a2c44 100644 --- a/packages/insight/src/app/services/api/api.service.ts +++ b/packages/insight/src/app/services/api/api.service.ts @@ -1,13 +1,14 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { retryBackoff } from 'backoff-rxjs'; -import { BehaviorSubject, interval } from 'rxjs'; +import { BehaviorSubject, timer } from 'rxjs'; import { finalize, switchMap } from 'rxjs/operators'; import { IBlock, StreamingFindOptions } from '../../types/bitcore-node'; import { Chain } from '../../types/configuration'; import { ConfigService } from '../config/config.service'; -const streamPollingInterval = interval(15 * 1000); +// TODO: expose this setting as a BehaviorSubject, tone it down when the window isn't active? +const streamPollingPeriod = timer(0, 15 * 1000); /** * Convert any object into an object with only elements of type `string`. @@ -27,11 +28,13 @@ const stringifyForQuery = (object: any) => export class ApiService { constructor(private config: ConfigService, private http: HttpClient) {} + private _bitcoreAvailable = new BehaviorSubject(true); + // TODO: service down notification (if NetworkService is up but the service is unreachable) /** * Emits when availability of the service appears to change, e.g. when an API * call doesn't return, or when a retry is successful. */ - serviceAvailable = new BehaviorSubject(true); + public bitcoreAvailable = this._bitcoreAvailable.asObservable(); /** * Everything before the API route itself, without the trailing `/`. @@ -47,20 +50,25 @@ export class ApiService { }); streamBlocks = (chain: Chain, params: StreamingFindOptions) => - streamPollingInterval.pipe( + streamPollingPeriod.pipe( switchMap(() => this.getBlocks(chain, params)), retryBackoff({ initialInterval: 5 * 1000, shouldRetry: error => { // TODO: retry only if ERR_CONNECTION_REFUSED or 5xx error code if (!!error) { - this.serviceAvailable.next(false); + this._bitcoreAvailable.next(false); return true; } } }), finalize(() => { - this.serviceAvailable.next(true); + if (this._bitcoreAvailable.getValue() !== true) { + this._bitcoreAvailable.next(true); + } }) ); + + // TODO: rates API: + // consumers subscribe to an observable – only if a consumer is listening, poll this.config.ratesApi for the rates object. If everyone unsubscribes, polling should stop, and the last rates object thrown away (we don't want to use old rates on a resubscription). If someone subscribes again, start from scratch. } diff --git a/packages/insight/src/app/services/config/config.service.ts b/packages/insight/src/app/services/config/config.service.ts index 8b1e7ab3af9..7838d7bd47e 100644 --- a/packages/insight/src/app/services/config/config.service.ts +++ b/packages/insight/src/app/services/config/config.service.ts @@ -15,6 +15,7 @@ export class ConfigService { environment.expectedNetworks ); public apiPrefix = new BehaviorSubject(environment.apiPrefix); + public ratesApi = new BehaviorSubject(environment.ratesApi); constructor(private logger: NGXLogger) { this.logger.debug( `ConfigService initialized in ${ diff --git a/packages/insight/src/app/services/network/network.service.spec.ts b/packages/insight/src/app/services/network/network.service.spec.ts new file mode 100644 index 00000000000..097db559e86 --- /dev/null +++ b/packages/insight/src/app/services/network/network.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { NetworkService } from './network.service'; + +describe('NetworkService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: NetworkService = TestBed.get(NetworkService); + expect(service).toBeTruthy(); + }); +}); diff --git a/packages/insight/src/app/services/network/network.service.ts b/packages/insight/src/app/services/network/network.service.ts new file mode 100644 index 00000000000..3904f0d1348 --- /dev/null +++ b/packages/insight/src/app/services/network/network.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, fromEvent } from 'rxjs'; + +// TODO: network connected/disconnected notification +@Injectable({ + providedIn: 'root' +}) +export class NetworkService { + private _isOnline = new BehaviorSubject(navigator.onLine); + public isOnline = this._isOnline.asObservable(); + + constructor() { + const connect = fromEvent(window, 'online'); + const disconnect = fromEvent(window, 'offline'); + + connect.subscribe(() => this._isOnline.next(true)); + disconnect.subscribe(() => this._isOnline.next(false)); + } +} diff --git a/packages/insight/src/app/shared/currency-value/currency-value.component.html b/packages/insight/src/app/shared/currency-value/currency-value.component.html new file mode 100644 index 00000000000..4a9917e8051 --- /dev/null +++ b/packages/insight/src/app/shared/currency-value/currency-value.component.html @@ -0,0 +1,3 @@ + + currency-value: inAtomicUnits: {{inAtomicUnits}}, ticker: {{ticker}}, displayAs: {{displayAs}} + diff --git a/packages/insight/src/app/shared/currency-value/currency-value.component.scss b/packages/insight/src/app/shared/currency-value/currency-value.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/insight/src/app/shared/currency-value/currency-value.component.spec.ts b/packages/insight/src/app/shared/currency-value/currency-value.component.spec.ts new file mode 100644 index 00000000000..84c278bcfc9 --- /dev/null +++ b/packages/insight/src/app/shared/currency-value/currency-value.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CurrencyValueComponent } from './currency-value.component'; + +describe('CurrencyUnitsComponent', () => { + let component: CurrencyValueComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [CurrencyValueComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CurrencyValueComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/packages/insight/src/app/shared/currency-value/currency-value.component.ts b/packages/insight/src/app/shared/currency-value/currency-value.component.ts new file mode 100644 index 00000000000..ce677756708 --- /dev/null +++ b/packages/insight/src/app/shared/currency-value/currency-value.component.ts @@ -0,0 +1,35 @@ +import { Component, Input, OnChanges } from '@angular/core'; +import { ApiService } from '../../services/api/api.service'; + +@Component({ + selector: 'app-currency-value', + templateUrl: './currency-value.component.html', + styleUrls: ['./currency-value.component.scss'] +}) +export class CurrencyValueComponent implements OnChanges { + /** + * The currency value in the smallest unit accounted for by Insight. (E.g. + * for BCH, the atomic unit is satoshis.) + */ + @Input() + inAtomicUnits: number; + /** + * The currency code/ticker symbol of the value. (E.g. when displaying BCH + * values, this should always be `BCH`, even if values will be displayed in an + * alternative currency.) + */ + @Input() + ticker: string; + /** + * Either the alternative ticker/currency code, or the unit (e.g. satoshis, + * bits, BCH) in which to display the value. + * + * If an alternative ticker is provided, the value will be estimated using + * market exchange rates. + */ + @Input() + displayAs: string; + + constructor(private apiService: ApiService) {} + ngOnChanges() {} +} diff --git a/packages/insight/src/app/shared/date-time/date-time.component.html b/packages/insight/src/app/shared/date-time/date-time.component.html new file mode 100644 index 00000000000..37155aab13a --- /dev/null +++ b/packages/insight/src/app/shared/date-time/date-time.component.html @@ -0,0 +1,4 @@ + + {{value | date:'shortTime'}} + {{value | date:'short'}} + diff --git a/packages/insight/src/app/shared/date-time/date-time.component.scss b/packages/insight/src/app/shared/date-time/date-time.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/insight/src/app/shared/date-time/date-time.component.spec.ts b/packages/insight/src/app/shared/date-time/date-time.component.spec.ts new file mode 100644 index 00000000000..ef377ce7fdb --- /dev/null +++ b/packages/insight/src/app/shared/date-time/date-time.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DateTimeComponent } from './date-time.component'; + +describe('DateTimeComponent', () => { + let component: DateTimeComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DateTimeComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DateTimeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/packages/insight/src/app/shared/date-time/date-time.component.ts b/packages/insight/src/app/shared/date-time/date-time.component.ts new file mode 100644 index 00000000000..431f48c703c --- /dev/null +++ b/packages/insight/src/app/shared/date-time/date-time.component.ts @@ -0,0 +1,17 @@ +import { Component, Input, OnChanges } from '@angular/core'; + +@Component({ + selector: 'app-date-time', + templateUrl: './date-time.component.html', + styleUrls: ['./date-time.component.scss'] +}) +export class DateTimeComponent implements OnChanges { + @Input() + value: any; + inThePastDay: boolean; + + ngOnChanges() { + const twentyFourHoursBeforeNow = new Date(Date.now() - 1000 * 60 * 60 * 24); + this.inThePastDay = twentyFourHoursBeforeNow < new Date(this.value); + } +} diff --git a/packages/insight/src/app/shared/shared.module.spec.ts b/packages/insight/src/app/shared/shared.module.spec.ts new file mode 100644 index 00000000000..3ecb62606e2 --- /dev/null +++ b/packages/insight/src/app/shared/shared.module.spec.ts @@ -0,0 +1,13 @@ +import { SharedModule } from './shared.module'; + +describe('SharedModule', () => { + let sharedModule: SharedModule; + + beforeEach(() => { + sharedModule = new SharedModule(); + }); + + it('should create an instance', () => { + expect(sharedModule).toBeTruthy(); + }); +}); diff --git a/packages/insight/src/app/shared/shared.module.ts b/packages/insight/src/app/shared/shared.module.ts new file mode 100644 index 00000000000..c369cc9a5cf --- /dev/null +++ b/packages/insight/src/app/shared/shared.module.ts @@ -0,0 +1,12 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; + +import { CurrencyValueComponent } from './currency-value/currency-value.component'; +import { DateTimeComponent } from './date-time/date-time.component'; + +@NgModule({ + imports: [CommonModule], + declarations: [DateTimeComponent, CurrencyValueComponent], + exports: [DateTimeComponent, CurrencyValueComponent] +}) +export class SharedModule {} diff --git a/packages/insight/src/app/types/bitcore-node.ts b/packages/insight/src/app/types/bitcore-node.ts index 1c0d31f3711..2b5cc9871bb 100644 --- a/packages/insight/src/app/types/bitcore-node.ts +++ b/packages/insight/src/app/types/bitcore-node.ts @@ -1,6 +1,4 @@ // TODO: a separate package of well-documented types shared between projects -export { IBlock } from '../../../../bitcore-node/src/models/block'; -export { - StreamingFindOptions -} from '../../../../bitcore-node/src/services/storage'; +export { IBlock } from '../../../../bitcore-node/src/types/Block'; +export { StreamingFindOptions } from '../../../../bitcore-node/src/types/Query'; diff --git a/packages/insight/src/assets/icon/favicon.png b/packages/insight/src/assets/icon/favicon.png index 51888a7bbdb59f04c29c548523eb2638c1c954f5..40eea5da85559cdd22de18aae730532915d0e398 100644 GIT binary patch delta 1275 zcmVBL7@>+!CQVwJW@pFWgcOI#U2?Oxdw(46EL?8p+xdRq_x@aNA&aS_sIewPMOlo=~RFJ;CX-?g@U`jQr)u@ z((MTF=&4iBe}C$Ep141XvC$oHum*zq?#m3nc(Di(u>__lDy4jF_0NdzwdbS3M(o`(0}_2$0unv_2!GyYfX#q}GhmlM!V|z=1nm;A zO8`5TC`h1`YB)sehqEFC33N?tl_S9hOn@aouImO&``A@2_4fANQ1u8&wTvtAC|&Cx zAweYAGIp9ukyp@IP@Okv%c2NAuU0Cb-%<5ywepX^m4%j8j#(jscXfcNsmd?F{zR9X z^~s;dAQFkCQpavLE_gF=|CQ`nm8 z>vOM{N+ndD<2zy{;J|_54^Ve|6YhtygS*Q$A%EN>mo9z#S6mdt_OEa*cj5^I|5n)G zWxy!6?;&LQow$ORxUmu7ILnVk<2czH4NiTO*a*PAHdWuGsZ>OSQ{QfE1k^Xzc~VP2 z=gmobJ+c|lUcNien$3XDo0Il>WHX?>e0QETn*p6SC++npwhTa3_+AvDpe!BzicqpcxY?{RIBCR;LVHp{A27D6jsaa{!cyWx$w$m34{tdLQSUGeS%sjX6G^u+G$K7pCf2 zW~}IPe)Sk`26!|^jjjQ}olmDT-#2>fR(~uOX|*~tjMCdKQ*8LU(ADLRPfUDsgXD6< zFW{Q{B`#N??+9?>(=at{{rm8RM>B9uLJ-9002ovPDHLkV1nHqeRco< literal 930 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!x&nMcT-^(N+`Ik{1{5}NM-k9f zx+Ot=!3-}y{#<)Uok@}X^0T)Mjw~(h@l#Ir^NC7r=_#0-8E$a3RKVL~nc3p~*UxA# z4bjg_XJBBO<>}%W5^;EK_{&+V40u?Vtzk_Oy;*YMVqw~ozwz&rPG6d~>D#mR`SK=; zww9K@s-5|}%Dy=+ak|}>#qwwKGH>ZO4~|`P_r2LJy1RYqyXB`=?Mw?@F8Z!h{rcpz z^y94Mdt1KFIr?nR|NE2g?N}2vO^d(4;=OB>WP0Plul!REN;cgwUAJuA?C`^98NGCJ zuf=eS_OHmETd)0azdLJ6Z^FIm6*YNHw|4%lGf`stp1;dK$1sNYdj7ec{*dYEN(RM7|LFZWoy#R9733Kz`kee` zcsH&1+JCz2|7qRT%bgpNUdZ3E%i6T)I?vY6{p)ga_qlB<;GAPn(!{OF;x_wm_K%n( zW67=BbK}Hx+Mo2yW_)0K?|;#s9f!Uw=S;9NQ)S^>UOs1+lefL)nQKa6oJzg9f7^uT zr^QQJHL|;hJ!n29P`>r{pI$??E8ll+Vp%D3sqgGAQ?16Ir>}307i!n%dGB~BKleRm zV`}#;lUe7&`Rm%PqCLs%HOEe${KnWk%jlG>GM}cUf9>%Jt~^m(q1U%2$4$<)xHgTq zd0EM;Wjbazr!IcCcJ{`1i$kvS+;+RTmT^vJdf^@OKlARE7bo`3-BEDmtJB75>uhqr zB>Z2>{qec~lUw13i#jzoi~HY)56&*(X~?N15zPW2p#@B#eDa>R6ga z5@jjIzMRBxV#d-KF&HGvD6ezg4iT0X1^@tJX>lHR zK;l0mBzUktqVatP;3MG7O@Z1$#5VvqQek=C%p2`LCt_xU+T$Siu(c1+| zkBJJ1m3JCT`&7KX`lcB*_qJ27bQl%hJILuT3>H&GG4;2K2rbm`H`K{s@{fIo%Wrq% z`abC)D;-_3C1nj)=j~`N^0ZZ2fyUZ){MOod2+~V^Olw5vq8l+9PICb!?>$~p$xMyJ zHLhe;QZ%isPkwQAbK4I!8ti=n8NoeocW&1gc1Mxw1jyvyz|dT6Zm8AceH z)V}TYcPW=MMRuJ!&a6a z$+k8{ZI<13Me$P^iqSSJ8{@(EO6ec?nG1X5;?-F~f)?`ygVC;m^xgTrTZbB*k>527 zEiWqW@kL{_?-yHyjmt<&pMpTHHE+1>;V0ktQRj>D@+y;$&gjo?kd4jiVM~kB&(aLT zr#{{`+LX{@#(5|}RqXp;NUDuF^CK2y;wt05y<>)^Y*$2|jW=>}Ri zkEmsCYuW63&s(hFl%6VjZaiJ@;1Hy1+JvVEWYCw#C#t1h_jN{J_=0HG4ngF603}+p zSjWFKeq8%|Y0fNGwylX(cTN}zR(Qge)UCEqFA?Ngo!De6f~vfH6we`v_HvtvzQAUn zy03+xXm5F32Dl_H{Vuo%Fpc~S&E)%BN6AoxV^m4ICP?D0t`t%UB*bzswh**$rJ3II z1xn8>9_|4a=`P(FV1+F6p9*lyPiYZ&CSTHemAulw2?4iNgv|+?_uxg{&_Qe7<|53p z<$RC~x`Ip0zf^R;0~aG;FgKIba8cXoYU*Jb*C0ZWN-sX4mKw~uM~%5FlRI=1gm?m4 zjgu1QS*tg26Q;0NZ%&`=ccp2$d^-9&>EmP}GLpf1X*;EL!cB3{j$vc;3p@UNYBW&kl2JV&X?)zk5nYi}>)a2ig4%f#?%}jGPWUD;F%Q1zyGr zMNKYScFD+l@7nJ{QqF$~m}8}#^>TNA$2asmo5Fq{1@6DB{<98FOLQyjPia7EPXE2! zNOM3TR0!j&vawZ3Nt@^*cKV9bg)QMZlCfam%d^qifLY}5GALbl_+&RnFRg3{-(f9I zhA!X8p0$}lE?gFztlbhiL^J6Qa~=6U=o_-XCEng&XOf!F!;ZK=ymPuk9va1eZQ)qQ zU|!{;g~h+=1KPtT!WoAufZs6xg&CapcQbqYYvo&iT+<8G81yHr=qIT6Avy}{N0gO0 z=cA)}Oq0Qs%FquWYYI>R1-G;f_JbiDcvVjiHMV zPE!M5t?9F^$?Y4Rc;vh%D!_K44yIWI<T!UXoo`MzlM+q zyPP?Z?ft_-)A-Sy!}X5U*PiFz;ToJ1vjBO2d$X*%g1wjDM^mY4`7oDcqW~f5dtEHd zxQ&9fN^T32#9QKZh~f!ZYEDG3!kGDvDjc&TmU<+UuVYtLUKyn7bw&F?TNim&xi7%7 zRzG#1_)>XH7PurPrIWu0xDuO1%H+$^q~soGTTNPbKoZxLrNos$$(s&nD?$5~jw=bi z00rG^-UIaJxL^+y|F>#3}5ls zGHu|F=NM_CnuvG-`E2u^D3-{;xc+7<8BrBs{NRoSUBsz8^rs<|J+N~ro+FBMx=W+c zJ`mWy(iWR|%U7@j)h8D9`~0A&iA|MEiOmor%=a;v%KZPlFgp_to}78(Q<-;t$SU zu@rBUZ*HhCjs>dI!{v11@A~Nmds1YsLo* zgTD{g2Rcv-O#?Ct>~G%1(lijMYi$YbL=q#s$MZ4o%#TF81X`N3dN$yaGo9&^H)evxW${uF=@BG9kKU4vuum&5 z9RDoJ&9jBdU04g4U^O9%Qyi()&)6T#ksDl1HfL4dC~a0Vt0!DLpsCU?XRxq2?ipIg z_x&NvF_;Pg`F-AnW9fFX68F=tbem8J<~W_MkLK4b-k!(t%HQZalJ+`(kXHhLE>LA; zh{nF^D2!N(Yv8(Wvtz@)Q?Bd?Q7iqBuOi09P=7FJ6Jp2P^4QB2vlG~htrrX8^-uSE z<*s>RaOe86^JTZ(X_N-?U>$+ick*4aOu;wxU^J_^NlsYfXQCT2h~UfX%k*Hq2C!Z^&1^{ z!8_5!MlVtPGu&z#{6a&G^P?QDh<|m7ze)V4a-uo?XHvcl&Tg92BA`?ugXk80pHdKT zPzk7EbQQ51#R}{yk0RKupi8lHGNi55HmnB=QAMeZ-n4A#x>2Y-E_1o0PYt~lv?6y$SPIfda#4e=awIs^}GE8g|N!T_vH0hZa(&SK66eNb9`M1bb-j(Znwh Y$Ti@)l(r1`pu7Q=7i`bhntCVv2US+KjQ{`u literal 0 HcmV?d00001 diff --git a/packages/insight/src/assets/icon/favicon@4x.png b/packages/insight/src/assets/icon/favicon@4x.png new file mode 100644 index 0000000000000000000000000000000000000000..eef589b4de6e57777f6b974f94c8c6f55249a82b GIT binary patch literal 5696 zcmeI0`8O2)zsF~geN7}wl$0%NQB)$bZ)4wO?EAirW$Z1o#xRML5eXS&n?abtOd*kM zGuhK%FwDq$eeV7K1^3*0?z!iDe|o>(=lwdb^E$8de!rgY$Looii7p4LAS(a>;Lz98 z1_1!TQxgbaW;|USL#sUh*@c00wE%TP!m9uP-@d-Kre!p6{S_qGaXh}C+}4rDjx;{( zHf4_NH=Yr^W2W{(LEOiKM;)t&1bkx@)PiRT=}EmxF25r2O|&LDUnQ3 zr$L6%kd?aahcP!)>;0u*DV()vMg*$Op5*g3ym>nSVL3_Jxj1a0ekJL7apnZt$Ktr4 zDc=rR5f<2XCpdzeLB@TJSP<>0@7V0qpSu{{vkzTtG7N?MpdCe&MjgLR)3T8oRMh<* z=;!ycDX6RyMb36gZmp0=*Y2W17U-FDaXPOKJuNKy znUY^0dPw~geKoU8B}#9`?>ZrT$&%;VF#>tO~!ZZuP&P?CKse>9qZ~Z&HntU=F&vz`8!jA{rJEpz5%hswQ(y31kXBZoum@w zg)}>S0$%R)F{=#t{({fd023vXf)`*Ozb9&97P##6j*(Yrv0-gl+A2c`Lv_!AZGUB< z6S4K_SV#-u<_)I&=%J-{q~=Ih=alNP=9GQ?t+>6Bw>8$a?=QIB^#;r#HD)MW=)uO) zD;b)95r!I|pgf&c_H~kshU~D{%duuVL))2|pFbl5K(`q_XKToVW%Uh+Do&5SeGHf` z<*igd4WXFWO#W*sR{@0+iHVzq`9x7X(b!m~o|8?1pX0oSOc?oDZEa0v*<<|D6#wHD z!rJ9V#pUn^VPT{1%}@mAe7s`7uRO1*qxXy`#;Vdk(z%mo^47xK13|L)Wqm<z}B1j>m)ZBY+;ZOVOEH_wRodL9D(YP~&fPsVg`^COvFS z6KVIh9G2AN#Uq|+yotZ7TnrA2EBt4D-3EPO(o7RZ34VN3dOZ1TcT$odNA-Pyl2+ zGxX(td>IPV=6NGR$I06b5SEjx))adRVC2?$9Lapk2tXg)+FHSwBuOW5|J$od4ddYm|X$pss9f2e_x}`0#J}z39-n+0;hh%Gk1ZmGMqilB1Wl`ECjBw#1>gE zYTwXG z?a~QqVYz<(W*l+t&f3PtJ?WCt;M{^ICGH*acXlmlKt-sJpNHG=Xlhl8AQvAi*D+4p zxyKgY1u^n&tGtxo&{bKvAyi2@JKJIG*Z$@jB_!v$i)37fX;#<0bpyBMZ8P1aiZNpG zcM57S-z!n4`=qC$lFdFeddF%~=x8Qf4&!E8U(^k*j1U=_M#WI}OBJ#|*8`tnc8LXp z#{o=dl?#)}r0nyoBxmKTl3I7~`ek{qyGZ1Nj9~pCbbk&SJ;!H@&pqGYXPJ0L&8pZ5 z_+B6~vH^#)OmJf2=hdJWlzmUM1Wy90%I(Y2l7wjoszHPqitAbSPyTka4{KX}414=l z0*Ar#eeImLokjJMBdXd$z_b&o0w*~z8a^iQ={GLR>)n@id@Q6RiWBo8-8psG-qRBd zKByhtQr?c8-~8ko9UqssS0>3nA(5~x2_xN7WyTTD4kg-h_-Cw0QVfTvH4qYMxGiD< z+i{T5BC*?!K!B$jz)LrK4xUzEtK6L)9T)w$nb_%p7Y|bBIZ~k)FnEnX1T1$$_hk^t zoNXq*Xz7HgEjyKcq2qc~F<=GP_7j3O;E%prPWdj{hj_Z@6A zuDzV_QXQ*gRMcoxo#Huy&=eVhK+( zo5?kk+wDh_{vQtpuTl8ILQK58ygZ5bf�@RCVhFKWHpvO7GEYzRnt`Y?oo?C*Oz( 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 literal 0 HcmV?d00001 diff --git a/packages/insight/src/assets/icon/logo.svg b/packages/insight/src/assets/icon/logo.svg new file mode 100644 index 00000000000..ad6c2dc08c2 --- /dev/null +++ b/packages/insight/src/assets/icon/logo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/packages/insight/src/environments/environment.prod.ts b/packages/insight/src/environments/environment.prod.ts index 701788d7e8f..6a50e731d00 100644 --- a/packages/insight/src/environments/environment.prod.ts +++ b/packages/insight/src/environments/environment.prod.ts @@ -30,6 +30,7 @@ const expectedNetworks: Chain[] = [ export const environment = { apiPrefix: 'https://api.bitcore.io/api', + ratesApi: 'https://bitpay.com/api/rates/bch', production: true, loggingSettings, initialNetwork, diff --git a/packages/insight/src/index.html b/packages/insight/src/index.html index fe55652acc6..789029cacdc 100644 --- a/packages/insight/src/index.html +++ b/packages/insight/src/index.html @@ -1,23 +1,41 @@ - - - Ionic App + + + Insight Block Explorer - + - - - + + + - + + + + - - - + + + - - - - + + + + diff --git a/packages/insight/src/theme/variables.scss b/packages/insight/src/theme/variables.scss index 4b39b39fd68..24f825340ba 100644 --- a/packages/insight/src/theme/variables.scss +++ b/packages/insight/src/theme/variables.scss @@ -3,21 +3,38 @@ /** Ionic CSS Variables **/ :root { + --ion-font-family: 'Roboto', 'Helvetica Neue', sans-serif; + --ion-text-color: #3d3d4d; + --ion-background-color: #f8f8f9; + /** primary **/ - --ion-color-primary: #3880ff; - --ion-color-primary-rgb: 56, 128, 255; + // --ion-color-primary: #3880ff; + // --ion-color-primary-rgb: 56, 128, 255; + // --ion-color-primary-contrast: #ffffff; + // --ion-color-primary-contrast-rgb: 255, 255, 255; + // --ion-color-primary-shade: #3171e0; + // --ion-color-primary-tint: #4c8dff; + + --ion-color-primary: #2a3387; + --ion-color-primary-rgb: 61, 210, 240; --ion-color-primary-contrast: #ffffff; --ion-color-primary-contrast-rgb: 255, 255, 255; - --ion-color-primary-shade: #3171e0; - --ion-color-primary-tint: #4c8dff; + --ion-color-primary-shade: darken(--ion-color-primary, 10%); + --ion-color-primary-tint: lighten(--ion-color-primary, 10%); /** secondary **/ - --ion-color-secondary: #0cd1e8; - --ion-color-secondary-rgb: 12, 209, 232; + // --ion-color-secondary: #0cd1e8; + // --ion-color-secondary-rgb: 12, 209, 232; + // --ion-color-secondary-contrast: #ffffff; + // --ion-color-secondary-contrast-rgb: 255, 255, 255; + // --ion-color-secondary-shade: #0bb8cc; + // --ion-color-secondary-tint: #24d6ea; + --ion-color-secondary: #3dd2f0; + --ion-color-secondary-rgb: 61, 210, 240; --ion-color-secondary-contrast: #ffffff; --ion-color-secondary-contrast-rgb: 255, 255, 255; - --ion-color-secondary-shade: #0bb8cc; - --ion-color-secondary-tint: #24d6ea; + --ion-color-secondary-shade: #02a1cb; + --ion-color-secondary-tint: #57e8fd; /** tertiary **/ --ion-color-tertiary: #7044ff;