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
							
								
									68cff33ce2
								
							
						
					
					
						commit
						b2889abfa9
					
				@ -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