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:
@@ -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>
|
||||
|
||||
@@ -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']);
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
.dashboard-container {
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.dashboard-container {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #1d1f31;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user