Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"start:dev": "ng serve --configuration development",
"build": "ng build",
"build:prod": "ng build --configuration production",
"watch": "ng build --watch --configuration development",
"tests": "npx cypress open --config baseUrl=http://localhost:4200",
"tests:headless": "npx cypress run --headless --config baseUrl=http://localhost:4200",
"docker": "npm run build:prod && docker buildx build --platform linux/amd64 -t openmina/frontend:latest . && docker push openmina/frontend:latest",
Expand Down
44 changes: 36 additions & 8 deletions frontend/src/app/core/services/web-node.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, filter, from, fromEvent, map, merge, Observable, of, switchMap, tap } from 'rxjs';
import { BehaviorSubject, catchError, filter, from, fromEvent, map, merge, Observable, of, switchMap, tap } from 'rxjs';
import base from 'base-x';
import { any } from '@openmina/shared';
import { HttpClient } from '@angular/common/http';
import { sendSentryEvent } from '@shared/helpers/webnode.helper';
import { DashboardPeerStatus } from '@shared/types/dashboard/dashboard.peer';

@Injectable({
providedIn: 'root',
Expand All @@ -11,6 +13,8 @@ export class WebNodeService {

private readonly webnode$: BehaviorSubject<any> = new BehaviorSubject<any>(null);
private webNodeKeyPair: { publicKey: string, privateKey: string };
private webNodeStartTime: number;
private sentryEvents: any = {};

constructor(private http: HttpClient) {
const basex = base('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz');
Expand All @@ -21,32 +25,39 @@ export class WebNodeService {
}

loadWasm$(): Observable<void> {
console.log('---LOADING WEBNODE---');
sendSentryEvent('Loading WebNode JS');
return merge(
of(any(window).webnode).pipe(filter(Boolean)),
fromEvent(window, 'webNodeLoaded'),
).pipe(
switchMap(() => this.http.get<{ publicKey: string, privateKey: string }>('assets/webnode/web-node-secrets.json')),
tap(data => this.webNodeKeyPair = data),
tap(data => {
this.webNodeKeyPair = data;
sendSentryEvent('WebNode JS Loaded. Loading WebNode Wasm');
}),
map(() => void 0),
);
}

startWasm$(): Observable<any> {
console.log('---STARTING WEBNODE---');
return of(any(window).webnode)
.pipe(
switchMap((wasm: any) => from(wasm.default('assets/webnode/pkg/openmina_node_web_bg.wasm')).pipe(map(() => wasm))),
switchMap((wasm) => {
console.log(wasm);
sendSentryEvent('WebNode Wasm loaded. Starting WebNode');
return from(wasm.run(this.webNodeKeyPair.privateKey));
}),
tap((webnode: any) => {
console.log('----------------WEBNODE----------------');
console.log(webnode);
(window as any)["webnode"] = webnode;
sendSentryEvent('WebNode Started');
this.webNodeStartTime = Date.now();
(window as any)['webnode'] = webnode;
this.webnode$.next(webnode);
}),
catchError((error) => {
sendSentryEvent('WebNode failed to start');
console.error(error);
return of(null);
}),
switchMap(() => this.webnode$.asObservable()),
filter(Boolean),
);
Expand All @@ -70,6 +81,23 @@ export class WebNodeService {
return this.webnode$.asObservable().pipe(
filter(Boolean),
switchMap(handle => from(any(handle).state().peers())),
tap((peers) => {
if (!this.sentryEvents.sentNoPeersEvent && Date.now() - this.webNodeStartTime >= 5000 && peers.length === 0) {
console.log('WebNode has no peers after 5 seconds from startup.');
sendSentryEvent('WebNode has no peers after 5 seconds from startup.');
this.sentryEvents.sentNoPeersEvent = true;
}
if (!this.sentryEvents.sentPeersEvent && peers.length > 0) {
const seconds = (Date.now() - this.webNodeStartTime) / 1000;
sendSentryEvent(`WebNode found its first peer after ${seconds}s`);
this.sentryEvents.sentPeersEvent = true;
}
if (!this.sentryEvents.firstPeerConnected && peers.some((p: any) => p.connection_status === DashboardPeerStatus.CONNECTED)) {
const seconds = (Date.now() - this.webNodeStartTime) / 1000;
sendSentryEvent(`WebNode connected to its first peer after ${seconds}s`);
this.sentryEvents.firstPeerConnected = true;
}
}),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const getSlotsSuccess = createAction(type('Get Slots Success'), props<{
const changeFilters = createAction(type('Change Filters'), props<{ filters: BlockProductionWonSlotsFilters }>());
const setActiveSlot = createAction(type('Set Active Slot'), props<{ slot: BlockProductionWonSlotsSlot }>());
const sort = createAction(type('Sort'), props<{ sort: TableSort<BlockProductionWonSlotsSlot> }>());
const toggleSidePanel = createAction(type('Toggle Side Panel'));

export const BlockProductionWonSlotsActions = {
init,
Expand All @@ -36,4 +37,5 @@ export const BlockProductionWonSlotsActions = {
changeFilters,
setActiveSlot,
sort,
toggleSidePanel,
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ChangeDetectionStrategy, Component, ElementRef, OnDestroy, OnInit } from '@angular/core';
import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class';
import { getMergedRoute, isDesktop, isMobile, MergedRoute } from '@openmina/shared';
import { debounceTime, filter, fromEvent, take, timer } from 'rxjs';
import { debounceTime, filter, fromEvent, skip, take, timer } from 'rxjs';
import { untilDestroyed } from '@ngneat/until-destroy';
import { BlockProductionWonSlotsActions } from '@block-production/won-slots/block-production-won-slots.actions';
import { AppSelectors } from '@app/app.state';
import { BlockProductionWonSlotsSelectors } from '@block-production/won-slots/block-production-won-slots.state';

@Component({
selector: 'mina-block-production-won-slots',
Expand Down Expand Up @@ -37,16 +38,10 @@ export class BlockProductionWonSlotsComponent extends StoreDispatcher implements
}

private listenToResize(): void {
fromEvent(window, 'resize')
.pipe(
debounceTime(100),
filter(() => this.showSidePanel === isMobile()),
untilDestroyed(this),
)
.subscribe(() => {
this.showSidePanel = isDesktop();
this.detect();
});
this.select(BlockProductionWonSlotsSelectors.openSidePanel, (open: boolean) => {
this.showSidePanel = open;
this.detect();
});
}

override ngOnDestroy(): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createReducer, on } from '@ngrx/store';
import { BlockProductionWonSlotsState } from '@block-production/won-slots/block-production-won-slots.state';
import { sort, SortDirection, TableSort } from '@openmina/shared';
import { isMobile, sort, SortDirection, TableSort } from '@openmina/shared';
import { BlockProductionWonSlotsActions } from '@block-production/won-slots/block-production-won-slots.actions';
import {
BlockProductionWonSlotsSlot,
Expand All @@ -13,6 +13,7 @@ const initialState: BlockProductionWonSlotsState = {
filteredSlots: [],
activeSlot: undefined,
activeSlotRoute: undefined,
openSidePanel: !isMobile(),
filters: {
accepted: true,
orphaned: true,
Expand All @@ -37,11 +38,13 @@ export const blockProductionWonSlotsReducer = createReducer(
epoch,
filteredSlots: filterSlots(sortSlots(slots, state.sort), state.filters),
activeSlot,
openSidePanel: !!activeSlot,
})),
on(BlockProductionWonSlotsActions.setActiveSlot, (state, { slot }) => ({
...state,
activeSlot: slot,
activeSlotRoute: slot.globalSlot.toString(),
openSidePanel: true,
})),
on(BlockProductionWonSlotsActions.sort, (state, { sort }) => ({
...state,
Expand All @@ -53,6 +56,7 @@ export const blockProductionWonSlotsReducer = createReducer(
filters,
filteredSlots: filterSlots(sortSlots(state.slots, state.sort), filters),
})),
on(BlockProductionWonSlotsActions.toggleSidePanel, state => ({ ...state, openSidePanel: !state.openSidePanel })),
on(BlockProductionWonSlotsActions.close, () => initialState),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface BlockProductionWonSlotsState {
filteredSlots: BlockProductionWonSlotsSlot[];
activeSlot: BlockProductionWonSlotsSlot;
activeSlotRoute: string;
openSidePanel: boolean;
filters: BlockProductionWonSlotsFilters;
sort: TableSort<BlockProductionWonSlotsSlot>;
}
Expand All @@ -34,6 +35,7 @@ const filteredSlots = select(state => state.filteredSlots);
const activeSlot = select(state => state.activeSlot);
const filters = select(state => state.filters);
const sort = select(state => state.sort);
const openSidePanel = select(state => state.openSidePanel);

export const BlockProductionWonSlotsSelectors = {
epoch,
Expand All @@ -42,4 +44,5 @@ export const BlockProductionWonSlotsSelectors = {
activeSlot,
filters,
sort,
openSidePanel,
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import {
BlockProductionWonSlotsEpoch,
} from '@shared/types/block-production/won-slots/block-production-won-slots-epoch.type';
import { BlockProductionWonSlotsActions } from '@block-production/won-slots/block-production-won-slots.actions';

@Component({
selector: 'mina-block-production-won-slots-cards',
Expand Down Expand Up @@ -67,9 +68,7 @@ export class BlockProductionWonSlotsCardsComponent extends StoreDispatcher imple
this.card4.lastBlockTime = getTimeDiff(lastItem(slots.filter(s => s.status === BlockProductionWonSlotsStatus.Canonical))?.slotTime).diff;

this.card6.totalRewards = slots
.filter(
s => [BlockProductionWonSlotsStatus.Canonical, BlockProductionWonSlotsStatus.Orphaned, BlockProductionWonSlotsStatus.Discarded].includes(s.status),
)
.filter(s => [BlockProductionWonSlotsStatus.Canonical].includes(s.status))
.map(s => s.coinbaseRewards + s.txFeesRewards).reduce((a, b) => a + b, 0).toFixed(0);

this.card6.totalRewards = isNaN(+this.card6.totalRewards) ? '0' : this.card6.totalRewards;
Expand All @@ -81,4 +80,8 @@ export class BlockProductionWonSlotsCardsComponent extends StoreDispatcher imple
const secondsToAdd = minutesToAdd * 60;
return timestampInSeconds + secondsToAdd;
}

toggleSidePanel(): void {
this.dispatch2(BlockProductionWonSlotsActions.toggleSidePanel());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
<div class="h-xl fx-row-vert-cent flex-between pl-12 pr-12 f-600 p-relative">
<span class="secondary">{{ title }}</span>
<div class="percentage success-primary">{{ percentage }}%</div>
<div class="fx-row-vert-cent">
@if (percentage !== null && percentage !== undefined) {
<div class="percentage success-primary">{{ percentage }}%</div>
}
@if (isMobile) {
<span class="mina-icon pointer tertiary primary-hover f-18"
(click)="closeSidePanel()">close</span>
}
</div>
<div *ngIf="percentage > 0" [style.width.%]="percentage"
class="progress-bar p-absolute">
<div class="highlight p-absolute" *ngIf="percentage < 100"></div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@ import {
BlockProductionWonSlotTimes,
} from '@shared/types/block-production/won-slots/block-production-won-slots-slot.type';
import { getTimeDiff } from '@shared/helpers/date.helper';
import { any, hasValue, noMillisFormat, ONE_THOUSAND, SecDurationConfig, toReadableDate } from '@openmina/shared';
import {
any,
hasValue,
isMobile,
noMillisFormat,
ONE_THOUSAND,
SecDurationConfig,
toReadableDate,
} from '@openmina/shared';
import { filter } from 'rxjs';
import { BlockProductionWonSlotsActions } from '@block-production/won-slots/block-production-won-slots.actions';
import { AppSelectors } from '@app/app.state';
Expand All @@ -31,12 +39,13 @@ export class BlockProductionWonSlotsSidePanelComponent extends StoreDispatcher i

protected readonly BlockProductionWonSlotsStatus = BlockProductionWonSlotsStatus;
protected readonly config: SecDurationConfig = {
includeMinutes: true,
color: false,
includeMinutes: true,
undefinedAlternative: undefined,
valueIsZeroFn: () => '<1ms',
};
protected readonly noMillisFormat = noMillisFormat;
isMobile = isMobile();
title: string;

slot: BlockProductionWonSlotsSlot;
Expand Down Expand Up @@ -178,11 +187,6 @@ export class BlockProductionWonSlotsSidePanelComponent extends StoreDispatcher i
}
}

override ngOnDestroy(): void {
super.ngOnDestroy();
clearInterval(this.timer);
}

private queryServerOftenToGetTheNewSlotState(): void {
const timer = setInterval(() => {
if (!this.stateWhenReachedZero) {
Expand All @@ -192,4 +196,13 @@ export class BlockProductionWonSlotsSidePanelComponent extends StoreDispatcher i
this.dispatch2(BlockProductionWonSlotsActions.getSlots());
}, 1000);
}

closeSidePanel(): void {
this.dispatch2(BlockProductionWonSlotsActions.toggleSidePanel());
}

override ngOnDestroy(): void {
super.ngOnDestroy();
clearInterval(this.timer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
}

div {
gap: 24px;
gap: 8px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class NodePickerComponent extends StoreDispatcher implements AfterViewIni
map(() => this.searchInput.nativeElement.value.toLowerCase()),
)
.subscribe((value: string) => {
this.filteredNodes = this.nodes.filter(n => n.name.toLowerCase().includes(value) || n.url.toLowerCase().includes(value));
this.filteredNodes = this.nodes.filter(n => n.name.toLowerCase().includes(value) || n.url?.toLowerCase().includes(value));
this.detect();
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="wrapper fx-row-vert-cent"
*ngIf="details"
[ngClass]="details.status">
[ngClass]="switchForbidden ? AppNodeStatus.PENDING : details.status">
<ng-container *ngIf="!switchForbidden && !hideNodeStats">
<div class="chip p-relative h-sm pl-5 pr-5 fx-row-vert-cent border-rad-6 text-nowrap"
#mempoolAnchor
Expand All @@ -23,7 +23,7 @@
}
</div>
</ng-container>
<div class="node-status fx-row-vert-cent bg-surface h-sm border-rad-6 p-relative z-1 mr-10"
<div class="node-status fx-row-vert-cent bg-surface h-sm p-relative z-1 mr-10"
[class.can-add-nodes]="switchForbidden">
<ng-container *ngIf="!switchForbidden && !hideNodeStats">
<div class="shine-parent overflow-hidden p-absolute z-0 border-rad-6">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,12 @@
}
}

.chip::before {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
.chip {
&,
&::before {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
}

.shine-parent {
Expand Down
Loading
Loading