From 5bd5d9a2b87ffd62edf9026ee4a8fbb37c89924c Mon Sep 17 00:00:00 2001 From: Teofil Jolte Date: Tue, 3 Dec 2024 13:08:40 +0200 Subject: [PATCH] Sentry sync events Parse file example benchmarks fix --- frontend/package-lock.json | 83 +++++++++++-- frontend/package.json | 3 +- .../app/core/helpers/file-progress.helper.ts | 2 +- .../src/app/core/services/rust.service.ts | 4 + .../src/app/core/services/sentry.service.ts | 70 +++++++++++ .../wallets/benchmarks-wallets.component.html | 6 +- .../wallets/benchmarks-wallets.effects.ts | 11 +- .../block-production-won-slots.component.html | 2 +- .../block-production-won-slots.component.ts | 5 +- .../block-production-won-slots.service.ts | 4 +- .../dashboard-blocks-sync.component.ts | 11 +- .../dashboard-ledger.component.ts | 22 ++-- .../features/dashboard/dashboard.component.ts | 1 - .../app/features/mempool/mempool.service.ts | 6 +- .../parse-files/parse-files.component.html | 12 ++ .../parse-files/parse-files.component.scss | 0 .../parse-files/parse-files.component.ts | 112 ++++++++++++++++++ .../server-status.component.html | 6 +- .../server-status.component.scss | 1 - .../web-node-landing-page.component.html | 1 + .../web-node-landing-page.component.ts | 2 + .../src/app/shared/helpers/date.helper.ts | 19 ++- frontend/src/environments/environment.ts | 24 ++-- frontend/src/index.html | 4 +- 24 files changed, 354 insertions(+), 57 deletions(-) create mode 100644 frontend/src/app/core/services/sentry.service.ts create mode 100644 frontend/src/app/layout/parse-files/parse-files.component.html create mode 100644 frontend/src/app/layout/parse-files/parse-files.component.scss create mode 100644 frontend/src/app/layout/parse-files/parse-files.component.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6c7b02ea18..f7e1bb1481 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "frontend", - "version": "1.0.43", + "version": "1.0.50", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "frontend", - "version": "1.0.43", + "version": "1.0.50", "dependencies": { "@angular/animations": "^17.3.12", "@angular/cdk": "^17.3.10", @@ -38,6 +38,7 @@ "eigen": "^0.2.2", "express": "^4.18.2", "firebase": "^11.0.1", + "jszip": "^3.10.1", "mathjs": "^12.3.0", "mina-signer": "^3.0.7", "ngx-json-viewer": "^3.2.1", @@ -9443,8 +9444,7 @@ "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "node_modules/cors": { "version": "2.8.5", @@ -12237,6 +12237,12 @@ "node": ">=0.10.0" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/immutable": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", @@ -12637,8 +12643,7 @@ "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "node_modules/isbinaryfile": { "version": "4.0.10", @@ -12937,6 +12942,48 @@ "verror": "1.10.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/karma": { "version": "6.4.2", "dev": true, @@ -13232,6 +13279,15 @@ } } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/limiter": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", @@ -14742,6 +14798,12 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -15217,8 +15279,7 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "node_modules/progress": { "version": "2.0.3", @@ -16205,6 +16266,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/setprototypeof": { "version": "1.2.0", "license": "ISC" diff --git a/frontend/package.json b/frontend/package.json index e0c8523cae..f77c5d1ec6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "1.0.44", + "version": "1.0.59", "scripts": { "install:deps": "npm install", "start": "npm install && ng serve --configuration local --open", @@ -53,6 +53,7 @@ "eigen": "^0.2.2", "express": "^4.18.2", "firebase": "^11.0.1", + "jszip": "^3.10.1", "mathjs": "^12.3.0", "mina-signer": "^3.0.7", "ngx-json-viewer": "^3.2.1", diff --git a/frontend/src/app/core/helpers/file-progress.helper.ts b/frontend/src/app/core/helpers/file-progress.helper.ts index 4347a22a71..2c5652b6b9 100644 --- a/frontend/src/app/core/helpers/file-progress.helper.ts +++ b/frontend/src/app/core/helpers/file-progress.helper.ts @@ -32,7 +32,7 @@ class AssetMonitor { url: resource.toString(), startTime, progress: 0, - totalSize: 27355980, + totalSize: 30111552, status: 'pending', endTime: 0, duration: 0, diff --git a/frontend/src/app/core/services/rust.service.ts b/frontend/src/app/core/services/rust.service.ts index deda879343..0a2d8a64a1 100644 --- a/frontend/src/app/core/services/rust.service.ts +++ b/frontend/src/app/core/services/rust.service.ts @@ -18,6 +18,10 @@ export class RustService { this.node = node; } + get activeNodeIsWebNode(): boolean { + return this.node.isWebNode; + } + get URL(): string { return this.node.url; } diff --git a/frontend/src/app/core/services/sentry.service.ts b/frontend/src/app/core/services/sentry.service.ts new file mode 100644 index 0000000000..18a3d1db70 --- /dev/null +++ b/frontend/src/app/core/services/sentry.service.ts @@ -0,0 +1,70 @@ +import { inject, Injectable } from '@angular/core'; +import { NodesOverviewLedger, NodesOverviewLedgerStepState } from '@shared/types/nodes/dashboard/nodes-overview-ledger.type'; +import * as Sentry from '@sentry/angular'; +import { NodesOverviewBlock, NodesOverviewNodeBlockStatus } from '@shared/types/nodes/dashboard/nodes-overview-block.type'; +import { lastItem, ONE_BILLION } from '@openmina/shared'; +import { RustService } from '@core/services/rust.service'; + +@Injectable({ + providedIn: 'root', +}) +export class SentryService { + + private ledgerIsSynced: boolean = false; + private blockIsSynced: boolean = false; + private rustService: RustService = inject(RustService); + + updateLedgerSyncStatus(ledger: NodesOverviewLedger): void { + if (this.ledgerIsSynced) { + return; + } + if (ledger.rootStaged.state === NodesOverviewLedgerStepState.SUCCESS) { + this.ledgerIsSynced = true; + const syncDetails = { + stakingLedger: { + fetchHashes: ledger.stakingEpoch.snarked.fetchHashesDuration + 's', + fetchAccounts: ledger.stakingEpoch.snarked.fetchAccountsDuration + 's', + }, + nextEpochLedger: { + fetchHashes: ledger.nextEpoch.snarked.fetchHashesDuration + 's', + fetchAccounts: ledger.nextEpoch.snarked.fetchAccountsDuration + 's', + }, + snarkedRootLedger: { + fetchHashes: ledger.rootSnarked.snarked.fetchHashesDuration + 's', + fetchAccounts: ledger.rootSnarked.snarked.fetchAccountsDuration + 's', + }, + stagedRootLedger: { + fetchParts: ledger.rootStaged.staged.fetchPartsDuration + 's', + reconstruct: ledger.rootStaged.staged.reconstructDuration + 's', + }, + }; + + const syncedIn = Math.round((ledger.rootStaged.staged.reconstructEnd - ledger.stakingEpoch.snarked.fetchHashesStart) / ONE_BILLION); + + Sentry.captureMessage(`Ledger synced in ${syncedIn}s`, { + level: 'info', + tags: { type: 'webnode', subType: 'sync.ledger' }, + contexts: { ledger: syncDetails }, + }); + } + } + + updateBlockSyncStatus(blocks: NodesOverviewBlock[], startTime: number): void { + if (this.blockIsSynced || !this.rustService.activeNodeIsWebNode) { + return; + } + + const blocksSynced = blocks.every(b => b.status === NodesOverviewNodeBlockStatus.APPLIED); + if (blocksSynced && blocks[0]) { + this.blockIsSynced = true; + blocks = blocks.slice(1); + const bestTipBlock = blocks[0].height; + const root = lastItem(blocks).height; + Sentry.captureMessage(`Last 290 blocks synced in ${Math.round((Date.now() - startTime) / 1000)}s`, { + level: 'info', + tags: { type: 'webnode', subType: 'sync.block' }, + contexts: { blocks: { bestTipBlock, root } }, + }); + } + } +} diff --git a/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.component.html b/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.component.html index 57ae83e92c..a7a94c8d55 100644 --- a/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.component.html +++ b/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.component.html @@ -1,7 +1,9 @@
- -
+ } +
diff --git a/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.effects.ts b/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.effects.ts index 54f5215991..d831c35149 100644 --- a/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.effects.ts +++ b/frontend/src/app/features/benchmarks/wallets/benchmarks-wallets.effects.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { MinaState, selectMinaState } from '@app/app.setup'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Effect } from '@openmina/shared'; -import { EMPTY, forkJoin, map, switchMap } from 'rxjs'; +import { combineLatest, EMPTY, map, switchMap } from 'rxjs'; import { Store } from '@ngrx/store'; import { BENCHMARKS_WALLETS_CLOSE, @@ -11,8 +11,11 @@ import { BENCHMARKS_WALLETS_GET_WALLETS, BENCHMARKS_WALLETS_GET_WALLETS_SUCCESS, BENCHMARKS_WALLETS_SEND_TX_SUCCESS, - BENCHMARKS_WALLETS_SEND_TXS, BENCHMARKS_WALLETS_SEND_ZKAPPS, BENCHMARKS_WALLETS_SEND_ZKAPPS_SUCCESS, - BenchmarksWalletsActions, BenchmarksWalletsClose, + BENCHMARKS_WALLETS_SEND_TXS, + BENCHMARKS_WALLETS_SEND_ZKAPPS, + BENCHMARKS_WALLETS_SEND_ZKAPPS_SUCCESS, + BenchmarksWalletsActions, + BenchmarksWalletsClose, BenchmarksWalletsGetWallets, BenchmarksWalletsSendTxs, } from '@benchmarks/wallets/benchmarks-wallets.actions'; @@ -74,7 +77,7 @@ export class BenchmarksWalletsEffects extends MinaRustBaseEffect this.actions$.pipe( ofType(BENCHMARKS_WALLETS_GET_ALL_TXS), switchMap(() => - forkJoin([ + combineLatest([ this.mempoolService.getTransactionPool(), this.benchmarksService.getAllIncludedTransactions(), ]), diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.html b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.html index ee26f0da36..811b8e7310 100644 --- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.html +++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.html @@ -1,4 +1,4 @@ -@if (isPending || nodeIsBootstrapping || isCalculatingVRF || isLoading) { +@if ((isPending || nodeIsBootstrapping || isCalculatingVRF || isLoading) && emptySlots) {
diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.ts b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.ts index dc098d8085..15951b189e 100644 --- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.ts +++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.ts @@ -38,7 +38,7 @@ export class BlockProductionWonSlotsComponent extends StoreDispatcher implements total: number; }; epoch: number; - emptySlots: boolean = false; + emptySlots: boolean = true; isLoading: boolean = true; constructor(protected el: ElementRef) { super(); } @@ -82,8 +82,9 @@ export class BlockProductionWonSlotsComponent extends StoreDispatcher implements private listenToActiveEpoch(): void { this.select(BlockProductionWonSlotsSelectors.epoch, (activeEpoch) => { - this.epoch = activeEpoch.epochNumber; + this.epoch = activeEpoch?.epochNumber; this.vrfStats = activeEpoch.vrfStats; + this.isCalculatingVRF = activeEpoch.vrfStats?.evaluated < activeEpoch.vrfStats?.total; this.detect(); }); } diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.service.ts b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.service.ts index c7f0039640..fa83219e63 100644 --- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.service.ts +++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.service.ts @@ -26,7 +26,7 @@ export class BlockProductionWonSlotsService { } const attemptsSlots = response.attempts.map((attempt: Attempt) => { attempt.won_slot.slot_time = Math.floor(attempt.won_slot.slot_time / ONE_MILLION); // converted to milliseconds - attempt.active = BlockProductionWonSlotsService.getActive(attempt); + attempt.active = this.getActive(attempt); let slot = { epoch: attempt.won_slot.epoch, message: this.getMessage(attempt), @@ -114,7 +114,7 @@ export class BlockProductionWonSlotsService { ); } - private static getActive(attempt: Attempt): boolean { + private getActive(attempt: Attempt): boolean { const slotTime = attempt.won_slot.slot_time; const now = Date.now(); return slotTime <= now && (now < 3 * 60 * 1000 + slotTime) && !attempt.times?.discarded; diff --git a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.ts b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.ts index 8f972c2684..465b5bdbd4 100644 --- a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.ts +++ b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.ts @@ -5,6 +5,7 @@ import { NodesOverviewNode } from '@shared/types/nodes/dashboard/nodes-overview- import { NodesOverviewNodeBlockStatus } from '@shared/types/nodes/dashboard/nodes-overview-block.type'; import { isDesktop, lastItem, ONE_MILLION } from '@openmina/shared'; import { DashboardPeer } from '@shared/types/dashboard/dashboard.peer'; +import { SentryService } from '@core/services/sentry.service'; const PENDING = 'Pending'; const SYNCED = 'Synced'; @@ -30,6 +31,10 @@ export class DashboardBlocksSyncComponent extends StoreDispatcher implements OnI isDesktop: boolean = isDesktop(); remaining: number; + private syncStartTime: number = Date.now(); + + constructor(private sentryService: SentryService) {super();} + ngOnInit(): void { this.listenToNodesChanges(); } @@ -76,6 +81,8 @@ export class DashboardBlocksSyncComponent extends StoreDispatcher implements OnI this.extractNodesData(nodes); this.extractPeersData(peers); + + this.sentryService.updateBlockSyncStatus(nodes[0].blocks, this.syncStartTime); } this.detect(); }); @@ -115,8 +122,8 @@ export class DashboardBlocksSyncComponent extends StoreDispatcher implements OnI this.fetched = blocks.filter(b => ![NodesOverviewNodeBlockStatus.MISSING, NodesOverviewNodeBlockStatus.FETCHING].includes(b.status)).length; this.applied = blocks.filter(b => b.status === NodesOverviewNodeBlockStatus.APPLIED).length; - this.fetchedPercentage = Math.round(this.fetched * 100 / 291) + '%'; - this.appliedPercentage = Math.round(this.applied * 100 / 291); + this.fetchedPercentage = Math.round(this.fetched * 100 / 290) + '%'; + this.appliedPercentage = Math.round(this.applied * 100 / 290); } private calculateProgressTime(timestamp: number): string { diff --git a/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.ts b/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.ts index a18e20923c..b0c46c1526 100644 --- a/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.ts +++ b/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.ts @@ -14,6 +14,7 @@ import { TemplatePortal } from '@angular/cdk/portal'; import { DashboardRpcStats } from '@shared/types/dashboard/dashboard-rpc-stats.type'; import { AppSelectors } from '@app/app.state'; import { MinaNode } from '@shared/types/core/environment/mina-env.type'; +import { SentryService } from '@core/services/sentry.service'; type LedgerConfigMap = { stakingEpoch: SecDurationConfig, @@ -80,11 +81,14 @@ export class DashboardLedgerComponent extends StoreDispatcher implements OnInit, totalProgress: number; isWebNode: boolean; + private startSync: number = Date.now(); + @ViewChild('tooltipRef') private tooltipRef: TemplateRef<{ start: number, end: number }>; private overlayRef: OverlayRef; constructor(private overlay: Overlay, - private viewContainerRef: ViewContainerRef) { + private viewContainerRef: ViewContainerRef, + private sentryService: SentryService) { super(); } @@ -229,6 +233,8 @@ export class DashboardLedgerComponent extends StoreDispatcher implements OnInit, this.rootStagedProgress = 100; } this.totalProgress = (this.stakingProgress + this.nextProgress + this.rootSnarkedProgress + this.rootStagedProgress) / 4; + + this.sentryService.updateLedgerSyncStatus(this.ledgers); } this.detect(); }); @@ -272,9 +278,7 @@ export class DashboardLedgerComponent extends StoreDispatcher implements OnInit, } hide(): void { - if (this.overlayRef?.hasAttached()) { - this.overlayRef.detach(); - } + this.overlayRef?.dispose(); } private get emptyConfig(): SecDurationConfig { @@ -294,11 +298,6 @@ export class DashboardLedgerComponent extends StoreDispatcher implements OnInit, }; } - override ngOnDestroy(): void { - super.ngOnDestroy(); - this.hide(); - } - private setProgressTime(): void { if (!this.ledgers.stakingEpoch.snarked.fetchHashesStart) { return; @@ -327,4 +326,9 @@ export class DashboardLedgerComponent extends StoreDispatcher implements OnInit, return `${action} <1m ago`; } } + + override ngOnDestroy(): void { + super.ngOnDestroy(); + this.hide(); + } } diff --git a/frontend/src/app/features/dashboard/dashboard.component.ts b/frontend/src/app/features/dashboard/dashboard.component.ts index edd17d58f3..1ff872cf57 100644 --- a/frontend/src/app/features/dashboard/dashboard.component.ts +++ b/frontend/src/app/features/dashboard/dashboard.component.ts @@ -31,7 +31,6 @@ export class DashboardComponent extends StoreDispatcher implements OnInit, OnDes timer: Subscription; lastStatus: AppNodeStatus; - ngOnInit(): void { // this.document.getElementById('mina-content').style.borderTopLeftRadius = '0'; this.updateAction = 'Connecting to peers'; diff --git a/frontend/src/app/features/mempool/mempool.service.ts b/frontend/src/app/features/mempool/mempool.service.ts index fba1975c0b..94f302a8fb 100644 --- a/frontend/src/app/features/mempool/mempool.service.ts +++ b/frontend/src/app/features/mempool/mempool.service.ts @@ -31,7 +31,7 @@ export class MempoolService { const memo = decodeMemo(tx.data[1].payload.common.memo); return { kind: MempoolTransactionKind.PAYMENT, - txHash: tx.hash.join(''), + txHash: tx.hash, sender: tx.data[1].payload.common.fee_payer_pk, fee: Number(tx.data[1].payload.common.fee), amount: Number(tx.data[1].payload.body[1].amount) / ONE_BILLION, @@ -46,7 +46,7 @@ export class MempoolService { const zkMemo = decodeMemo(zkapp.memo); return { kind: MempoolTransactionKind.ZK_APP, - txHash: tx.hash.join(''), + txHash: tx.hash, sender: zkapp.fee_payer.body.public_key, fee: Number(zkapp.fee_payer.body.fee), amount: null, @@ -64,7 +64,7 @@ export class MempoolService { export interface MempoolTransactionResponse { data: [MempoolTransactionResponseKind, SignedCommand | ZkappCommand]; - hash: number[]; + hash: string; } export enum MempoolTransactionResponseKind { diff --git a/frontend/src/app/layout/parse-files/parse-files.component.html b/frontend/src/app/layout/parse-files/parse-files.component.html new file mode 100644 index 0000000000..ad62091962 --- /dev/null +++ b/frontend/src/app/layout/parse-files/parse-files.component.html @@ -0,0 +1,12 @@ +

parse-files works!

+ + +
+
+

File Contents:

+
{{ fileContent }}
+
+
diff --git a/frontend/src/app/layout/parse-files/parse-files.component.scss b/frontend/src/app/layout/parse-files/parse-files.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/app/layout/parse-files/parse-files.component.ts b/frontend/src/app/layout/parse-files/parse-files.component.ts new file mode 100644 index 0000000000..d8a7725887 --- /dev/null +++ b/frontend/src/app/layout/parse-files/parse-files.component.ts @@ -0,0 +1,112 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { NgFor, NgIf } from '@angular/common'; +import * as JSZip from 'jszip'; + +@Component({ + selector: 'mina-parse-files', + standalone: true, + imports: [ + NgIf, NgFor, + ], + templateUrl: './parse-files.component.html', + styleUrl: './parse-files.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ParseFilesComponent { + + fileContents: string[] = []; + + onFileSelected(event: any) { + const files: FileList = event.target.files; + + // Reset previous file contents + this.fileContents = []; + + // Loop through selected files + for (let i = 0; i < files.length; i++) { + const file = files[i]; + + // Ensure it's a .txt file + } + this.handleFileUpload(event); + } + + async processZipFile(zipFile: File) { + try { + // Load the ZIP file + const zip = await JSZip.loadAsync(zipFile); + + // Array to store file contents + const fileContents: { name: string, content: string }[] = []; + + // Iterate through each file in the ZIP + await Promise.all(Object.keys(zip.files).map(async (filename) => { + // Skip directories + if (!zip.files[filename].dir) { + try { + // Read file as text + const content = await zip.files[filename].async('string'); + fileContents.push({ + name: filename, + content: content, + }); + } catch (readError) { + console.error(`Error reading file ${filename}:`, readError); + } + } + })); + + // Return or process the file contents + return fileContents; + } catch (error) { + console.error('Error processing ZIP file:', error); + return []; + } + } + +// Usage example + handleFileUpload(event: Event) { + const input = event.target as HTMLInputElement; + if (input.files && input.files.length > 0) { + const zipFile = input.files[0]; + this.processZipFile(zipFile).then(files => { + files.forEach(file => { + console.log(`File: ${file.name}`); + console.log(`Content: ${file.content.substring(0, 200)}...`); + }); + }); + } + } + + private readFileContent(file: File) { + const reader = new FileReader(); + + reader.onload = (e: any) => { + const content = e.target.result as string; + this.fileContents.push(content); + + // Perform operations on the content here + this.processFileContent(content); + }; + + reader.onerror = (e) => { + console.error('Error reading file', e); + }; + + // Read the file as text + reader.readAsText(file); + } + + private processFileContent(content: string) { + // Example operations + const lines = content.split('\n'); + const wordCount = content.split(/\s+/).length; + const characterCount = content.length; + + console.log('Lines:', lines); + console.log('Word Count:', wordCount); + console.log('Character Count:', characterCount); + + // Add your specific file processing logic here + } +} diff --git a/frontend/src/app/layout/server-status/server-status.component.html b/frontend/src/app/layout/server-status/server-status.component.html index 5ebfaf93a3..6b727bb5c2 100644 --- a/frontend/src/app/layout/server-status/server-status.component.html +++ b/frontend/src/app/layout/server-status/server-status.component.html @@ -2,7 +2,7 @@ *ngIf="details" [ngClass]="switchForbidden ? AppNodeStatus.PENDING : details.status"> -
@@ -10,7 +10,7 @@
{{ details.transactions }} Tx{{ details.transactions | plural }}
{{ details.snarks }} SNARK{{ details.snarks | plural }}
-
@@ -18,7 +18,7 @@
{{ details.peersConnected }} Peer{{ details.peersConnected | plural }}
-
diff --git a/frontend/src/app/layout/server-status/server-status.component.scss b/frontend/src/app/layout/server-status/server-status.component.scss index ce0fe36beb..a3de40e2d0 100644 --- a/frontend/src/app/layout/server-status/server-status.component.scss +++ b/frontend/src/app/layout/server-status/server-status.component.scss @@ -100,7 +100,6 @@ .chip { gap: 4px; - margin-left: 4px; } @media (max-width: 767px) { diff --git a/frontend/src/app/layout/web-node-landing-page/web-node-landing-page.component.html b/frontend/src/app/layout/web-node-landing-page/web-node-landing-page.component.html index 6263563da9..4214b1508e 100644 --- a/frontend/src/app/layout/web-node-landing-page/web-node-landing-page.component.html +++ b/frontend/src/app/layout/web-node-landing-page/web-node-landing-page.component.html @@ -30,6 +30,7 @@

The private key is pre-set, and the stake is delegated to it.

+
diff --git a/frontend/src/app/layout/web-node-landing-page/web-node-landing-page.component.ts b/frontend/src/app/layout/web-node-landing-page/web-node-landing-page.component.ts index f8cd2143ec..0c56241e53 100644 --- a/frontend/src/app/layout/web-node-landing-page/web-node-landing-page.component.ts +++ b/frontend/src/app/layout/web-node-landing-page/web-node-landing-page.component.ts @@ -3,12 +3,14 @@ import { NgOptimizedImage } from '@angular/common'; import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class'; import { AppSelectors } from '@app/app.state'; import { filter } from 'rxjs'; +import { ParseFilesComponent } from '@app/layout/parse-files/parse-files.component'; @Component({ selector: 'mina-web-node-landing-page', standalone: true, imports: [ NgOptimizedImage, + ParseFilesComponent, ], templateUrl: './web-node-landing-page.component.html', styleUrl: './web-node-landing-page.component.scss', diff --git a/frontend/src/app/shared/helpers/date.helper.ts b/frontend/src/app/shared/helpers/date.helper.ts index e481923184..84289d0a8a 100644 --- a/frontend/src/app/shared/helpers/date.helper.ts +++ b/frontend/src/app/shared/helpers/date.helper.ts @@ -39,11 +39,23 @@ export function getTimeDiff(time: number, config?: { withSecs?: boolean, only1un if (config?.only1unit) { if (days > 0) { - timeAgo += `${days}d `; + if (hours >= 12) { + timeAgo += `<${days + 1}d `; + } else { + timeAgo += `~${days}d `; + } } else if (hours > 0) { - timeAgo += `${hours}h `; + if (minutes >= 30) { + timeAgo += `<${hours + 1}h `; + } else { + timeAgo += `~${hours}h `; + } } else if (minutes > 0) { - timeAgo += `${minutes}m `; + if (seconds >= 30) { + timeAgo += `<${minutes + 1}m `; + } else { + timeAgo += `~${minutes}m `; + } } else { if (config?.withSecs) { timeAgo += `${seconds}s `; @@ -53,6 +65,7 @@ export function getTimeDiff(time: number, config?: { withSecs?: boolean, only1un } return { diff: timeAgo.trim(), inFuture }; } + if (days > 0) { timeAgo += `${days}d `; } diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts index 8737585db9..a4fcf27a89 100644 --- a/frontend/src/environments/environment.ts +++ b/frontend/src/environments/environment.ts @@ -39,10 +39,10 @@ export const environment: Readonly = { // name: 'Producer-2', // url: 'https://staging-devnet-openmina-bp-2-dashboard.minaprotocol.network', // }, - { - name: 'staging-devnet-bp-0', - url: 'https://staging-devnet-openmina-bp-0.minaprotocol.network', - }, + // { + // name: 'staging-devnet-bp-0', + // url: 'https://staging-devnet-openmina-bp-0.minaprotocol.network', + // }, // { // name: 'staging-devnet-bp-1', // url: 'https://staging-devnet-openmina-bp-1.minaprotocol.network', @@ -55,18 +55,18 @@ export const environment: Readonly = { // name: 'staging-devnet-bp-3', // url: 'https://staging-devnet-openmina-bp-3.minaprotocol.network', // }, - // { - // name: 'Web Node 1', - // isWebNode: true, - // }, + { + name: 'Web Node 1', + isWebNode: true, + }, // { // name: 'http://65.109.105.40:3000', // url: 'http://65.109.105.40:3000', // }, - { - name: 'Local rust node', - url: 'http://127.0.0.1:3000', - }, + // { + // name: 'Local rust node', + // url: 'http://127.0.0.1:3000', + // }, // { // name: 'feat/frontend-api-peers', // url: 'http://176.9.147.28:3000', diff --git a/frontend/src/index.html b/frontend/src/index.html index a1d71b7fda..2cbd41a14a 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -49,11 +49,11 @@