-
-
-
-
-
-
-
-
{{ replacement.tx.fee / (replacement.tx.vsize) | feeRounding }} sat/vB
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ cell.replacement.tx.fee / (cell.replacement.tx.vsize) | feeRounding }} sat/vB
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/app/components/clock-face/clock-face.component.scss b/frontend/src/app/components/clock-face/clock-face.component.scss
index 60b2c4eba..d671341a6 100644
--- a/frontend/src/app/components/clock-face/clock-face.component.scss
+++ b/frontend/src/app/components/clock-face/clock-face.component.scss
@@ -17,4 +17,52 @@
fill: #11131f;
}
}
+
+ .gnomon {
+ transform-origin: center;
+ stroke-linejoin: round;
+
+ &.minute {
+ fill:#80C2E1;
+ stroke:#80C2E1;
+ stroke-width: 2px;
+ }
+
+ &.hour {
+ fill: #105fb0;
+ stroke: #105fb0;
+ stroke-width: 6px;
+ }
+ }
+
+ .tick {
+ transform-origin: center;
+ fill: none;
+ stroke: white;
+ stroke-width: 2px;
+
+ &.minor {
+ stroke-opacity: 0.5;
+ }
+
+ &.very.major {
+ stroke-width: 4px;
+ }
+ }
+
+ .block-segment {
+ fill: none;
+ stroke: url(#dial-gradient);
+ stroke-width: 18px;
+ }
+
+ .dial-segment {
+ fill: none;
+ stroke: white;
+ stroke-width: 2px;
+ }
+
+ .dial-gradient-img {
+ transform-origin: center;
+ }
}
\ No newline at end of file
diff --git a/frontend/src/app/components/clock-face/clock-face.component.ts b/frontend/src/app/components/clock-face/clock-face.component.ts
index c63ea56ea..9c373a50d 100644
--- a/frontend/src/app/components/clock-face/clock-face.component.ts
+++ b/frontend/src/app/components/clock-face/clock-face.component.ts
@@ -1,15 +1,55 @@
-import { Component, Input, OnChanges } from '@angular/core';
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core';
+import { Subscription, tap, timer } from 'rxjs';
+import { WebsocketService } from '../../services/websocket.service';
+import { StateService } from '../../services/state.service';
@Component({
selector: 'app-clock-face',
templateUrl: './clock-face.component.html',
styleUrls: ['./clock-face.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class ClockFaceComponent implements OnChanges {
+export class ClockFaceComponent implements OnInit, OnChanges, OnDestroy {
@Input() size: number = 300;
- faceStyle;
- constructor() {}
+ blocksSubscription: Subscription;
+ timeSubscription: Subscription;
+
+ faceStyle;
+ dialPath;
+ blockTimes = [];
+ segments = [];
+ hours: number = 0;
+ minutes: number = 0;
+ minorTicks: number[] = [];
+ majorTicks: number[] = [];
+
+ constructor(
+ public stateService: StateService,
+ private websocketService: WebsocketService,
+ private cd: ChangeDetectorRef
+ ) {
+ this.updateTime();
+ this.makeTicks();
+ }
+
+ ngOnInit(): void {
+ this.timeSubscription = timer(0, 250).pipe(
+ tap(() => {
+ this.updateTime();
+ })
+ ).subscribe();
+ this.websocketService.want(['blocks']);
+ this.blocksSubscription = this.stateService.blocks$
+ .subscribe(([block]) => {
+ if (block) {
+ this.blockTimes.push([block.height, new Date(block.timestamp * 1000)]);
+ // using block-reported times, so ensure they are sorted chronologically
+ this.blockTimes = this.blockTimes.sort((a, b) => a[1].getTime() - b[1].getTime());
+ this.updateSegments();
+ }
+ });
+ }
ngOnChanges(): void {
this.faceStyle = {
@@ -17,4 +57,93 @@ export class ClockFaceComponent implements OnChanges {
height: `${this.size}px`,
};
}
+
+ ngOnDestroy(): void {
+ this.timeSubscription.unsubscribe();
+ }
+
+ updateTime(): void {
+ const now = new Date();
+ const seconds = now.getSeconds() + (now.getMilliseconds() / 1000);
+ this.minutes = (now.getMinutes() + (seconds / 60)) % 60;
+ this.hours = now.getHours() + (this.minutes / 60);
+ this.updateSegments();
+ }
+
+ updateSegments(): void {
+ const now = new Date();
+ this.blockTimes = this.blockTimes.filter(time => (now.getTime() - time[1].getTime()) <= 3600000);
+ const tail = new Date(now.getTime() - 3600000);
+ const hourStart = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours());
+
+ const times = [
+ ['start', tail],
+ ...this.blockTimes,
+ ['end', now],
+ ];
+ const minuteTimes = times.map(time => {
+ return [time[0], (time[1].getTime() - hourStart.getTime()) / 60000];
+ });
+ this.segments = [];
+ const r = 174;
+ const cx = 192;
+ const cy = cx;
+ for (let i = 1; i < minuteTimes.length; i++) {
+ const arc = this.getArc(minuteTimes[i-1][1], minuteTimes[i][1], r, cx, cy);
+ if (arc) {
+ arc.id = minuteTimes[i][0];
+ this.segments.push(arc);
+ }
+ }
+ const arc = this.getArc(minuteTimes[0][1], minuteTimes[1][1], r, cx, cy);
+ if (arc) {
+ this.dialPath = arc.path;
+ }
+
+ this.cd.markForCheck();
+ }
+
+ getArc(startTime, endTime, r, cx, cy): any {
+ const startDegrees = (startTime + 0.2) * 6;
+ const endDegrees = (endTime - 0.2) * 6;
+ const start = this.getPointOnCircle(startDegrees, r, cx, cy);
+ const end = this.getPointOnCircle(endDegrees, r, cx, cy);
+ const arcLength = endDegrees - startDegrees;
+ // merge gaps and omit lines shorter than 1 degree
+ if (arcLength >= 1) {
+ const path = `M ${start.x} ${start.y} A ${r} ${r} 0 ${arcLength > 180 ? 1 : 0} 1 ${end.x} ${end.y}`;
+ return {
+ path,
+ start,
+ end
+ };
+ } else {
+ return null;
+ }
+ }
+
+ getPointOnCircle(deg, r, cx, cy) {
+ const modDeg = ((deg % 360) + 360) % 360;
+ const rad = (modDeg * Math.PI) / 180;
+ return {
+ x: cx + (r * Math.sin(rad)),
+ y: cy - (r * Math.cos(rad)),
+ };
+ }
+
+ makeTicks() {
+ this.minorTicks = [];
+ this.majorTicks = [];
+ for (let i = 1; i < 60; i++) {
+ if (i % 5 === 0) {
+ this.majorTicks.push(i * 6);
+ } else {
+ this.minorTicks.push(i * 6);
+ }
+ }
+ }
+
+ trackBySegment(index: number, segment) {
+ return segment.id;
+ }
}
diff --git a/frontend/src/app/components/clock/clock.component.scss b/frontend/src/app/components/clock/clock.component.scss
index e5904b4f1..a27c62499 100644
--- a/frontend/src/app/components/clock/clock.component.scss
+++ b/frontend/src/app/components/clock/clock.component.scss
@@ -84,7 +84,7 @@
right: 0;
top: 0;
bottom: 0;
- background: radial-gradient(transparent 0%, transparent 48%, #11131f 62%, #11131f 100%);
+ background: radial-gradient(transparent 0%, transparent 44%, #11131f 58%, #11131f 100%);
}
.block-cube {
diff --git a/frontend/src/app/components/clock/clock.component.ts b/frontend/src/app/components/clock/clock.component.ts
index 7aa875695..c804860af 100644
--- a/frontend/src/app/components/clock/clock.component.ts
+++ b/frontend/src/app/components/clock/clock.component.ts
@@ -66,7 +66,7 @@ export class ClockComponent implements OnInit {
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));
+ this.clockSize = Math.min(800, window.innerWidth, window.innerHeight - (1.4 * this.chainHeight));
const size = Math.ceil(this.clockSize / 75) * 75;
const margin = (this.clockSize - size) / 2;
this.blockSizerStyle = {
diff --git a/frontend/src/app/components/clockchain/clockchain.component.scss b/frontend/src/app/components/clockchain/clockchain.component.scss
index 0b01adc26..acff1e725 100644
--- a/frontend/src/app/components/clockchain/clockchain.component.scss
+++ b/frontend/src/app/components/clockchain/clockchain.component.scss
@@ -1,6 +1,6 @@
.divider {
position: absolute;
- left: -1px;
+ left: -0.5px;
top: 0;
.divider-line {
stroke: white;
diff --git a/frontend/src/app/components/clockchain/clockchain.component.ts b/frontend/src/app/components/clockchain/clockchain.component.ts
index addc22948..ab9220c54 100644
--- a/frontend/src/app/components/clockchain/clockchain.component.ts
+++ b/frontend/src/app/components/clockchain/clockchain.component.ts
@@ -39,8 +39,8 @@ export class ClockchainComponent implements OnInit, OnChanges, OnDestroy {
});
this.connectionStateSubscription = this.stateService.connectionState$.subscribe(state => {
this.connected = (state === 2);
- })
- firstValueFrom(this.stateService.chainTip$).then(tip => {
+ });
+ firstValueFrom(this.stateService.chainTip$).then(() => {
this.loadingTip = false;
});
}
diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts
index 6877823f5..6267eed21 100644
--- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts
+++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts
@@ -27,7 +27,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
@Input() minimal: boolean = false;
@Input() blockWidth: number = 125;
@Input() count: number = null;
-
+
specialBlocks = specialBlocks;
mempoolBlocks: MempoolBlock[] = [];
mempoolEmptyBlocks: MempoolBlock[] = this.mountEmptyBlocks();
diff --git a/frontend/src/resources/clock/gradient.png b/frontend/src/resources/clock/gradient.png
new file mode 100644
index 0000000000000000000000000000000000000000..372105fbd128b7428d4e31f393bc70587d0b44d3
GIT binary patch
literal 38328
zcmXt
8pC4-d%gGcU6Te$ce*3
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