Merge branch 'master' into simon/bisq-dashboard

# Conflicts:
#	frontend/package-lock.json
#	frontend/src/app/components/master-page/master-page.component.html
This commit is contained in:
softsimon
2021-04-23 15:35:35 +04:00
32 changed files with 358 additions and 64 deletions

View File

@@ -5,7 +5,7 @@
<br>
<div class="text-small text-center offset-md-1">
v2.2-dev ({{ gitCommit$ | async }})
v{{ packetJsonVersion }} [{{ frontendGitCommitHash }}]
</div>
<br>
@@ -48,7 +48,7 @@
<a href="https://squarecrypto.org/" target="_blank">
<div class="profile_photo enterprise_sponsor d-inline-block" title="Square Crypto">
<img class="profile_img" src="/resources/profile/sqcrypto.png" />
<img class="profile_img" src="/resources/profile/sqcrypto.svg" />
Square
</div>
</a>
@@ -60,6 +60,13 @@
</div>
</a>
<a href="https://exodus.com/" target="_blank">
<div class="profile_photo enterprise_sponsor d-inline-block" title="Exodus">
<img class="profile_img" src="/resources/profile/exodus.svg" />
Exodus
</div>
</a>
<br><br>
<br><br>
@@ -392,4 +399,10 @@
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
</div>
<br>
<div class="text-small text-center" *ngIf="officialMempoolSpace">
{{ (backendInfo$ | async)?.hostname }} (v{{ (backendInfo$ | async )?.version }}) [{{ (backendInfo$ | async )?.gitCommit | slice:0:8 }}]
</div>
</div>

View File

