Skip to content

Commit

Permalink
Add support for multi-multi-explorer
Browse files Browse the repository at this point in the history
- Makes it possible to run the multi-chain explorer in different modes.
- Remove query against status when navigating back to home.
  • Loading branch information
sondreb committed Feb 3, 2022
1 parent 516c173 commit 8112b23
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 65 deletions.
6 changes: 1 addition & 5 deletions src/Blockcore.Explorer/ClientApp/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Component, OnInit, Renderer2 } from '@angular/core';
import { ApiService } from './services/api.service';
import { SetupService } from './services/setup.service';
import { Router, ActivatedRoute, NavigationEnd, ResolveEnd, NavigationStart } from '@angular/router';
import { filter } from 'rxjs/operators';
import { Router, ActivatedRoute } from '@angular/router';
import { ThemeService } from './services/theme.service';

@Component({
Expand All @@ -21,9 +20,6 @@ export class AppComponent implements OnInit {
private activatedRoute: ActivatedRoute) {

this.theme.init(renderer);

// Initial loading.
this.setup.getChains();
}

async ngOnInit() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ export class ExplorerComponent implements OnInit, OnDestroy {

constructor(private api: ApiService, public setup: SetupService) {
this.subscription = this.setup.currentChain$.subscribe(async (chain) => {
await this.updateInfo();
await this.updateBlocks();
if (!this.setup.isCurrentRootChain) {
await this.updateInfo();
await this.updateBlocks();
}
});
}

Expand All @@ -41,6 +43,10 @@ export class ExplorerComponent implements OnInit, OnDestroy {
}

async updateBlocks() {
if (this.setup.isCurrentRootChain) {
return;
}

try {
const list = await this.api.getBlocks(0, 5);

Expand Down Expand Up @@ -69,6 +75,10 @@ export class ExplorerComponent implements OnInit, OnDestroy {
}

async updateInfo() {
if (this.setup.isCurrentRootChain) {
return;
}

try {
this.info = await this.api.getInfo();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<header class="header dropdown">
<a class="header-logo" [routerLink]="['/']"><img class="header-image" [ngClass]="{'mode-aware': setup.Chain?.IconFilter}" [src]="setup.Chain?.Icon"></a>
<a class="header-title" [routerLink]="['/']">{{setup.Chain?.Name}}<br>Explorer</a>
<header class="header dropdown" *ngIf="chain">
<a class="header-logo" [routerLink]="['/']"><img class="header-image" [ngClass]="{'mode-aware': chain.IconFilter}" [src]="chain.Icon"></a>
<a class="header-title" [routerLink]="['/']">{{chain.Name}}<br>Explorer</a>

<div *ngIf="setup.multiChain" class="dropdown-content chains">
<ul>
<li class="chain-item" *ngFor="let chain of setup.chains">
<a class="chain-link" [routerLink]="[chain.symbol.toLowerCase()]"><img [ngClass]="{'mode-aware': chain.filter}" [src]="chain.icon"></a>
<a class="chain-link" [routerLink]="[chain.symbol.toLowerCase()]">{{chain.name}}</a>
<li class="chain-item" *ngFor="let c of setup.chains">
<a class="chain-link" [routerLink]="[c.symbol.toLowerCase()]"><img [ngClass]="{'mode-aware': c.filter}" [src]="c.icon"></a>
<a class="chain-link" [routerLink]="[c.symbol.toLowerCase()]">{{c.name}}</a>
</li>
</ul>
</div>
</header>

<nav class="menu">
<ul *ngIf="setup.current != 'BLOCKCORE'">
<nav class="menu" *ngIf="chain">
<ul *ngIf="!setup.isCurrentRootChain">
<li class="nav-item" *ngIf="setup.featureEnabled(setup.Explorer?.Features?.Ticker)">
<a class="nav-link" [routerLinkActive]="['link-active']" [routerLinkActiveOptions]="{ exact: true }"
[routerLink]="[setup.current]">Ticker</a>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
import { Component, HostBinding } from '@angular/core';
import { ChangeDetectorRef, Component, HostBinding, OnInit } from '@angular/core';
import { SetupService } from '../services/setup.service';

@Component({
selector: 'app-nav-menu',
templateUrl: './nav-menu.component.html'
})
export class NavMenuComponent {
export class NavMenuComponent implements OnInit {
@HostBinding('attr.ngNoHost') noHost = '';

showList: boolean;
chain: any;

constructor(public setup: SetupService) {
constructor(private setup: SetupService, private changeDetectorRef: ChangeDetectorRef) {

}

ngOnInit(): void {
this.setup.currentChain$.subscribe(() => {
console.log('CURRENT CHAIN CHANGED IN MENU!!', this.setup.current);
// this.changeDetectorRef.detectChanges();

if (this.setup.initialized) {
console.log('INITIALIZED, UPDATE UI!');
this.chain = this.setup.Chain;
console.log(this.chain);
} else {
console.log('NOT INITIALIZED YET....');
}

console.log('WHOPS');
});
}

onMouseOver() {
this.showList = true;
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ export class ApiService {
}

async loadSetup(chain: string) {

console.log('LOAD SETUP', chain);

let setup = null;

if (environment.local) {
Expand All @@ -89,8 +92,8 @@ export class ApiService {
return setup;
}

async loadSetups() {
return this.download('https://chains.blockcore.net/CHAINS.json');
async loadSetups(chain: string) {
return this.download(`https://chains.blockcore.net/CHAINS-${chain}.json`);
}

async getInfo() {
Expand Down
36 changes: 17 additions & 19 deletions src/Blockcore.Explorer/ClientApp/src/app/services/setup.service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { EventEmitter, Injectable } from '@angular/core';
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ApiService } from './api.service';
import { Observable, BehaviorSubject, Subject } from 'rxjs';
import { Router, NavigationStart } from '@angular/router';
import { filter } from 'rxjs/operators';
import { environment } from 'src/environments/environment';
import { BehaviorSubject } from 'rxjs';
import { Router } from '@angular/router';
import { environment } from '../../environments/environment';
import * as satcomma from 'satcomma';


@Injectable({
providedIn: 'root'
})
Expand All @@ -23,9 +21,18 @@ export class SetupService {
multiChain: boolean;
initialized = false;

private readonly currentChainSubjectBehavior = new BehaviorSubject<string>('BLOCKCORE');
readonly currentChain$ = this.currentChainSubjectBehavior.asObservable();

// Default to bitcoin as that is most user friendly for coins other than Bitcoin.
format: string = 'bitcoin'; // sat, satcommas, bitcoin

apiChain: string;

get isCurrentRootChain(): boolean {
return this.current == 'BLOCKCORE' || this.current == 'COINVAULT';
}

toggleFormat() {
if (this.format == 'sat') {
this.format = 'satcommas';
Expand All @@ -50,15 +57,6 @@ export class SetupService {
}
}

// Both SubjectBehavior and Behavior, depending on consumer.
// The "currentChainSubject$" will return current value as soon as subscribed.
private readonly currentChainSubjectBehavior = new BehaviorSubject<string>('BLOCKCORE');
// private readonly currentChainSubject = new Subject<string>();

// readonly currentChainSubjectBehavior$ = this.currentChainSubjectBehavior.asObservable();
// readonly currentChainBehavior$ = this.currentChainBehavior.asObservable();
readonly currentChain$ = this.currentChainSubjectBehavior.asObservable();

get current(): string {
return this.currentChainSubjectBehavior.getValue();
}
Expand All @@ -75,22 +73,22 @@ export class SetupService {

}

async getChains() {
async getChains(chain: string) {
if (environment.local) {
console.log(`Environment is local, don't get setup configuration from server.`);
return;
}

const data = await this.api.loadSetups();
const data = await this.api.loadSetups(chain);
this.chains = data;
}

// Important that this is async and we wait for continued processing,
// as we must have the chain setup as early as possible.
async setChain(chain: string) {
if (chain !== 'BLOCKCORE' && this.current === chain) {
if (this.current === chain) {
// Update the chain subject, which should trigger consumers to do some processing.
this.current = chain;

return;
}

Expand Down
67 changes: 41 additions & 26 deletions src/Blockcore.Explorer/ClientApp/src/app/shared/loading.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,48 +19,63 @@ export class LoadingResolverService implements Resolve<any> {
) { }
async resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Observable<any> | Observable<never>> {

// When resolving the route, just get the latest always (defaults to 'BLOCKCORE').
let explorerChain = this.setup.current;
this.setup.multiChain = true;

// If local is set to true, then we'll default to single chain and also not run normal initialization where the API is queried.
if (environment.local) {
this.setup.multiChain = false;
this.setup.initialized = true;
explorerChain = 'local'; // Used in the URLs, so make sure it is lowercase.
}
let wasInitilized = this.setup.initialized;

// If not initialized yet, perform some operations:
if (!this.setup.initialized) {
try {
this.setup.initialized = true;
console.log('Initilization of Explorer');

// Fetch the configure explorer chain to see if we should run in single or multi-chain mode.
try {
// First make a request to the local API to check what chain instance it is runnign.
const explorerChainRequest = await this.api.request('/api/explorer/chain');

if (explorerChainRequest.status === 200) {
explorerChain = await explorerChainRequest.text();
this.setup.multiChain = (explorerChain === 'BLOCKCORE');
}

console.log('API Chain:', explorerChain);
this.setup.apiChain = explorerChain;

this.setup.multiChain = (explorerChain === 'BLOCKCORE' || explorerChain === 'COINVAULT');
} else { // If it fails... fallback
this.setup.multiChain = true;
this.setup.apiChain = 'BLOCKCORE';
}
} catch {
this.setup.multiChain = true;
this.setup.apiChain = 'BLOCKCORE';
}
}

if (this.setup.multiChain) {
// TODO: Figure out a better way to get path fragments pre-parsed into an array.
const fragments = state.url.split('/');
let chain = fragments[1];

if (!chain) {
chain = 'BLOCKCORE';
if (this.setup.multiChain) {
console.log('GO GET', explorerChain);
await this.setup.getChains(explorerChain);
}

// If the chain has changed, load again.
if (chain === 'BLOCKCORE' || this.setup.current !== chain) {
return this.setup.setChain(chain);
// If local is set to true, then we'll default to single chain and also not run normal initialization where the API is queried.
if (environment.local) {
this.setup.multiChain = false;
this.setup.initialized = true;
explorerChain = 'local'; // Used in the URLs, so make sure it is lowercase.
}

this.setup.initialized = true;
}

console.log('Explorer Chain:', explorerChain);

// TODO: Figure out a better way to get path fragments pre-parsed into an array.
const fragments = state.url.split('/');
let chain = fragments[1];

if (chain != '') {
return this.setup.setChain(chain);
} else {
// If the chain has changed, load again.
if (this.setup.current !== explorerChain) {
// We should reset to multichain configuration if user navigate back to home.
// If already initilized and no chain in URL; we'll reset back to root.
if (wasInitilized) {
return this.setup.setChain(this.setup.apiChain);
} else {
return this.setup.setChain(explorerChain);
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/Blockcore.Explorer/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"COINVAULT (MULTI)": {
"commandName": "Project",
"commandLineArgs": "--chain=COINVAULT",
"launchBrowser": true,
"applicationUrl": "http://localhost:9911",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"CITY": {
"commandName": "Project",
"commandLineArgs": "--chain=CITY",
Expand Down

0 comments on commit 8112b23

Please sign in to comment.