responsive clock, fix blockchain

This commit is contained in:
Mononaut 2023-04-19 03:34:13 +09:00
parent 61531171c9
commit f879a34021
No known key found for this signature in database
GPG Key ID: A3F058E41374C04E
21 changed files with 412 additions and 396 deletions

View File

@ -36,7 +36,6 @@ export default class BlockScene {
this.gridSize = this.width / this.gridWidth;
this.unitPadding = Math.max(1, Math.floor(this.gridSize / 2.5));
this.unitWidth = this.gridSize - (this.unitPadding);
console.log(this.gridSize, this.unitPadding, this.unitWidth);
this.dirty = true;
if (this.initialised && this.scene) {

View File

@ -1,21 +1,21 @@
<div class="blocks-container blockchain-blocks-container" [class.time-ltr]="timeLtr" [class.tiny]="tiny"
[style.left]="static ? (offset || 0) + 'px' : null"
<div class="blocks-container blockchain-blocks-container" [class.time-ltr]="timeLtr" [class.minimal]="minimal"
[style.left]="static ? (offset || 0) + 'px' : null" [style.--block-size]="blockWidth+'px'"
*ngIf="static || (loadingBlocks$ | async) === false; else loadingBlocksTemplate">
<div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn">
<ng-container *ngIf="connected && block && !block.loading && !block.placeholder; else placeholderBlock">
<div [attr.data-cy]="'bitcoin-block-offset-' + offset + '-index-' + i"
class="text-center bitcoin-block mined-block blockchain-blocks-offset-{{ offset }}-index-{{ i }}"
[class.offscreen]="tiny && i >= 6"
[class.offscreen]="!static && count && i >= count"
id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]"
[class.blink-bg]="isSpecial(block.height)">
<a draggable="false" [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }"
class="blockLink" [ngClass]="{'disabled': (this.stateService.blockScrolling$ | async)}">&nbsp;</a>
<div *ngIf="!tiny" [attr.data-cy]="'bitcoin-block-' + i + '-height'" class="block-height">
<div *ngIf="!minimal" [attr.data-cy]="'bitcoin-block-' + i + '-height'" class="block-height">
<a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height
}}</a>
</div>
<div class="block-body">
<ng-container *ngIf="!tiny">
<ng-container *ngIf="!minimal">
<div *ngIf="block?.extras; else emptyfees" [attr.data-cy]="'bitcoin-block-offset=' + offset + '-index-' + i + '-fees'" class="fees">
~{{ block?.extras?.medianFee | number:feeRounding }} <ng-container
i18n="shared.sat-vbyte|sat/vB">sat/vB</ng-container>
@ -82,11 +82,11 @@
</div>
<ng-template #loadingBlocksTemplate>
<div class="blocks-container" [class.time-ltr]="timeLtr">
<div class="blocks-container" [class.time-ltr]="timeLtr" [style.--block-size]="blockWidth+'px'">
<div class="flashing">
<div *ngFor="let block of emptyBlocks; let i = index; trackBy: trackByBlocksFn">
<div class="text-center bitcoin-block mined-block" id="bitcoin-block-{{ block.height }}"
[ngStyle]="emptyBlockStyles[i]"></div>
[ngStyle]="emptyBlockStyles[i]" [class.offscreen]="!static && count && i >= count"></div>
</div>
</div>
</div>

View File