@@ -6,7 +6,8 @@ import { Observable, Subscription } from 'rxjs';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ApiService } from 'src/app/services/api.service';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { delay, map, retryWhen, switchMap, tap } from 'rxjs/operators';
import { delay, retryWhen, switchMap, tap } from 'rxjs/operators';
import { IBackendInfo } from 'src/app/interfaces/websocket.interface';
@Component({
selector: 'app-about',
@@ -14,7 +15,7 @@ import { delay, map, retryWhen, switchMap, tap } from 'rxjs/operators';
styleUrls: ['./about.component.scss'],
})
export class AboutComponent implements OnInit, OnDestroy {
gitCommit$: Observable<string>;
backendInfo$: Observable<IBackendInfo>;
donationForm: FormGroup;
paymentForm: FormGroup;
donationStatus = 1;
@@ -22,6 +23,9 @@ export class AboutComponent implements OnInit, OnDestroy {
contributors$: Observable<any>;
donationObj: any;
sponsorsEnabled = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
frontendGitCommitHash = this.stateService.env.GIT_COMMIT_HASH.substr(0, 8);
packetJsonVersion = this.stateService.env.PACKAGE_JSON_VERSION;
officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE;
sponsors = null;
contributors = null;
requestSubscription: Subscription | undefined;
@@ -36,7 +40,7 @@ export class AboutComponent implements OnInit, OnDestroy {
) { }
ngOnInit() {
this.gitCommit$ = this.stateService.gitCommit$.pipe(map((str) => str.substr(0, 8)));
this.backendInfo$ = this.stateService.backendInfo$;
this.seoService.setTitle($localize`:@@004b222ff9ef9dd4771b777950ca1d0e4cd4348a:About`);
this.websocketService.want(['blocks']);

View File

@@ -4,7 +4,7 @@
<div class="clearfix"></div>
<table class="table table-borderless" [alwaysCallback]="true" [fromRoot]="true" [infiniteScrollContainer]="'body'" infiniteScroll [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()">
<table class="table table-borderless" [alwaysCallback]="true" infiniteScroll [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()">
<thead>
<th style="width: 15%;" i18n="latest-blocks.height">Height</th>
<th class="d-none d-md-block" style="width: 20%;" i18n="latest-blocks.timestamp">Timestamp</th>

View File

@@ -26,7 +26,7 @@
</div>
<div class="navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav mr-auto pt-2 pb-2 pb-md-0 pt-md-0 {{ network.val }}">
<ul class="navbar-nav {{ network.val }}">
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
<a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" [fixedWidth]="true" i18n-title="master-page.dashboard" title="Dashboard"></fa-icon></a>
</li>
@@ -62,7 +62,7 @@
<a class="nav-link" [routerLink]="['/about']" (click)="collapse()"><fa-icon [icon]="['fas', 'info-circle']" [fixedWidth]="true" i18n-title="master-page.about" title="About"></fa-icon></a>
</li>
</ul>
<app-search-form location="top" (searchTriggered)="collapse()"></app-search-form>
<app-search-form class="search-form-container" location="top" (searchTriggered)="collapse()"></app-search-form>
</div>
</nav>
</header>

View File

@@ -8,14 +8,16 @@ fa-icon {
.navbar {
z-index: 100;
min-height: 64px;
}
li.nav-item {
margin: auto 10px;
padding-left: 10px;
padding-right: 10px;
}
@media (min-width: 768px) {
@media (min-width: 992px) {
.navbar {
padding: 0rem 2rem;
}
@@ -26,17 +28,50 @@ li.nav-item {
margin-right: 16px;
}
li.nav-item {
margin: auto 0px;
padding: 10px;
}
}
li.nav-item a {
color: #ffffff;
.navbar-nav {
background: #212121;
bottom: 0;
box-shadow: 0px 0px 15px 0px #000;
flex-direction: row;
left: 0;
justify-content: center;
position: fixed;
width: 100%;
}
.navbar-nav {
flex-direction: row;
justify-content: center;
@media (min-width: 992px) {
.navbar-nav {
background: transparent;
box-shadow: none;
position: relative;
width: auto;
}
}
.navbar-collapse {
flex-basis: auto;
justify-content: flex-end;
}
@media (min-width: 992px) {
.navbar-collapse {
justify-content: space-between;
}
}
.navbar-brand {
width: 60%;
}
@media (min-width: 576px) {
.navbar-brand {
width: 140px;
}
}
nav {
@@ -81,5 +116,16 @@ nav {
.dropdown-item {
display: flex;
align-items:center;
align-items: center;
}
@media (min-width: 992px) {
.search-form-container {
width: 100%;
max-width: 500px;
padding-left: 15px;
}
}
.navbar-dark .navbar-nav .nav-link {
color: #f1f1f1;
}

View File

@@ -5,12 +5,53 @@
text-overflow: ellipsis;
}
form {
margin-top: 5px;
}
@media (min-width: 576px) {
form {
margin-top: 0px;
margin-left: 8px;
}
}
@media (min-width: 992px) {
form {
width: 100%;
}
}
.btn-block {
width: 58.55px;
}
.search-box-container {
width: 100%;
}
@media (min-width: 992px) {
.search-box-container input {
border: 0px;
}
.search-box-container .btn {
width: 100px;
}
@media (min-width: 768px) {
.search-box-container {
width: 350px;
min-width: 400px;
}
}
@media (min-width: 992px) {
.search-box-container {
min-width: 260px;
}
}
@media (min-width: 1200px) {
.search-box-container {
min-width: 300px;
}
}

View File

@@ -15,7 +15,7 @@
<div class="card-header">
<i class="fa fa-area-chart"></i> <span i18n="statistics.memory-by-vBytes">Mempool by vBytes (sat/vByte)</span>
<form [formGroup]="radioGroupForm" style="float: right;">
<form [formGroup]="radioGroupForm" style="float: right;" (click)="saveGraphPreference()">
<div class="spinner-border text-light bootstrap-spinner" *ngIf="spinnerLoading"></div>
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
<label ngbButtonLabel class="btn-primary btn-sm">

View File

@@ -35,6 +35,7 @@ export class StatisticsComponent implements OnInit {
radioGroupForm: FormGroup;
inverted: boolean;
graphWindowPreference: String;
constructor(
@Inject(LOCALE_ID) private locale: string,
@@ -45,16 +46,13 @@ export class StatisticsComponent implements OnInit {
private stateService: StateService,
private seoService: SeoService,
private storageService: StorageService,
) {
this.radioGroupForm = this.formBuilder.group({
dateSpan: '2h'
});
}
) { }
ngOnInit() {
this.seoService.setTitle($localize`:@@5d4f792f048fcaa6df5948575d7cb325c9393383:Graphs`);
this.stateService.networkChanged$.subscribe((network) => this.network = network);
this.inverted = this.storageService.getValue('inverted-graph') === 'true';
this.graphWindowPreference = this.storageService.getValue('graphWindowPreference') ? this.storageService.getValue('graphWindowPreference').trim() : '2h';
const isMobile = window.innerWidth <= 767.98;
let labelHops = isMobile ? 48 : 24;
@@ -62,8 +60,12 @@ export class StatisticsComponent implements OnInit {
labelHops = 96;
}
this.radioGroupForm = this.formBuilder.group({
dateSpan: this.graphWindowPreference
});
const labelInterpolationFnc = (value: any, index: any) => {
switch (this.radioGroupForm.controls.dateSpan.value) {
switch (this.graphWindowPreference) {
case '2h':
case '24h':
value = formatDate(value, 'HH:mm', this.locale);
@@ -166,4 +168,8 @@ export class StatisticsComponent implements OnInit {
this.storageService.setValue('inverted-graph', !this.inverted);
document.location.reload();
}
saveGraphPreference() {
this.storageService.setValue('graphWindowPreference', this.radioGroupForm.controls.dateSpan.value);
}
}

View File

@@ -12,7 +12,7 @@
</div>
<div class="clearfix"></div>
</div>
<div class="header-bg box" infiniteScroll [alwaysCallback]="true" [fromRoot]="true" [infiniteScrollContainer]="'body'" [infiniteScrollDistance]="2" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="onScroll()">
<div class="header-bg box" infiniteScroll [alwaysCallback]="true" [infiniteScrollDistance]="2" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="onScroll()">
<div class="row">
<div class="col">
<table class="table table-borderless smaller-text table-xs" style="margin: 0;">

View File

@@ -1,5 +1,5 @@
<div class="container-xl mt-2">
<div class="container-xl mt-2 dashboard-container">
<div class="row row-cols-1 row-cols-md-2" *ngIf="{ value: (mempoolInfoData$ | async) } as mempoolInfoData">
<ng-template [ngIf]="collapseLevel === 'three'" [ngIfElse]="expanded">

View File

@@ -1,3 +1,12 @@
.dashboard-container {
padding-bottom: 60px;
}
@media (min-width: 992px) {
.dashboard-container {
padding-bottom: 0px;
}
}
.card {
background-color: #1d1f31;
}

View File

@@ -16,6 +16,7 @@ export interface WebsocketResponse {
rbfTransaction?: Transaction;
transactions?: TransactionStripped[];
loadingIndicators?: ILoadingIndicators;
backendInfo?: IBackendInfo;
'track-tx'?: string;
'track-address'?: string;
'track-asset'?: string;
@@ -50,3 +51,8 @@ export interface TransactionStripped {
value: number;
}
export interface IBackendInfo {
hostname: string;
gitCommit: string;
version: string;
}

View File

@@ -1,7 +1,7 @@
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs';
import { Block, Transaction } from '../interfaces/electrs.interface';
import { MempoolBlock, MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface';
import { IBackendInfo, MempoolBlock, MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface';
import { OptimizedMempoolStats } from '../interfaces/node-api.interface';
import { Router, NavigationStart } from '@angular/router';
import { isPlatformBrowser } from '@angular/common';
@@ -28,6 +28,8 @@ export interface Env {
NGINX_PROTOCOL?: string;
NGINX_HOSTNAME?: string;
NGINX_PORT?: string;
GIT_COMMIT_HASH: string;
PACKAGE_JSON_VERSION: string;
}
const defaultEnv: Env = {
@@ -43,6 +45,8 @@ const defaultEnv: Env = {
'NGINX_PROTOCOL': 'http',
'NGINX_HOSTNAME': '127.0.0.1',
'NGINX_PORT': '80',
'GIT_COMMIT_HASH': '',
'PACKAGE_JSON_VERSION': '',
};
@Injectable({
@@ -67,7 +71,7 @@ export class StateService {
isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
vbytesPerSecond$ = new ReplaySubject<number>(1);
lastDifficultyAdjustment$ = new ReplaySubject<number>(1);
gitCommit$ = new ReplaySubject<string>(1);
backendInfo$ = new ReplaySubject<IBackendInfo>(1);
loadingIndicators$ = new ReplaySubject<ILoadingIndicators>(1);
live2Chart$ = new Subject<OptimizedMempoolStats>();

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
import { WebsocketResponse } from '../interfaces/websocket.interface';
import { WebsocketResponse, IBackendInfo } from '../interfaces/websocket.interface';
import { StateService } from './state.service';
import { Block, Transaction } from '../interfaces/electrs.interface';
import { Subscription } from 'rxjs';
@@ -244,13 +244,13 @@ export class WebsocketService {
this.stateService.bsqPrice$.next(response['bsq-price']);
}
if (response['git-commit']) {
this.stateService.gitCommit$.next(response['git-commit']);
if (response.backendInfo) {
this.stateService.backendInfo$.next(response.backendInfo);
if (!this.latestGitCommit) {
this.latestGitCommit = response['git-commit'];
this.latestGitCommit = response.backendInfo.gitCommit;
} else {
if (this.latestGitCommit !== response['git-commit']) {
if (this.latestGitCommit !== response.backendInfo.gitCommit) {
setTimeout(() => {
window.location.reload();
}, Math.floor(Math.random() * 60000) + 60000);
@@ -291,7 +291,7 @@ export class WebsocketService {
}
if (response['git-commit']) {
this.stateService.gitCommit$.next(response['git-commit']);
this.stateService.backendInfo$.next(response['git-commit']);
}
}
}