mirror of
https://github.com/mempool/mempool.git
synced 2025-03-29 11:12:16 +01:00
145 lines
4.0 KiB
TypeScript
145 lines
4.0 KiB
TypeScript
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 OnInit, OnChanges, OnDestroy {
|
|
@Input() size: number = 300;
|
|
|
|
blocksSubscription: Subscription;
|
|
timeSubscription: Subscription;
|
|
|
|
faceStyle;
|
|
dialPath;
|
|
blockTimes = [];
|
|
segments = [];
|
|
hours: number = 0;
|
|
minutes: number = 0;
|
|
minorTicks: number[] = [];
|
|
majorTicks: number[] = [];
|
|
|
|
constructor(
|
|
public stateService: StateService,
|
|
private cd: ChangeDetectorRef
|
|
) {
|
|
this.updateTime();
|
|
this.makeTicks();
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
this.timeSubscription = timer(0, 250).pipe(
|
|
tap(() => {
|
|
this.updateTime();
|
|
})
|
|
).subscribe();
|
|
this.blocksSubscription = this.stateService.blocks$
|
|
.subscribe((blocks) => {
|
|
this.blockTimes = blocks.map(block => [block.height, new Date(block.timestamp * 1000)]);
|
|
this.blockTimes = this.blockTimes.sort((a, b) => a[1].getTime() - b[1].getTime());
|
|
this.updateSegments();
|
|
});
|
|
}
|
|
|
|
ngOnChanges(): void {
|
|
this.faceStyle = {
|
|
width: `${this.size}px`,
|
|
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;
|
|
}
|
|
}
|