Adding support for optional frontend config.
Dropdown network selector is hidden by default, and enabled using config. fixes #79
This commit is contained in:
parent
7e9ba6b983
commit
0a264a7078
@ -200,6 +200,13 @@ Build the frontend static HTML/CSS/JS, rsync the output into nginx folder:
|
|||||||
sudo rsync -av --delete dist/mempool/ /var/www/html/
|
sudo rsync -av --delete dist/mempool/ /var/www/html/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Optional frontend configuration
|
||||||
|
In the `frontend` folder, make a copy of the sample config and modify it to fit your settings.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp mempool-frontend-config.sample.json mempool-frontend-config.json
|
||||||
|
```
|
||||||
|
|
||||||
## Try It Out
|
## Try It Out
|
||||||
|
|
||||||
If everything went okay you should see the beautiful mempool :grin:
|
If everything went okay you should see the beautiful mempool :grin:
|
||||||
|
4
frontend/.gitignore
vendored
4
frontend/.gitignore
vendored
@ -48,3 +48,7 @@ Thumbs.db
|
|||||||
src/resources/assets.json
|
src/resources/assets.json
|
||||||
src/resources/assets.minimal.json
|
src/resources/assets.minimal.json
|
||||||
src/resources/pools.json
|
src/resources/pools.json
|
||||||
|
|
||||||
|
# environment config
|
||||||
|
mempool-frontend-config.json
|
||||||
|
generated-config.js
|
||||||
|
@ -30,7 +30,9 @@
|
|||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.scss"
|
"src/styles.scss"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": [
|
||||||
|
"generated-config.js"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
|
36
frontend/generate-config.js
Normal file
36
frontend/generate-config.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
const CONFIG_FILE_NAME = 'mempool-frontend-config.json';
|
||||||
|
const GENERATED_CONFIG_FILE_NAME = 'generated-config.js';
|
||||||
|
|
||||||
|
let settings = [];
|
||||||
|
let configContent = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const rawConfig = fs.readFileSync(CONFIG_FILE_NAME);
|
||||||
|
configContent = JSON.parse(rawConfig);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.code !== 'ENOENT') {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (setting in configContent) {
|
||||||
|
settings.push({
|
||||||
|
key: setting,
|
||||||
|
value: configContent[setting]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = `(function (window) {
|
||||||
|
window.__env = window.__env || {};${settings.reduce((str, obj) => `${str}
|
||||||
|
window.__env.${obj.key} = ${ typeof obj.value === 'string' ? `'${obj.value}'` : obj.value };`, '')}
|
||||||
|
}(this));`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(GENERATED_CONFIG_FILE_NAME, code, 'utf8');
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Config file generated');
|
6
frontend/mempool-frontend-config.sample.json
Normal file
6
frontend/mempool-frontend-config.sample.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"TESTNET_ENABLED": false,
|
||||||
|
"LIQUID_ENABLED": false,
|
||||||
|
"ELCTRS_ITEMS_PER_PAGE": 25,
|
||||||
|
"KEEP_BLOCKS_AMOUNT": 8
|
||||||
|
}
|
@ -21,10 +21,11 @@
|
|||||||
"main": "index.ts",
|
"main": "index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "npm run sync-assets-dev && ng serve --proxy-config proxy.conf.json",
|
"start": "npm run generate-config && npm run sync-assets-dev && ng serve --proxy-config proxy.conf.json",
|
||||||
"build": "ng build --prod && npm run sync-assets",
|
"build": "npm run generate-config && ng build --prod && npm run sync-assets",
|
||||||
"sync-assets": "node sync-assets.js",
|
"sync-assets": "node sync-assets.js",
|
||||||
"sync-assets-dev": "node sync-assets.js dev",
|
"sync-assets-dev": "node sync-assets.js dev",
|
||||||
|
"generate-config": "node generate-config.js",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"lint": "ng lint",
|
"lint": "ng lint",
|
||||||
"e2e": "ng e2e"
|
"e2e": "ng e2e"
|
||||||
|
@ -34,5 +34,21 @@ export const mempoolFeeColors = [
|
|||||||
export const feeLevels = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
|
export const feeLevels = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
|
||||||
250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000];
|
250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000];
|
||||||
|
|
||||||
export const ELCTRS_ITEMS_PER_PAGE = 25;
|
interface Env {
|
||||||
export const KEEP_BLOCKS_AMOUNT = 8;
|
TESTNET_ENABLED: boolean;
|
||||||
|
LIQUID_ENABLED: boolean;
|
||||||
|
ELCTRS_ITEMS_PER_PAGE: number;
|
||||||
|
KEEP_BLOCKS_AMOUNT: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultEnv: Env = {
|
||||||
|
'TESTNET_ENABLED': false,
|
||||||
|
'LIQUID_ENABLED': false,
|
||||||
|
'ELCTRS_ITEMS_PER_PAGE': 25,
|
||||||
|
'KEEP_BLOCKS_AMOUNT': 8
|
||||||
|
};
|
||||||
|
|
||||||
|
const browserWindow = window || {};
|
||||||
|
// @ts-ignore
|
||||||
|
const browserWindowEnv = browserWindow.__env || {};
|
||||||
|
export const env: Env = Object.assign(defaultEnv, browserWindowEnv);
|
||||||
|
@ -7,7 +7,7 @@ import { Block, Transaction, Vout } from '../../interfaces/electrs.interface';
|
|||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
import { SeoService } from 'src/app/services/seo.service';
|
import { SeoService } from 'src/app/services/seo.service';
|
||||||
import { ELCTRS_ITEMS_PER_PAGE } from 'src/app/app.constants';
|
import { env } from 'src/app/app.constants';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-block',
|
selector: 'app-block',
|
||||||
@ -28,7 +28,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
|||||||
fees: number;
|
fees: number;
|
||||||
paginationMaxSize: number;
|
paginationMaxSize: number;
|
||||||
page = 1;
|
page = 1;
|
||||||
itemsPerPage = ELCTRS_ITEMS_PER_PAGE;
|
itemsPerPage = env.ELCTRS_ITEMS_PER_PAGE;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
@ -6,14 +6,14 @@
|
|||||||
<div class="badge badge-warning connection-badge" style="left: 30px;" *ngIf="connectionState === 1">Reconnecting...</div>
|
<div class="badge badge-warning connection-badge" style="left: 30px;" *ngIf="connectionState === 1">Reconnecting...</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="btn-group" style="margin-right: 16px;">
|
<div class="btn-group" style="margin-right: 16px;" *ngIf="env.TESTNET_ENABLED || env.LIQUID_ENABLED">
|
||||||
<button type="button" (click)="networkDropdownHidden = !networkDropdownHidden" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button type="button" (click)="networkDropdownHidden = !networkDropdownHidden" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
<span class="sr-only">Toggle Dropdown</span>
|
<span class="sr-only">Toggle Dropdown</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu" [class.d-block]="!networkDropdownHidden">
|
<div class="dropdown-menu" [class.d-block]="!networkDropdownHidden">
|
||||||
<a class="dropdown-item mainnet" [class.active]="network === ''" routerLink="/"><img src="./resources/bitcoin-logo.png" style="width: 35.5px;"> Mainnet</a>
|
<a class="dropdown-item mainnet" [class.active]="network === ''" routerLink="/"><img src="./resources/bitcoin-logo.png" style="width: 35.5px;"> Mainnet</a>
|
||||||
<a class="dropdown-item liquid" [class.active]="network === 'liquid'" routerLink="/liquid"><img src="./resources/liquid-logo.png" style="width: 35.5px;"> Liquid</a>
|
<a *ngIf="env.LIQUID_ENABLED" class="dropdown-item liquid" [class.active]="network === 'liquid'" routerLink="/liquid"><img src="./resources/liquid-logo.png" style="width: 35.5px;"> Liquid</a>
|
||||||
<a class="dropdown-item testnet" [class.active]="network === 'testnet'" routerLink="/testnet"><img src="./resources/testnet-logo.png" style="width: 35.5px;"> Testnet</a>
|
<a *ngIf="env.TESTNET_ENABLED" class="dropdown-item testnet" [class.active]="network === 'testnet'" routerLink="/testnet"><img src="./resources/testnet-logo.png" style="width: 35.5px;"> Testnet</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Component, OnInit, HostListener } from '@angular/core';
|
import { Component, OnInit, HostListener } from '@angular/core';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
|
import { env } from 'src/app/app.constants';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-master-page',
|
selector: 'app-master-page',
|
||||||
@ -9,6 +10,7 @@ import { StateService } from '../../services/state.service';
|
|||||||
export class MasterPageComponent implements OnInit {
|
export class MasterPageComponent implements OnInit {
|
||||||
network = '';
|
network = '';
|
||||||
tvViewRoute = '/tv';
|
tvViewRoute = '/tv';
|
||||||
|
env = env;
|
||||||
|
|
||||||
navCollapsed = false;
|
navCollapsed = false;
|
||||||
connectionState = 2;
|
connectionState = 2;
|
||||||
|
@ -4,7 +4,7 @@ import { Block, Transaction } from '../interfaces/electrs.interface';
|
|||||||
import { MempoolBlock, MemPoolState } from '../interfaces/websocket.interface';
|
import { MempoolBlock, MemPoolState } from '../interfaces/websocket.interface';
|
||||||
import { OptimizedMempoolStats } from '../interfaces/node-api.interface';
|
import { OptimizedMempoolStats } from '../interfaces/node-api.interface';
|
||||||
import { Router, NavigationStart } from '@angular/router';
|
import { Router, NavigationStart } from '@angular/router';
|
||||||
import { KEEP_BLOCKS_AMOUNT } from '../app.constants';
|
import { env } from '../app.constants';
|
||||||
import { shareReplay, map } from 'rxjs/operators';
|
import { shareReplay, map } from 'rxjs/operators';
|
||||||
|
|
||||||
interface MarkBlockState {
|
interface MarkBlockState {
|
||||||
@ -21,7 +21,7 @@ export class StateService {
|
|||||||
latestBlockHeight = 0;
|
latestBlockHeight = 0;
|
||||||
|
|
||||||
networkChanged$ = new ReplaySubject<string>(1);
|
networkChanged$ = new ReplaySubject<string>(1);
|
||||||
blocks$ = new ReplaySubject<[Block, boolean, boolean]>(KEEP_BLOCKS_AMOUNT);
|
blocks$ = new ReplaySubject<[Block, boolean, boolean]>(env.KEEP_BLOCKS_AMOUNT);
|
||||||
conversions$ = new ReplaySubject<any>(1);
|
conversions$ = new ReplaySubject<any>(1);
|
||||||
mempoolStats$ = new ReplaySubject<MemPoolState>(1);
|
mempoolStats$ = new ReplaySubject<MemPoolState>(1);
|
||||||
mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1);
|
mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user