@ -1,6 +1,6 @@
.bitcoin-block {
width: 125px;
height: 125px;
width: var(--block-size);
height: var(--block-size);
}
.blockLink {
@ -39,9 +39,11 @@
}
.blocks-container {
--block-size: 125px;
--block-offset: calc(0.32 * var(--block-size));
position: absolute;
top: 0px;
left: 40px;
left: var(--block-offset);
}
.block-body {
@ -81,11 +83,11 @@
.bitcoin-block::after {
content: '';
width: 125px;
height: 24px;
width: var(--block-size);
height: calc(0.192 * var(--block-size));
position:absolute;
top: -24px;
left: -20px;
top: calc(-0.192 * var(--block-size));
left: calc(-0.16 * var(--block-size));
background-color: #232838;
transform:skew(40deg);
transform-origin:top;
@ -93,11 +95,11 @@
.bitcoin-block::before {
content: '';
width: 20px;
height: 125px;
width: calc(0.16 * var(--block-size));
height: var(--block-size);
position: absolute;
top: -12px;
left: -20px;
top: calc(-0.096 * var(--block-size));
left: calc(-0.16 * var(--block-size));
background-color: #191c27;
transform: skewY(50deg);

View File

@ -24,7 +24,8 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
@Input() count: number = 8; // number of blocks in this chunk (dynamic blocks only)
@Input() loadingTip: boolean = false;
@Input() connected: boolean = true;
@Input() tiny: boolean = false;
@Input() minimal: boolean = false;
@Input() blockWidth: number = 125;
specialBlocks = specialBlocks;
network = '';
@ -52,6 +53,10 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
timeLtrSubscription: Subscription;
timeLtr: boolean;
blockOffset: number = 155;
dividerBlockOffset: number = 205;
blockPadding: number = 30;
gradientColors = {
'': ['#9339f4', '#105fb0'],
bisq: ['#9339f4', '#105fb0'],
@ -119,7 +124,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
this.blockStyles = [];
if (this.blocksFilled && block.height > this.chainTip) {
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i, i ? -155 : -205)));
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i, i ? -this.blockOffset : -this.dividerBlockOffset)));
setTimeout(() => {
this.blockStyles = [];
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i)));
@ -160,6 +165,13 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.blockWidth && this.blockWidth) {
this.blockPadding = 0.24 * this.blockWidth;
this.blockOffset = this.blockWidth + this.blockPadding;
this.dividerBlockOffset = this.blockOffset + (0.4 * this.blockWidth);
this.blockStyles = [];
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i)));
}
if (this.static) {
const animateSlide = changes.height && (changes.height.currentValue === changes.height.previousValue + 1);
this.updateStaticBlocks(animateSlide);
@ -192,14 +204,14 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
}
this.arrowVisible = true;
if (newBlockFromLeft) {
this.arrowLeftPx = blockindex * 155 + 30 - 205;
this.arrowLeftPx = blockindex * this.blockOffset + this.blockPadding - this.dividerBlockOffset;
setTimeout(() => {
this.arrowTransition = '2s';
this.arrowLeftPx = blockindex * 155 + 30;
this.arrowLeftPx = blockindex * this.blockOffset + this.blockPadding;
this.cd.markForCheck();
}, 50);
} else {
this.arrowLeftPx = blockindex * 155 + 30;
this.arrowLeftPx = blockindex * this.blockOffset + this.blockPadding;
if (!animate) {
setTimeout(() => {
this.arrowTransition = '2s';
@ -246,7 +258,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
}
this.blocks = this.blocks.slice(0, this.count);
this.blockStyles = [];
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i, animateSlide ? -155 : 0)));
this.blocks.forEach((b, i) => this.blockStyles.push(this.getStyleForBlock(b, i, animateSlide ? -this.blockOffset : 0)));
this.cd.markForCheck();
if (animateSlide) {
// animate blocks slide right
@ -288,7 +300,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
}
return {
left: addLeft + 155 * index + 'px',
left: addLeft + this.blockOffset * index + 'px',
background: `repeating-linear-gradient(
#2d3348,
#2d3348 ${greenBackgroundHeight}%,
@ -310,7 +322,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
const addLeft = animateEnterFrom || 0;
return {
left: addLeft + (155 * index) + 'px',
left: addLeft + (this.blockOffset * index) + 'px',
background: "#2d3348",
};
}
@ -318,7 +330,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
getStyleForPlaceholderBlock(index: number, animateEnterFrom: number = 0) {
const addLeft = animateEnterFrom || 0;
return {
left: addLeft + (155 * index) + 'px',
left: addLeft + (this.blockOffset * index) + 'px',
};
}
@ -326,7 +338,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
const addLeft = animateEnterFrom || 0;
return {
left: addLeft + 155 * this.emptyBlocks.indexOf(block) + 'px',
left: addLeft + this.blockOffset * this.emptyBlocks.indexOf(block) + 'px',
background: "#2d3348",
};
}

View File

@ -1,9 +1,3 @@
<div class="clock-wrapper" [style]="wrapperStyle">
<div class="clockchain-bar">
<div class="clockchain" [style]="chainStyle">
<app-clockchain></app-clockchain>
</div>
</div>
<div class="clock-face" [style]="faceStyle">
<ng-content></ng-content>
<svg
@ -91,4 +85,3 @@
<path id="gnomon" style="opacity:1;fill:#80C2E1;fill-opacity:1;stroke:none;stroke-width:3.73798;stroke-opacity:1" d="M 46.463002,316.72751 33.743954,300.31177 65.383738,288.93232 Z" />
</svg>
</div>
</div>

View File

@ -1,27 +1,3 @@
.clock-wrapper {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
.clockchain-bar, .clock-face {
flex-shrink: 0;
flex-grow: 0;
}
.clockchain-bar {
position: relative;
height: 15.625%;
// background: #1d1f31;
// box-shadow: 0 0 15px #000;
}
.clock-face {
position: relative;
height: 84.375%;
@ -42,4 +18,3 @@
}
}
}
}

View File

@ -1,36 +1,17 @@
import { Component, HostListener, OnInit } from '@angular/core';
import { Component, Input, OnChanges } from '@angular/core';
@Component({
selector: 'app-clock-face',
templateUrl: './clock-face.component.html',
styleUrls: ['./clock-face.component.scss'],
})
export class ClockFaceComponent implements OnInit {
size: number;
wrapperStyle;
chainStyle;
export class ClockFaceComponent implements OnChanges {
@Input() size: number = 300;
faceStyle;
showDial: boolean = false;
constructor() {}
ngOnInit(): void {
// initialize stuff
this.resizeCanvas();
}
@HostListener('window:resize', ['$event'])
resizeCanvas(): void {
this.size = Math.min(window.innerWidth, 0.78125 * window.innerHeight);
this.wrapperStyle = {
'--clock-width': `${this.size}px`
};
const scaleFactor = window.innerWidth / 1390;
this.chainStyle = {
transform: `translate(2vw, 0.5vw) scale(${scaleFactor})`,
transformOrigin: 'top left',
};
ngOnChanges(): void {
this.faceStyle = {
width: `${this.size}px`,
height: `${this.size}px`,

View File

@ -1,17 +1 @@
<app-clock-face>
<div class="block-wrapper">
<ng-container *ngIf="block && block.height >= 0">
<div class="block-cube">
<div class="side top"></div>
<div class="side bottom"></div>
<div class="side right" [style]="blockStyle"></div>
<div class="side left" [style]="blockStyle"></div>
<div class="side front" [style]="blockStyle"></div>
<div class="side back" [style]="blockStyle"></div>
</div>
<div class="title-wrapper">
<h1 class="block-height">{{ block.height }}</h1>
</div>
</ng-container>
</div>
</app-clock-face>
<app-clock mode="block"></app-clock>

View File

@ -1,57 +1,7 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { StateService } from '../../services/state.service';
import { BlockExtended } from '../../interfaces/node-api.interface';
import { WebsocketService } from '../../services/websocket.service';
import { Component } from '@angular/core';
@Component({
selector: 'app-clock-a',
templateUrl: './clock-a.component.html',
styleUrls: ['./clock.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ClockAComponent implements OnInit {
blocksSubscription: Subscription;
block: BlockExtended;
blockStyle;
gradientColors = {
'': ['#9339f4', '#105fb0'],
bisq: ['#9339f4', '#105fb0'],
liquid: ['#116761', '#183550'],
'liquidtestnet': ['#494a4a', '#272e46'],
testnet: ['#1d486f', '#183550'],
signet: ['#6f1d5d', '#471850'],
};
constructor(
public stateService: StateService,
private websocketService: WebsocketService,
private cd: ChangeDetectorRef,
) {}
ngOnInit(): void {
this.websocketService.want(['blocks']);
this.blocksSubscription = this.stateService.blocks$
.subscribe(([block]) => {
if (block) {
this.block = block;
this.blockStyle = this.getStyleForBlock(this.block);
this.cd.markForCheck();
}
});
}
getStyleForBlock(block: BlockExtended) {
const greenBackgroundHeight = 100 - (block.weight / this.stateService.env.BLOCK_WEIGHT_UNITS) * 100;
return {
background: `repeating-linear-gradient(
#2d3348,
#2d3348 ${greenBackgroundHeight}%,
${this.gradientColors[''][0]} ${Math.max(greenBackgroundHeight, 0)}%,
${this.gradientColors[''][1]} 100%
)`,
};
}
}
export class ClockAComponent {}

View File

@ -1,13 +1 @@
<app-clock-face>
<div class="block-wrapper">
<ng-container *ngIf="block && block.height >= 0">
<div class="block-sizer" [style]="blockSizerStyle">
<app-mempool-block-overview [index]="0"></app-mempool-block-overview>
</div>
<div class="fader"></div>
<div class="title-wrapper">
<h1 class="block-height">{{ block.height }}</h1>
</div>
</ng-container>
</div>
</app-clock-face>
<app-clock mode="mempool"></app-clock>

View File

@ -1,57 +1,7 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { StateService } from '../../services/state.service';
import { BlockExtended } from '../../interfaces/node-api.interface';
import { WebsocketService } from '../../services/websocket.service';
import { Component } from '@angular/core';
@Component({
selector: 'app-clock-b',
templateUrl: './clock-b.component.html',
styleUrls: ['./clock.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ClockBComponent implements OnInit {
blocksSubscription: Subscription;
block: BlockExtended;
blockSizerStyle;
gradientColors = {
'': ['#9339f4', '#105fb0'],
bisq: ['#9339f4', '#105fb0'],
liquid: ['#116761', '#183550'],
'liquidtestnet': ['#494a4a', '#272e46'],
testnet: ['#1d486f', '#183550'],
signet: ['#6f1d5d', '#471850'],
};
constructor(
public stateService: StateService,
private websocketService: WebsocketService,
private cd: ChangeDetectorRef,
) {}
ngOnInit(): void {
this.resizeCanvas();
this.websocketService.want(['blocks']);
this.blocksSubscription = this.stateService.blocks$
.subscribe(([block]) => {
if (block) {
this.block = block;
this.cd.markForCheck();
}
});
}
@HostListener('window:resize', ['$event'])
resizeCanvas(): void {
const clockSize = Math.min(window.innerWidth, 0.78125 * window.innerHeight);
const size = Math.ceil(clockSize / 75) * 75;
const margin = (clockSize - size) / 2;
this.blockSizerStyle = {
transform: `translate(${margin}px, ${margin}px)`,
width: `${size}px`,
height: `${size}px`,
};
this.cd.markForCheck();
}
}
export class ClockBComponent {}

View File

@ -0,0 +1,34 @@
<div class="clock-wrapper" [style]="wrapperStyle">
<div class="clockchain-bar" [style.height]="chainHeight + 'px'">
<div class="clockchain">
<app-clockchain [width]="chainWidth" [height]="chainHeight"></app-clockchain>
</div>
</div>
<div class="clock-face">
<app-clock-face [size]="clockSize">
<div class="block-wrapper">
<ng-container *ngIf="block && block.height >= 0">
<ng-container *ngIf="mode === 'block'; else mempoolMode;">
<div class="block-cube">
<div class="side top"></div>
<div class="side bottom"></div>
<div class="side right" [style]="blockStyle"></div>
<div class="side left" [style]="blockStyle"></div>
<div class="side front" [style]="blockStyle"></div>
<div class="side back" [style]="blockStyle"></div>
</div>
</ng-container>
<ng-template #mempoolMode>
<div class="block-sizer" [style]="blockSizerStyle">
<app-mempool-block-overview [index]="0"></app-mempool-block-overview>
</div>
</ng-template>
<div class="fader"></div>
<div class="title-wrapper">
<h1 class="block-height">{{ block.height }}</h1>
</div>
</ng-container>
</div>
</app-clock-face>
</div>
</div>

View File

@ -1,3 +1,44 @@
.clock-wrapper {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
--clock-width: 300px;
.clockchain-bar, .clock-face {
flex-shrink: 0;
flex-grow: 0;
}
.clockchain-bar {
position: relative;
width: 100%;
height: 15.625%;
z-index: 2;
overflow: hidden;
// background: #1d1f31;
// box-shadow: 0 0 15px #000;
}
.clock-face {
position: relative;
height: 84.375%;
margin: auto;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
z-index: 1;
}
}
.title-wrapper {
position: absolute;
left: 0;
@ -101,8 +142,6 @@
}
}
@keyframes block-spin {
0% {transform: translate(-50%, -50%) rotateX(-20deg) rotateY(0deg);}
100% {transform: translate(-50%, -50%) rotateX(-20deg) rotateY(-360deg);}

View File

@ -0,0 +1,82 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostListener, Input, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { StateService } from '../../services/state.service';
import { BlockExtended } from '../../interfaces/node-api.interface';
import { WebsocketService } from '../../services/websocket.service';
@Component({
selector: 'app-clock',
templateUrl: './clock.component.html',
styleUrls: ['./clock.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ClockComponent implements OnInit {
@Input() mode: string = 'block';
blocksSubscription: Subscription;
block: BlockExtended;
clockSize: number = 300;
chainWidth: number = 384;
chainHeight: number = 60;
blockStyle;
blockSizerStyle;
wrapperStyle;
gradientColors = {
'': ['#9339f4', '#105fb0'],
bisq: ['#9339f4', '#105fb0'],
liquid: ['#116761', '#183550'],
'liquidtestnet': ['#494a4a', '#272e46'],
testnet: ['#1d486f', '#183550'],
signet: ['#6f1d5d', '#471850'],
};
constructor(
public stateService: StateService,
private websocketService: WebsocketService,
private cd: ChangeDetectorRef,
) {}
ngOnInit(): void {
this.resizeCanvas();
this.websocketService.want(['blocks']);
this.blocksSubscription = this.stateService.blocks$
.subscribe(([block]) => {
if (block) {
this.block = block;
this.blockStyle = this.getStyleForBlock(this.block);
this.cd.markForCheck();
}
});
}
getStyleForBlock(block: BlockExtended) {
const greenBackgroundHeight = 100 - (block.weight / this.stateService.env.BLOCK_WEIGHT_UNITS) * 100;
return {
background: `repeating-linear-gradient(
#2d3348,
#2d3348 ${greenBackgroundHeight}%,
${this.gradientColors[''][0]} ${Math.max(greenBackgroundHeight, 0)}%,
${this.gradientColors[''][1]} 100%
)`,
};
}
@HostListener('window:resize', ['$event'])
resizeCanvas(): void {
this.chainWidth = window.innerWidth;
this.chainHeight = Math.max(60, window.innerHeight / 8);
this.clockSize = Math.min(500, window.innerWidth, window.innerHeight - (1.4 * this.chainHeight));
const size = Math.ceil(this.clockSize / 75) * 75;
const margin = (this.clockSize - size) / 2;
this.blockSizerStyle = {
transform: `translate(${margin}px, ${margin}px)`,
width: `${size}px`,
height: `${size}px`,
};
this.wrapperStyle = {
'--clock-width': `${this.clockSize}px`
};
this.cd.markForCheck();
}
}

View File

@ -1,11 +1,25 @@
<div class="text-center" class="blockchain-wrapper" [class.time-ltr]="timeLtr" [class.ltr-transition]="ltrTransitionEnabled" #container>
<div class="position-container" [ngClass]="network ? network : ''">
<div class="position-container" [ngClass]="network ? network : ''" [style.top]="(height / 3) + 'px'">
<span>
<div class="blocks-wrapper">
<app-mempool-blocks [tiny]="true" [count]="3"></app-mempool-blocks>
<app-blockchain-blocks [tiny]="true"></app-blockchain-blocks>
<app-mempool-blocks [minimal]="true" [count]="mempoolBlocks" [blockWidth]="blockWidth"></app-mempool-blocks>
<app-blockchain-blocks [minimal]="true" [count]="blockchainBlocks" [blockWidth]="blockWidth"></app-blockchain-blocks>
</div>
<div class="divider" [style.top]="-(height / 6) + 'px'">
<svg
viewBox="0 0 2 175"
[style.width]="'2px'"
[style.height]="(5 * height / 6) + 'px'"
>
<line
class="divider-line"
x0="0"
x1="0"
y0="0"
y1="175px"
></line>
</svg>
</div>
<div class="divider"></div>
</span>
</div>
</div>

View File

@ -1,15 +1,17 @@
.divider {
width: 4px;
height: 180px;
left: 0;
top: -40px;
position: absolute;
margin-bottom: 120px;
background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3cline x1='0' y1='0' x2='0' y2='100%' stroke='white' stroke-width='8' stroke-dasharray='18%2c32' stroke-dashoffset='-5' stroke-linecap='square'/%3e%3c/svg%3e");
left: -1px;
top: 0;
.divider-line {
stroke: white;
stroke-width: 4px;
stroke-linecap: butt;
stroke-dasharray: 25px 25px;
}
}
.blockchain-wrapper {
height: 250px;
height: 100%;
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
@ -20,37 +22,10 @@
.position-container {
position: absolute;
left: 0;
top: 75px;
top: 0;
transform: translateX(50vw);
}
.position-container.liquid, .position-container.liquidtestnet {
transform: translateX(420px);
}
.blockchain-wrapper {
.position-container {
transform: translateX(95vw);
}
.position-container.liquid, .position-container.liquidtestnet {
transform: translateX(50vw);
}
.position-container.loading {
transform: translateX(50vw);
}
}
.blockchain-wrapper.time-ltr {
.position-container {
transform: translateX(5vw);
}
.position-container.liquid, .position-container.liquidtestnet {
transform: translateX(50vw);
}
.position-container.loading {
transform: translateX(50vw);
}
}
.black-background {
background-color: #11131f;
z-index: 100;

View File

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Input } from '@angular/core';
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Input, OnChanges, ChangeDetectorRef } from '@angular/core';
import { firstValueFrom, Subscription } from 'rxjs';
import { StateService } from '../../services/state.service';
@ -8,7 +8,15 @@ import { StateService } from '../../services/state.service';
styleUrls: ['./clockchain.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ClockchainComponent implements OnInit, OnDestroy {
export class ClockchainComponent implements OnInit, OnChanges, OnDestroy {
@Input() width: number = 300;
@Input() height: number = 60;
mempoolBlocks: number = 3;
blockchainBlocks: number = 6;
blockWidth: number = 50;
dividerStyle;
network: string;
timeLtrSubscription: Subscription;
timeLtr: boolean = this.stateService.timeLtr.value;
@ -19,9 +27,12 @@ export class ClockchainComponent implements OnInit, OnDestroy {
constructor(
public stateService: StateService,
private cd: ChangeDetectorRef,
) {}
ngOnInit() {
this.ngOnChanges();
this.network = this.stateService.network;
this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => {
this.timeLtr = !!ltr;
@ -34,6 +45,17 @@ export class ClockchainComponent implements OnInit, OnDestroy {
});
}
ngOnChanges() {
this.blockWidth = Math.floor(7 * this.height / 12);
this.mempoolBlocks = Math.floor(((this.width / 2) - (this.blockWidth * 0.32)) / (1.24 * this.blockWidth));
this.blockchainBlocks = this.mempoolBlocks;
this.dividerStyle = {
width: '2px',
height: `${this.height}px`,
};
this.cd.markForCheck();
}
ngOnDestroy() {
this.timeLtrSubscription.unsubscribe();
this.connectionStateSubscription.unsubscribe();

View File

@ -1,12 +1,12 @@
<ng-container *ngIf="(loadingBlocks$ | async) === false; else loadingBlocks" [class.tiny]="tiny">
<div class="mempool-blocks-container" [class.time-ltr]="timeLtr" *ngIf="(difficultyAdjustments$ | async) as da;">
<ng-container *ngIf="(loadingBlocks$ | async) === false; else loadingBlocks" [class.minimal]="minimal">
<div class="mempool-blocks-container" [class.time-ltr]="timeLtr" [style.--block-size]="blockWidth+'px'" *ngIf="(difficultyAdjustments$ | async) as da;">
<div class="flashing">
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
<div @blockEntryTrigger [@.disabled]="i > 0 || !animateEntry" [attr.data-cy]="'mempool-block-' + i" class="bitcoin-block text-center mempool-block" [class.last-block]="tiny && i >= count - 1" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink">
<div @blockEntryTrigger [@.disabled]="i > 0 || !animateEntry" [attr.data-cy]="'mempool-block-' + i" class="bitcoin-block text-center mempool-block" [class.hide-block]="count && i >= count" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]" [class.blink-bg]="projectedBlock.blink">
<a draggable="false" [routerLink]="['/mempool-block/' | relativeUrl, i]"
class="blockLink" [ngClass]="{'disabled': (this.stateService.blockScrolling$ | async)}">&nbsp;</a>
<div class="block-body">
<ng-container *ngIf="!tiny">
<ng-container *ngIf="!minimal">
<div [attr.data-cy]="'mempool-block-' + i + '-fees'" class="fees">
~{{ projectedBlock.medianFee | number:feeRounding }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
</div>
@ -73,10 +73,10 @@
</ng-container>
<ng-template #loadingBlocks>
<div class="mempool-blocks-container" [class.time-ltr]="timeLtr">
<div class="mempool-blocks-container" [class.time-ltr]="timeLtr" [style.--block-size]="blockWidth+'px'">
<div class="flashing">
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolEmptyBlocks" let-i="index" [ngForTrackBy]="trackByFn">
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolEmptyBlockStyles[i]"></div>
<div class="bitcoin-block text-center mempool-block" [class.hide-block]="count && i >= count" id="mempool-block-{{ i }}" [ngStyle]="mempoolEmptyBlockStyles[i]"></div>
</ng-template>
</div>
</div>

View File

@ -1,6 +1,6 @@
.bitcoin-block {
width: 125px;
height: 125px;
width: var(--block-size);
height: var(--block-size);
transition: background 2s, right 2s, transform 1s, opacity 1s;
}
@ -14,6 +14,7 @@
top: 0px;
right: 0px;
left: 0px;
--block-size: 125px;
}
.flashing {
@ -66,11 +67,11 @@
.bitcoin-block::after {
content: '';
width: 125px;
height: 24px;
width: var(--block-size);
height: calc(0.192 * var(--block-size));
position:absolute;
top: -24px;
left: -20px;
top: calc(-0.192 * var(--block-size));
left: calc(-0.16 * var(--block-size));
background-color: #232838;
transform:skew(40deg);
transform-origin:top;
@ -79,11 +80,11 @@
.bitcoin-block::before {
content: '';
width: 20px;
height: 125px;
width: calc(0.16 * var(--block-size));
height: var(--block-size);
position: absolute;
top: -12px;
left: -20px;
top: calc(-0.096 * var(--block-size));
left: calc(-0.16 * var(--block-size));
background-color: #191c27;
z-index: -1;
@ -100,7 +101,7 @@
background-color: #2d2825;
}
.mempool-block.last-block {
.mempool-block.hide-block {
opacity: 0;
}
@ -145,7 +146,7 @@
.bitcoin-block::before {
transform: skewY(-50deg);
left: 125px;
left: var(--block-size);
}
.block-body {
transform: scaleX(-1);

View File

@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input } from '@angular/core';
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input, OnChanges, SimpleChanges } from '@angular/core';
import { Subscription, Observable, fromEvent, merge, of, combineLatest } from 'rxjs';
import { MempoolBlock } from '../../interfaces/websocket.interface';
import { StateService } from '../../services/state.service';
@ -23,8 +23,9 @@ import { animate, style, transition, trigger } from '@angular/animations';
])],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MempoolBlocksComponent implements OnInit, OnDestroy {
@Input() tiny: boolean = false;
export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
@Input() minimal: boolean = false;
@Input() blockWidth: number = 125;
@Input() count: number = null;
specialBlocks = specialBlocks;
@ -51,8 +52,9 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
timeLtr: boolean;
animateEntry: boolean = false;
blockWidth = 125;
blockPadding = 30;
blockOffset: number = 155;
blockPadding: number = 30;
containerOffset: number = 40;
arrowVisible = false;
tabHidden = false;
feeRounding = '1.0-0';
@ -221,6 +223,14 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
});
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.blockWidth && this.blockWidth) {
this.blockPadding = 0.24 * this.blockWidth;
this.containerOffset = 0.32 * this.blockWidth;
this.blockOffset = this.blockWidth + this.blockPadding;
}
}
ngOnDestroy() {
this.markBlocksSubscription.unsubscribe();
this.blockSubscription.unsubscribe();
@ -243,12 +253,13 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
const innerWidth = this.stateService.env.BASE_MODULE !== 'liquid' && window.innerWidth <= 767.98 ? window.innerWidth : window.innerWidth / 2;
let blocksAmount;
if (this.count) {
blocksAmount = this.count;
blocksAmount = 8;
} else {
blocksAmount = Math.min(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT, Math.floor(innerWidth / (this.blockWidth + this.blockPadding)));
}
while (blocks.length > blocksAmount) {
const block = blocks.pop();
if (!this.count) {
const lastBlock = blocks[blocks.length - 1];
lastBlock.blockSize += block.blockSize;
lastBlock.blockVSize += block.blockVSize;
@ -258,6 +269,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
lastBlock.medianFee = this.median(lastBlock.feeRange);
lastBlock.totalFees += block.totalFees;
}
}
if (blocks.length) {
blocks[blocks.length - 1].isStack = blocks[blocks.length - 1].blockVSize > this.stateService.blockVSize;
}
@ -302,14 +314,14 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
});
return {
'right': 40 + index * 155 + 'px',
'right': this.containerOffset + index * this.blockOffset + 'px',
'background': backgroundGradients.join(',') + ')'
};
}
getStyleForMempoolEmptyBlock(index: number) {
return {
'right': 40 + index * 155 + 'px',
'right': this.containerOffset + index * this.blockOffset + 'px',
'background': '#554b45',
};
}

View File

@ -93,6 +93,7 @@ import { GlobalFooterComponent } from './components/global-footer/global-footer.
import { MempoolBlockOverviewComponent } from '../components/mempool-block-overview/mempool-block-overview.component';
import { ClockchainComponent } from '../components/clockchain/clockchain.component';
import { ClockFaceComponent } from '../components/clock-face/clock-face.component';
import { ClockComponent } from '../components/clock/clock.component';
import { ClockAComponent } from '../components/clock/clock-a.component';
import { ClockBComponent } from '../components/clock/clock-b.component';
@ -181,6 +182,7 @@ import { ClockBComponent } from '../components/clock/clock-b.component';
MempoolBlockOverviewComponent,
ClockchainComponent,
ClockComponent,
ClockAComponent,
ClockBComponent,
ClockFaceComponent,
@ -294,6 +296,7 @@ import { ClockBComponent } from '../components/clock/clock-b.component';
MempoolBlockOverviewComponent,
ClockchainComponent,
ClockComponent,
ClockAComponent,
ClockBComponent,
ClockFaceComponent,