diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.scss b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.scss
index 76009ed69f..c1d2d3ea65 100644
--- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.scss
+++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.scss
@@ -1,3 +1,14 @@
-mina-block-production-won-slots-cards + div {
+@media (max-width: 767px) {
+ :host ::ng-deep mina-horizontal-resizable-container aside {
+ right: unset !important;
+ transition: left .4s cubic-bezier(.22, 1, .36, 1) !important;
+ &.in-view {
+ left: 0;
+ }
+
+ &:not(.in-view) {
+ left: -100%;
+ }
+ }
}
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 666a02d44d..81d78072d0 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
@@ -16,6 +16,7 @@ import { BlockProductionWonSlotsSelectors } from '@block-production/won-slots/bl
export class BlockProductionWonSlotsComponent extends StoreDispatcher implements OnInit, OnDestroy {
showSidePanel: boolean = isDesktop();
+ isDesktop: boolean = isDesktop();
constructor(protected el: ElementRef) { super(); }
diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.effects.ts b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.effects.ts
index 0cd95a3eab..bbccec2c35 100644
--- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.effects.ts
+++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.effects.ts
@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { MinaState, selectMinaState } from '@app/app.setup';
import { Actions, createEffect, ofType } from '@ngrx/effects';
-import { Effect, isDesktop } from '@openmina/shared';
+import { Effect, isDesktop, isMobile } from '@openmina/shared';
import { EMPTY, map, switchMap } from 'rxjs';
import { catchErrorAndRepeat2 } from '@shared/constants/store-functions';
import { MinaErrorType } from '@shared/types/error-preview/mina-error-type.enum';
@@ -9,13 +9,8 @@ import { Store } from '@ngrx/store';
import { BaseEffect } from '@shared/base-classes/mina-rust-base.effect';
import { BlockProductionModule } from '@block-production/block-production.module';
import { BlockProductionWonSlotsService } from '@block-production/won-slots/block-production-won-slots.service';
-import {
- BLOCK_PRODUCTION_WON_SLOTS_KEY,
- BlockProductionWonSlotsActions,
-} from '@block-production/won-slots/block-production-won-slots.actions';
-import {
- BlockProductionWonSlotsStatus,
-} from '@shared/types/block-production/won-slots/block-production-won-slots-slot.type';
+import { BLOCK_PRODUCTION_WON_SLOTS_KEY, BlockProductionWonSlotsActions } from '@block-production/won-slots/block-production-won-slots.actions';
+import { BlockProductionWonSlotsStatus } from '@shared/types/block-production/won-slots/block-production-won-slots-slot.type';
import { Router } from '@angular/router';
import { Routes } from '@shared/enums/routes.enum';
import { fromPromise } from 'rxjs/internal/observable/innerFrom';
@@ -51,11 +46,15 @@ export class BlockProductionWonSlotsEffects extends BaseEffect {
const bpState = state.blockProduction[BLOCK_PRODUCTION_WON_SLOTS_KEY];
const activeSlotRoute = bpState.activeSlotRoute;
let newActiveSlot = slots.find(s => s.globalSlot.toString() === activeSlotRoute);
- if (!activeSlotRoute || (activeSlotRoute && !newActiveSlot)) {
+ if (
+ (isDesktop() && !activeSlotRoute)
+ || (activeSlotRoute && !newActiveSlot)
+ || (isMobile() && !activeSlotRoute && bpState.slots.length === 0)
+ ) {
newActiveSlot = slots.find(s => s.active)
?? slots.find(s => s.status === BlockProductionWonSlotsStatus.Committed)
?? slots.find(s => s.status === BlockProductionWonSlotsStatus.Scheduled)
- ?? null;
+ ?? slots.find(s => !s.status);
}
const routes: string[] = [Routes.BLOCK_PRODUCTION, Routes.WON_SLOTS];
if (
diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.module.ts b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.module.ts
index df36ef612c..ce1b55df9c 100644
--- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.module.ts
+++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.module.ts
@@ -20,6 +20,7 @@ import {
BlockProductionWonSlotsCardsComponent,
} from '@block-production/won-slots/cards/block-production-won-slots-cards.component';
import { SharedModule } from '@shared/shared.module';
+import { BlockProductionWonSlotsEpochComponent } from '@block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component';
@NgModule({
@@ -29,6 +30,7 @@ import { SharedModule } from '@shared/shared.module';
BlockProductionWonSlotsSidePanelComponent,
BlockProductionWonSlotsFiltersComponent,
BlockProductionWonSlotsCardsComponent,
+ BlockProductionWonSlotsEpochComponent,
],
imports: [
CommonModule,
diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.reducer.ts b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.reducer.ts
index 63fd852440..d18bc61073 100644
--- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.reducer.ts
+++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.reducer.ts
@@ -59,8 +59,8 @@ export const blockProductionWonSlotsReducer = createReducer(
on(BlockProductionWonSlotsActions.toggleSidePanel, state => ({
...state,
openSidePanel: !state.openSidePanel,
- activeSlot: !state.openSidePanel ? state.activeSlot : undefined,
- activeSlotRoute: !state.openSidePanel ? state.activeSlotRoute : undefined,
+ activeSlot: state.openSidePanel ? undefined : state.activeSlot,
+ activeSlotRoute: state.openSidePanel ? undefined : state.activeSlotRoute,
})),
on(BlockProductionWonSlotsActions.close, () => initialState),
);
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 3fd23cc33b..f99d53354d 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
@@ -5,13 +5,10 @@ import {
BlockProductionWonSlotsSlot,
BlockProductionWonSlotsStatus,
} from '@shared/types/block-production/won-slots/block-production-won-slots-slot.type';
-import { BlockProductionModule } from '@block-production/block-production.module';
-import { hasValue, nanOrElse, ONE_BILLION, ONE_MILLION } from '@openmina/shared';
+import { hasValue, isDesktop, nanOrElse, ONE_BILLION, ONE_MILLION } from '@openmina/shared';
import { getTimeDiff } from '@shared/helpers/date.helper';
import { RustService } from '@core/services/rust.service';
-import {
- BlockProductionWonSlotsEpoch,
-} from '@shared/types/block-production/won-slots/block-production-won-slots-epoch.type';
+import { BlockProductionWonSlotsEpoch } from '@shared/types/block-production/won-slots/block-production-won-slots-epoch.type';
@Injectable({
providedIn: 'root',
@@ -90,7 +87,7 @@ export class BlockProductionWonSlotsService {
const futureWonSlots = response.future_won_slots.map((slot: WonSlot) => {
slot.slot_time = Math.floor(slot.slot_time / ONE_MILLION);
return {
- message: 'Upcoming Won Slot',
+ message: this.getMessage({ won_slot: slot } as Attempt),
age: this.calculateTimeAgo({ won_slot: slot }),
slotTime: slot.slot_time,
globalSlot: slot.global_slot,
@@ -120,10 +117,10 @@ export class BlockProductionWonSlotsService {
private getMessage(attempt: Attempt): string {
if (attempt.active) {
- return 'Produced';
+ return 'Producing';
}
if (attempt.status === BlockProductionWonSlotsStatus.Scheduled) {
- return 'Production Scheduled';
+ return (isDesktop() ? 'Production ' : '') + 'Scheduled';
} else if (attempt.status === BlockProductionWonSlotsStatus.Canonical) {
return 'Produced Block';
} else if (attempt.status === BlockProductionWonSlotsStatus.Orphaned) {
@@ -131,9 +128,9 @@ export class BlockProductionWonSlotsService {
} else if (attempt.status === BlockProductionWonSlotsStatus.Discarded) {
return BlockProductionWonSlotsStatus.Discarded + ' Block';
} else if (attempt.status === BlockProductionWonSlotsStatus.Committed) {
- return 'Waiting for Confirmation';
+ return isDesktop() ? 'Waiting for Confirmation' : 'Confirming';
}
- return 'Upcoming Won Slot';
+ return (isDesktop() ? 'Upcoming ' : '') + 'Won Slot';
}
private calculateTimeAgo({ active, won_slot }: { active?: boolean; won_slot: WonSlot }): string {
diff --git a/frontend/src/app/features/block-production/won-slots/cards/block-production-won-slots-cards.component.html b/frontend/src/app/features/block-production/won-slots/cards/block-production-won-slots-cards.component.html
index 37d961d2b3..0e8049dc34 100644
--- a/frontend/src/app/features/block-production/won-slots/cards/block-production-won-slots-cards.component.html
+++ b/frontend/src/app/features/block-production/won-slots/cards/block-production-won-slots-cards.component.html
@@ -2,37 +2,30 @@
-
{
- const epochStartTime = this.addMinutesToTimestamp(Math.floor(epoch.currentTime / ONE_BILLION), -(epoch.currentGlobalSlot - epoch.start) * 3);
- this.card1.startedAgo = getTimeDiff(Math.floor(epochStartTime * ONE_THOUSAND)).diff;
-
const epochEndTime = this.addMinutesToTimestamp(epoch.currentTime / ONE_BILLION, (epoch.end - epoch.currentGlobalSlot) * 3);
- this.card5.endIn = getTimeDiff(epochEndTime * ONE_THOUSAND).diff;
- this.card5.epochProgress = Math.floor((epoch.currentGlobalSlot - epoch.start) / (epoch.end - epoch.start) * 100) + '%';
+ this.card4.endIn = getTimeDiff(epochEndTime * ONE_THOUSAND).diff;
+ this.card4.epochProgress = Math.floor((epoch.currentGlobalSlot - epoch.start) / (epoch.end - epoch.start) * 100) + '%';
this.detect();
}, filter(Boolean));
@@ -49,29 +45,28 @@ export class BlockProductionWonSlotsCardsComponent extends StoreDispatcher imple
private listenToSlots(): void {
this.select(BlockProductionWonSlotsSelectors.slots, (slots: BlockProductionWonSlotsSlot[]) => {
- this.card1.epoch = slots[0].epoch;
const nextSlot = slots.find(s => s.status === BlockProductionWonSlotsStatus.Scheduled || !s.status);
if (nextSlot) {
- this.card2.nextWonSlot = getTimeDiff(nextSlot.slotTime).diff;
- this.card2.slot = nextSlot.globalSlot;
+ this.card1.nextWonSlot = getTimeDiff(nextSlot.slotTime).diff;
+ this.card1.slot = nextSlot.globalSlot;
} else {
- this.card2.nextWonSlot = 'Now';
- this.card2.slot = slots.find(s => s.active)?.globalSlot;
+ this.card1.nextWonSlot = 'Now';
+ this.card1.slot = slots.find(s => s.active)?.globalSlot;
}
- this.card3.wonSlots = slots.length;
- this.card3.slotsUsed = slots.filter(
+ this.card2.wonSlots = slots.length;
+ this.card2.slotsUsed = slots.filter(
s => [BlockProductionWonSlotsStatus.Canonical, BlockProductionWonSlotsStatus.Orphaned, BlockProductionWonSlotsStatus.Discarded].includes(s.status),
).length;
- this.card4.acceptedBlocks = slots.filter(s => s.status === BlockProductionWonSlotsStatus.Canonical).length;
- this.card4.lastBlockTime = getTimeDiff(lastItem(slots.filter(s => s.status === BlockProductionWonSlotsStatus.Canonical))?.slotTime).diff;
+ this.card3.acceptedBlocks = slots.filter(s => s.status === BlockProductionWonSlotsStatus.Canonical).length;
+ this.card3.lastBlockTime = getTimeDiff(lastItem(slots.filter(s => s.status === BlockProductionWonSlotsStatus.Canonical))?.slotTime).diff;
- this.card6.totalRewards = slots
+ this.card5.totalRewards = slots
.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;
+ this.card5.totalRewards = isNaN(+this.card5.totalRewards) ? '0' : this.card5.totalRewards;
this.detect();
}, filter(slots => slots.length > 0));
}
diff --git a/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.html b/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.html
index 4c48efdc23..dc6ca90815 100644
--- a/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.html
+++ b/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.html
@@ -1,13 +1,19 @@
-
-
{{ title }}
+
+
+ @if (isMobile) {
+
+ }
+
{{ title }}
+
@if (percentage !== null && percentage !== undefined) {
{{ percentage }}%
}
- @if (isMobile) {
-
close
- }
0" [style.width.%]="percentage"
class="progress-bar p-absolute">
@@ -15,7 +21,7 @@
-
+
Global slot
diff --git a/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.scss b/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.scss
index d2b6dceb15..bb189a824f 100644
--- a/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.scss
+++ b/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.scss
@@ -1,10 +1,23 @@
@import 'openmina';
+$blue: #57d7ff;
+$pink: #fda2ff;
+$orange: #ff833d;
+
+:host {
+ //linear-gradient(64deg, rgba(87, 215, 255, 0.15) 10%, rgba(253, 162, 255, 0.15) 60%, rgba(255, 131, 61, 0.15) 82%)
+ background: linear-gradient(70deg, rgba($blue, .15) 5%, rgba($pink, .15) 45%, rgba($orange, .15) 90%);
+}
+
+.top {
+ border-bottom: 2px solid transparent;
+}
+
.progress-bar {
overflow: hidden;
height: 6px;
left: 0;
- bottom: 0;
+ bottom: -3px;
transition: width 0.4s ease-in-out;
min-width: 20px;
diff --git a/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.ts b/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.ts
index de74fe2848..486440ef00 100644
--- a/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.ts
+++ b/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.ts
@@ -7,7 +7,7 @@ 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, isMobile, noMillisFormat, ONE_THOUSAND, safelyExecuteInBrowser, SecDurationConfig, toReadableDate } from '@openmina/shared';
+import { any, hasValue, isDesktop, isMobile, noMillisFormat, ONE_THOUSAND, safelyExecuteInBrowser, 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';
@@ -20,6 +20,7 @@ import { Routes } from '@shared/enums/routes.enum';
templateUrl: './block-production-won-slots-side-panel.component.html',
styleUrls: ['./block-production-won-slots-side-panel.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
+ host: { class: 'flex-column h-100' },
})
export class BlockProductionWonSlotsSidePanelComponent extends StoreDispatcher implements OnInit, OnDestroy {
@@ -194,4 +195,5 @@ export class BlockProductionWonSlotsSidePanelComponent extends StoreDispatcher i
super.ngOnDestroy();
clearInterval(this.timer);
}
+
}
diff --git a/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.html b/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.html
index 3e42122963..1c4d6d44ca 100644
--- a/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.html
+++ b/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.html
@@ -6,7 +6,7 @@
*ngIf="row.active || row.status === BlockProductionWonSlotsStatus.Committed; else icon">
schedule
+ class="schedule mina-icon icon-200 f-20">schedule
circle
diff --git a/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.scss b/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.scss
index 25d6f79317..f8fc4575fc 100644
--- a/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.scss
+++ b/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.scss
@@ -1,5 +1,9 @@
@import 'openmina';
+$blue: #57d7ff;
+$pink: #fda2ff;
+$orange: #ff833d;
+
.mina-icon.f-big {
font-variation-settings: 'FILL' 1, 'wght' 400 !important;
}
@@ -36,6 +40,21 @@
border-color: $selected-primary !important;
border-top-color: transparent !important;
}
+
+ .schedule,
+ .Scheduled,
+ .Producing {
+ background: linear-gradient(5deg, $blue 30%, $pink 55%, $orange 100%);
+ -webkit-background-clip: text;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+ }
+
+ .schedule {
+ @media (max-width: 767px) {
+ font-size: 16px !important;
+ }
+ }
}
.fx-row-vert-cent {
diff --git a/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.ts b/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.ts
index 0334986ccb..4f23b56c9c 100644
--- a/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.ts
+++ b/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.ts
@@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { MinaTableRustWrapper } from '@shared/base-classes/mina-table-rust-wrapper.class';
-import { getMergedRoute, MergedRoute, SecDurationConfig, TableColumnList } from '@openmina/shared';
+import { getMergedRoute, isDesktop, MergedRoute, SecDurationConfig, TableColumnList } from '@openmina/shared';
import { Router } from '@angular/router';
import { SnarksWorkPoolToggleSidePanel } from '@snarks/work-pool/snarks-work-pool.actions';
import { filter, take } from 'rxjs';
@@ -57,7 +57,7 @@ export class BlockProductionWonSlotsTableComponent extends MinaTableRustWrapper<
}
protected override setupTable(): void {
- this.table.gridTemplateColumns = [210, 140, 90, 140, 120, 120, 120, 120, 150, 150];
+ this.table.gridTemplateColumns = isDesktop() ? [210, 140, 90, 142, 120, 120, 120, 124, 159, 150] : [150, 100, 90, 130, 100, 105, 100, 114, 139, 130];
this.table.propertyForActiveCheck = 'globalSlot';
this.table.thGroupsTemplate = this.thGroupsTemplate;
this.table.sortAction = BlockProductionWonSlotsActions.sort;
@@ -96,7 +96,7 @@ export class BlockProductionWonSlotsTableComponent extends MinaTableRustWrapper<
this.table.activeRow = slot;
this.table.detect();
this.detect();
- }, filter(Boolean));
+ });
}
private scrollToElement(): void {
@@ -107,11 +107,11 @@ export class BlockProductionWonSlotsTableComponent extends MinaTableRustWrapper<
this.onRowClick(this.table.rows[i]);
}
- protected override onRowClick(slot: BlockProductionWonSlotsSlot): void {
+ protected override onRowClick(slot: BlockProductionWonSlotsSlot, isRealClick?: boolean): void {
if (!slot) {
return;
}
- if (this.table.activeRow?.globalSlot !== slot?.globalSlot) {
+ if (this.table.activeRow?.globalSlot !== slot?.globalSlot || isRealClick) {
this.dispatch2(BlockProductionWonSlotsActions.setActiveSlot({ slot }));
this.router.navigate([Routes.BLOCK_PRODUCTION, Routes.WON_SLOTS, slot.globalSlot], { queryParamsHandling: 'merge' });
}
diff --git a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.html b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.html
index 46037f53df..b8ec53bcfe 100644
--- a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.html
+++ b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.html
@@ -1,38 +1,46 @@
-
-
-
Last 290 blocks
-
{{ syncProgress }}
-
-
-
+
+
+
+ @if (appliedPercentage === 100) {
+
check_circle
+ } @else {
+
+ }
+
Last 290 blocks
+
{{ syncProgress }}
-
+
-
+ @if (isDesktop) {
+
+ }
-
+ @if (isDesktop) {
+
+ }
diff --git a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.scss b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.scss
index 51d9324e02..c5bb8daa6e 100644
--- a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.scss
+++ b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.scss
@@ -1,42 +1,9 @@
-@import 'openmina';
-
-.progress-bar {
- overflow: hidden;
- height: 6px;
- left: 0;
- bottom: 0;
- transition: width 0.6s ease-in-out;
- min-width: 20px;
-
- .highlight {
- height: 4px;
- top: 1px;
- left: -50px;
- width: 50px;
- background: radial-gradient($success-primary, $success-primary, $success-primary, $success-primary, transparent, transparent);
- border-radius: 20px;
- animation: move 9s linear infinite;
- }
-
- .progress {
- height: 2px;
- background-color: $success-primary;
- top: 2px;
- left: 0;
- }
+.mina-icon {
+ font-variation-settings: 'FILL' 1, 'wght' 300 !important;
}
-@keyframes move {
- 0% {
- left: -50px;
- }
- 10% {
- left: -50px;
- }
- 80% {
- left: 100%;
- }
- 100% {
- left: 100%;
+@media (max-width: 767px) {
+ .mobile mina-card {
+ width: 33%;
}
}
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 bd29e3cb04..4caaf5090d 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
@@ -2,9 +2,8 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class';
import { selectDashboardNodesAndPeers } from '@dashboard/dashboard.state';
import { NodesOverviewNode } from '@shared/types/nodes/dashboard/nodes-overview-node.type';
-import { filter } from 'rxjs';
import { NodesOverviewNodeBlockStatus } from '@shared/types/nodes/dashboard/nodes-overview-block.type';
-import { lastItem, ONE_MILLION } from '@openmina/shared';
+import { isDesktop, lastItem, ONE_MILLION } from '@openmina/shared';
import { DashboardPeer } from '@shared/types/dashboard/dashboard.peer';
const PENDING = 'Pending';
@@ -28,6 +27,7 @@ export class DashboardBlocksSyncComponent extends StoreDispatcher implements OnI
bestTipBlockSyncedText: string = PENDING;
targetBlock: number;
syncProgress: string;
+ isDesktop: boolean = isDesktop();
ngOnInit(): void {
this.listenToNodesChanges();
diff --git a/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.html b/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.html
index def4089c6b..193765b65f 100644
--- a/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.html
+++ b/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.html
@@ -1,12 +1,14 @@
-
-
-
Ledgers
-
{{ progress }}
-
0" [style.width.%]="totalProgress"
- class="progress-bar p-absolute">
-
-
+
+
+
+ @if (totalProgress === 100) {
+
check_circle
+ } @else {
+
+ }
+
Ledgers
+
{{ progress }}
-
- Network
-
-
-
-
Peers
-
0"
- [style.width.%]="stats.connected === 0 ? 0 : 100"
- class="progress-bar p-absolute">
-
-
-
+
+
+ @if (stats.connected) {
+
check_circle
+ } @else {
+
+ }
+
Peers
-
+
-
-
-
- account_tree
-
-
-
-
diff --git a/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.scss b/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.scss
index 818da2c61d..6e1afba0ea 100644
--- a/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.scss
+++ b/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.scss
@@ -1,60 +1,3 @@
-@import 'openmina';
-
-:host {
- max-width: 384px;
-}
-
-.loading-icon {
- animation: spin 2s linear infinite;
-}
-
-@keyframes spin {
- 0% {
- transform: rotate(0);
- }
- 100% {
- transform: rotate(360deg);
- }
-}
-
-
-.progress-bar {
- overflow: hidden;
- height: 6px;
- left: 0;
- bottom: 0;
- transition: width 0.6s ease-in-out;
- min-width: 20px;
-
- .highlight {
- height: 4px;
- top: 1px;
- left: -50px;
- width: 50px;
- background: radial-gradient($success-primary, $success-primary, $success-primary, $success-primary, transparent, transparent);
- border-radius: 20px;
- animation: move 9s linear infinite;
- }
-
- .progress {
- height: 2px;
- background-color: $success-primary;
- top: 2px;
- left: 0;
- }
-}
-
-@keyframes move {
- 0% {
- left: -50px;
- }
- 10% {
- left: -50px;
- }
- 80% {
- left: 100%;
- }
- 100% {
- left: 100%;
- }
+.mina-icon {
+ font-variation-settings: 'FILL' 1, 'wght' 300 !important;
}
diff --git a/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.ts b/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.ts
index 3c8b63e204..ff47a7e17f 100644
--- a/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.ts
+++ b/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.ts
@@ -3,7 +3,6 @@ import { DashboardPeersStats } from '@shared/types/dashboard/dashboard-peers-sta
import { selectDashboardPeersStats } from '@dashboard/dashboard.state';
import { skip } from 'rxjs';
import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class';
-import { NodesOverviewLedgerStepState } from '@shared/types/nodes/dashboard/nodes-overview-ledger.type';
@Component({
selector: 'mina-dashboard-network',
diff --git a/frontend/src/app/features/dashboard/dashboard-peers-minimal-table/dashboard-peers-minimal-table.component.scss b/frontend/src/app/features/dashboard/dashboard-peers-minimal-table/dashboard-peers-minimal-table.component.scss
index bb3787a918..53e4ca2058 100644
--- a/frontend/src/app/features/dashboard/dashboard-peers-minimal-table/dashboard-peers-minimal-table.component.scss
+++ b/frontend/src/app/features/dashboard/dashboard-peers-minimal-table/dashboard-peers-minimal-table.component.scss
@@ -14,6 +14,7 @@
:host ::ng-deep cdk-virtual-scroll-viewport {
overflow-y: auto !important;
+ border-bottom: none !important;
}
.fx-row-vert-cent {
diff --git a/frontend/src/app/features/dashboard/dashboard.component.html b/frontend/src/app/features/dashboard/dashboard.component.html
index 97e54aa457..7e2a718d84 100644
--- a/frontend/src/app/features/dashboard/dashboard.component.html
+++ b/frontend/src/app/features/dashboard/dashboard.component.html
@@ -1,4 +1,18 @@
-
+
+
-
+
+
diff --git a/frontend/src/app/features/dashboard/dashboard.component.scss b/frontend/src/app/features/dashboard/dashboard.component.scss
index cd151fd9f7..ceee98e524 100644
--- a/frontend/src/app/features/dashboard/dashboard.component.scss
+++ b/frontend/src/app/features/dashboard/dashboard.component.scss
@@ -4,6 +4,93 @@
background-color: $base-background;
}
-div {
+.comps {
gap: 8px;
+ padding-right: 8px;
+
+ @media (max-width: 767px) {
+ padding: 0 8px;
+ }
+}
+
+mina-dashboard-network {
+ max-width: 384px;
+}
+
+mina-dashboard-ledger {
+ width: 100%;
+ max-width: 384px;
+ min-width: 250px;
+}
+
+mina-dashboard-blocks-sync {
+ width: 100%;
+ max-width: 640px;
+}
+
+@media (max-width: 767px) {
+ mina-dashboard-network,
+ mina-dashboard-ledger,
+ mina-dashboard-blocks-sync {
+ max-width: 100%;
+ }
+}
+
+.header {
+ transition: height .6s ease-in, opacity .4s .4s ease-in;
+
+ &.hide {
+ height: 0 !important;
+ opacity: 0;
+ }
+
+ @media (max-width: 767px) {
+ &.hide {
+ height: 8px !important;
+ }
+ .primary {
+ padding-left: 8px;
+ }
+ }
+}
+
+.progress-bar {
+ overflow: hidden;
+ height: 4px;
+ left: 0;
+ top: -2px;
+ transition: width 0.6s ease-in-out;
+ min-width: 20px;
+
+ .highlight {
+ height: 4px;
+ top: 1px;
+ left: -50px;
+ width: 50px;
+ background: radial-gradient($success-primary, $success-primary, $success-primary, $success-primary, transparent, transparent);
+ border-radius: 20px;
+ animation: move 9s linear infinite;
+ }
+
+ .progress {
+ height: 2px;
+ background-color: $success-primary;
+ top: 2px;
+ left: 0;
+ }
+}
+
+@keyframes move {
+ 0% {
+ left: -50px;
+ }
+ 10% {
+ left: -50px;
+ }
+ 80% {
+ left: 100%;
+ }
+ 100% {
+ left: 100%;
+ }
}
diff --git a/frontend/src/app/features/dashboard/dashboard.component.ts b/frontend/src/app/features/dashboard/dashboard.component.ts
index cc2dbaaf7e..bcb8fc4605 100644
--- a/frontend/src/app/features/dashboard/dashboard.component.ts
+++ b/frontend/src/app/features/dashboard/dashboard.component.ts
@@ -4,18 +4,43 @@ import { DashboardGetData, DashboardInit } from '@dashboard/dashboard.actions';
import { filter, skip, tap, timer } from 'rxjs';
import { untilDestroyed } from '@ngneat/until-destroy';
import { AppSelectors } from '@app/app.state';
+import { selectDashboardNodesAndPeers, selectDashboardNodesAndRpcStats, selectDashboardPeersStats } from '@dashboard/dashboard.state';
+import { DashboardPeersStats } from '@shared/types/dashboard/dashboard-peers-stats.type';
+import { NodesOverviewNode } from '@shared/types/nodes/dashboard/nodes-overview-node.type';
+import { DashboardRpcStats } from '@shared/types/dashboard/dashboard-rpc-stats.type';
+import { NodesOverviewLedgerStepState } from '@shared/types/nodes/dashboard/nodes-overview-ledger.type';
+import { lastItem, ONE_MILLION, SecDurationConfig } from '@openmina/shared';
+import { DashboardPeer } from '@shared/types/dashboard/dashboard.peer';
+import { NodesOverviewNodeBlockStatus } from '@shared/types/nodes/dashboard/nodes-overview-block.type';
@Component({
selector: 'mina-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
- host: { class: 'h-100 w-100 flex-row' },
+ host: { class: 'h-100 w-100 flex-column' },
})
export class DashboardComponent extends StoreDispatcher implements OnInit {
+ updateAction: string;
+ updateDetail: string;
+ connectedProgress: number = 0;
+ ledgerProgress: number = 0;
+ blockSyncProgress: number = 0;
+
+ private connected: boolean = false;
+
ngOnInit(): void {
+ this.updateAction = 'Connecting to peers';
+
this.listenToNodeChanging();
+ this.listenToPeersChanges();
+ this.getDashboardData();
+ this.listenToNodesChanges();
+ this.listenToNodesChanges();
+ }
+
+ private getDashboardData(): void {
this.dispatch(DashboardInit);
timer(4000, 4000)
.pipe(
@@ -28,6 +53,85 @@ export class DashboardComponent extends StoreDispatcher implements OnInit {
private listenToNodeChanging(): void {
this.select(AppSelectors.activeNode, () => {
this.dispatch(DashboardGetData, { force: true });
- }, filter(Boolean), skip(1), untilDestroyed(this));
+ }, filter(Boolean), skip(1));
+ }
+
+ private listenToPeersChanges(): void {
+ this.select(selectDashboardPeersStats, (stats: DashboardPeersStats) => {
+ if (this.connected && stats.connected === 0 || !this.connected) {
+ if (stats.connected > 0) {
+ this.updateAction = `Connected to ${stats.connected} peer${stats.connected !== 1 ? 's' : ''}`;
+ } else if (stats.connecting > 0) {
+ this.updateAction = `Connecting to ${stats.connecting} peer${stats.connecting !== 1 ? 's' : ''}`;
+ } else {
+ this.updateAction = 'Looking for peers';
+ }
+ if (stats.connected) {
+ this.connectedProgress = 33;
+ } else {
+ this.connectedProgress = 0;
+ this.ledgerProgress = 0;
+ this.blockSyncProgress = 0;
+ }
+ this.detect();
+ }
+ this.connected = stats.connected > 0;
+ }, skip(1));
+ }
+
+ private listenToNodesChanges(): void {
+ this.select(selectDashboardNodesAndRpcStats, ([nodes, rpcStats]: [NodesOverviewNode[], DashboardRpcStats]) => {
+ if (nodes.length !== 0) {
+ const ledgers = nodes[0].ledgers;
+
+ let stakingProgress = rpcStats.stakingLedger?.fetched / rpcStats.stakingLedger?.estimation * 100 || 0;
+ let nextProgress = rpcStats.nextLedger?.fetched / rpcStats.nextLedger?.estimation * 100 || 0;
+ let rootSnarkedProgress = rpcStats.rootLedger?.fetched / rpcStats.rootLedger?.estimation * 100 || 0;
+ let rootStagedProgress = ledgers.rootStaged.staged.fetchPartsEnd ? 50 : 0;
+
+ if (ledgers.stakingEpoch.state === NodesOverviewLedgerStepState.SUCCESS) {
+ stakingProgress = 100;
+ this.updateAction = 'Downloading Next Epoch Ledger';
+ }
+ if (ledgers.nextEpoch.state === NodesOverviewLedgerStepState.SUCCESS) {
+ nextProgress = 100;
+ this.updateAction = 'Downloading Root Snarked Ledger';
+ }
+ if (ledgers.rootSnarked.state === NodesOverviewLedgerStepState.SUCCESS) {
+ rootSnarkedProgress = 100;
+ this.updateAction = 'Downloading Root Staged Ledger';
+ }
+ if (ledgers.rootStaged.state === NodesOverviewLedgerStepState.SUCCESS) {
+ rootStagedProgress = 100;
+ this.updateAction = 'Fetching Blocks';
+ }
+ const ledgerProgressTotal = (stakingProgress + nextProgress + rootSnarkedProgress + rootStagedProgress) / 4;
+ this.ledgerProgress = ledgerProgressTotal * 0.33;
+
+ let blocks = nodes[0].blocks;
+
+ blocks = blocks.slice(1);
+
+ const fetched = blocks.filter(b => ![NodesOverviewNodeBlockStatus.MISSING, NodesOverviewNodeBlockStatus.FETCHING].includes(b.status)).length;
+ const fetchedPercentage = Math.round(fetched * 100 / 291);
+
+ const applied = blocks.filter(b => b.status === NodesOverviewNodeBlockStatus.APPLIED).length;
+ const appliedPercentage = Math.round(applied * 100 / 291);
+ this.blockSyncProgress = appliedPercentage * 0.34;
+
+ if (fetchedPercentage < 100) {
+ this.updateAction = `Fetching Blocks (${fetchedPercentage}%)`;
+ } else if (appliedPercentage < 100) {
+ this.updateAction = `Applying Blocks (${appliedPercentage}%)`;
+ } else {
+ this.updateAction = '';
+ }
+
+ } else {
+ this.ledgerProgress = 0;
+ this.blockSyncProgress = 0;
+ }
+ this.detect();
+ });
}
}
diff --git a/frontend/src/app/features/dashboard/mina-card/mina-card.component.html b/frontend/src/app/features/dashboard/mina-card/mina-card.component.html
deleted file mode 100644
index a83c6194b1..0000000000
--- a/frontend/src/app/features/dashboard/mina-card/mina-card.component.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
- {{ label }}
- {{ icon }}
-
-
{{ value ?? '-' }}
-
{{ hint }}
diff --git a/frontend/src/app/features/dashboard/mina-card/mina-card.component.scss b/frontend/src/app/features/dashboard/mina-card/mina-card.component.scss
deleted file mode 100644
index a5a87f8b45..0000000000
--- a/frontend/src/app/features/dashboard/mina-card/mina-card.component.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-:host {
- height: 128px;
- width: 128px;
-
- @media (max-width: 700px) {
- height: 140px;
- width: 140px;
- }
-}
-
-.mina-icon {
- margin-left: 3px;
-}
-
-.value {
- font-size: 24px;
- letter-spacing: 0.15px;
-}
diff --git a/frontend/src/app/features/dashboard/mina-card/mina-card.component.ts b/frontend/src/app/features/dashboard/mina-card/mina-card.component.ts
deleted file mode 100644
index 9681cbd29a..0000000000
--- a/frontend/src/app/features/dashboard/mina-card/mina-card.component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
-
-@Component({
- selector: 'mina-card',
- templateUrl: './mina-card.component.html',
- styleUrls: ['./mina-card.component.scss'],
- changeDetection: ChangeDetectionStrategy.OnPush,
- host: { class: 'flex-column flex-between pt-8 pb-8 pl-12 border-rad-8 bg-surface' },
-})
-export class MinaCardComponent {
-
- @Input() color: string = 'var(--base-primary)';
- @Input() labelColor: string = 'var(--base-primary)';
- @Input() hintColor: string = 'var(--base-tertiary)';
- @Input() icon: string;
- @Input() label: string | number;
- @Input() value: string | number;
- @Input() hint: string | number;
- @Input() tooltipText: string;
-
-}
diff --git a/frontend/src/app/features/webnode/web-node-initialization/web-node-initialization.component.html b/frontend/src/app/features/webnode/web-node-initialization/web-node-initialization.component.html
index 2bfabe225f..5fac9c6f20 100644
--- a/frontend/src/app/features/webnode/web-node-initialization/web-node-initialization.component.html
+++ b/frontend/src/app/features/webnode/web-node-initialization/web-node-initialization.component.html
@@ -12,7 +12,8 @@
{{ loadingMessage || downloadingMessage }}
+ [@messageChange]="loadingMessage">{{ loadingMessage || downloadingMessage }}
+
@@ -35,21 +36,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-