diff --git a/frontend/httpd.conf b/frontend/httpd.conf index 94d2dca798..6008c20a62 100644 --- a/frontend/httpd.conf +++ b/frontend/httpd.conf @@ -498,6 +498,7 @@ LogLevel warn # AddType application/x-compress .Z AddType application/x-gzip .gz .tgz + AddType application/wasm .wasm # # AddHandler allows you to map certain file extensions to "handlers": 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 6cb1e24b4f..ab027cf104 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 } from '@openmina/shared'; +import { Effect, isDesktop } 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'; @@ -47,7 +47,8 @@ export class BlockProductionWonSlotsEffects extends BaseEffect { ? EMPTY : this.wonSlotsService.getSlots().pipe( switchMap(({ slots, epoch }) => { - const activeSlotRoute = state.blockProduction[BLOCK_PRODUCTION_WON_SLOTS_KEY].activeSlotRoute; + 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)) { newActiveSlot = slots.find(s => s.active) @@ -56,7 +57,7 @@ export class BlockProductionWonSlotsEffects extends BaseEffect { ?? null; } const routes: string[] = [Routes.BLOCK_PRODUCTION, Routes.WON_SLOTS]; - if (newActiveSlot) { + if (newActiveSlot && isDesktop() || (activeSlotRoute && !bpState.activeSlot) || (activeSlotRoute && bpState.openSidePanel)) { routes.push(newActiveSlot.globalSlot.toString()); } return fromPromise(this.router.navigate(routes, { queryParamsHandling: 'merge' })).pipe(map(() => ({ 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 9243daa5f7..63fd852440 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 @@ -1,6 +1,6 @@ import { createReducer, on } from '@ngrx/store'; import { BlockProductionWonSlotsState } from '@block-production/won-slots/block-production-won-slots.state'; -import { isMobile, sort, SortDirection, TableSort } from '@openmina/shared'; +import { isDesktop, isMobile, sort, SortDirection, TableSort } from '@openmina/shared'; import { BlockProductionWonSlotsActions } from '@block-production/won-slots/block-production-won-slots.actions'; import { BlockProductionWonSlotsSlot, @@ -38,7 +38,7 @@ export const blockProductionWonSlotsReducer = createReducer( epoch, filteredSlots: filterSlots(sortSlots(slots, state.sort), state.filters), activeSlot, - openSidePanel: !!activeSlot, + openSidePanel: state.activeSlot ? state.openSidePanel : isDesktop(), })), on(BlockProductionWonSlotsActions.setActiveSlot, (state, { slot }) => ({ ...state, @@ -56,7 +56,12 @@ export const blockProductionWonSlotsReducer = createReducer( filters, filteredSlots: filterSlots(sortSlots(state.slots, state.sort), filters), })), - on(BlockProductionWonSlotsActions.toggleSidePanel, state => ({ ...state, openSidePanel: !state.openSidePanel })), + on(BlockProductionWonSlotsActions.toggleSidePanel, state => ({ + ...state, + openSidePanel: !state.openSidePanel, + activeSlot: !state.openSidePanel ? state.activeSlot : undefined, + activeSlotRoute: !state.openSidePanel ? state.activeSlotRoute : undefined, + })), on(BlockProductionWonSlotsActions.close, () => initialState), ); 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 14762e01ae..de74fe2848 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 @@ -1,12 +1,4 @@ -import { - ChangeDetectionStrategy, - Component, - OnDestroy, - OnInit, - TemplateRef, - ViewChild, - ViewContainerRef, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core'; import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class'; import { BlockProductionWonSlotsSelectors } from '@block-production/won-slots/block-production-won-slots.state'; import { @@ -15,19 +7,13 @@ 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, 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'; import { AppNodeDetails } from '@shared/types/app/app-node-details.type'; +import { Router } from '@angular/router'; +import { Routes } from '@shared/enums/routes.enum'; @Component({ selector: 'mina-block-production-won-slots-side-panel', @@ -70,6 +56,8 @@ export class BlockProductionWonSlotsSidePanelComponent extends StoreDispatcher i @ViewChild('discarded') private discardedTemplate: TemplateRef; + constructor(private router: Router) {super();} + ngOnInit(): void { this.listenToActiveSlot(); this.parseRemainingTime(); @@ -198,6 +186,7 @@ export class BlockProductionWonSlotsSidePanelComponent extends StoreDispatcher i } closeSidePanel(): void { + this.router.navigate([Routes.BLOCK_PRODUCTION, Routes.WON_SLOTS]); this.dispatch2(BlockProductionWonSlotsActions.toggleSidePanel()); } diff --git a/frontend/src/app/features/webnode/web-node-not-supported/web-node-not-supported.component.html b/frontend/src/app/features/webnode/web-node-not-supported/web-node-not-supported.component.html index 7fa84854eb..ef3d9e3b80 100644 --- a/frontend/src/app/features/webnode/web-node-not-supported/web-node-not-supported.component.html +++ b/frontend/src/app/features/webnode/web-node-not-supported/web-node-not-supported.component.html @@ -2,33 +2,16 @@ @if (isPhone) { -
-
-
Your device isn't supported
+
+
iOS
+
+
Your iOS {{ iOSVersion ?? '' }} isn't supported
- Unfortunately, iOS devices are not currently supported. -
-
- -
-
Switch to one of the following platforms
-
-
- - Windows -
-
- - Android -
-
- - MacOS -
+ To use the Web Node please update your device to the latest iOS version.
-
And use Chrome, Edge, Safari or Brave
+
} @else {
diff --git a/frontend/src/app/features/webnode/web-node-not-supported/web-node-not-supported.component.scss b/frontend/src/app/features/webnode/web-node-not-supported/web-node-not-supported.component.scss index 82eeb41224..e44bc5b434 100644 --- a/frontend/src/app/features/webnode/web-node-not-supported/web-node-not-supported.component.scss +++ b/frontend/src/app/features/webnode/web-node-not-supported/web-node-not-supported.component.scss @@ -1,3 +1,5 @@ +@import 'openmina'; + .header { height: 56px; @@ -10,6 +12,27 @@ .browser-content { height: calc(100% - 56px); + .ios { + width: 96px; + height: 96px; + font-size: 30px; + border-radius: 100px; + } + + .ios-box { + margin-top: 40px; + margin-bottom: 40px; + padding: 0 32px; + } + + .learn-btn { + background-color: $base-background; + color: $base-primary; + max-width: 330px; + margin: 0 12px; + filter: invert(1); + } + .headline { font-size: 20px; line-height: 30px; diff --git a/frontend/src/app/features/webnode/web-node-not-supported/web-node-not-supported.component.ts b/frontend/src/app/features/webnode/web-node-not-supported/web-node-not-supported.component.ts index f7af277c05..37ffb3247a 100644 --- a/frontend/src/app/features/webnode/web-node-not-supported/web-node-not-supported.component.ts +++ b/frontend/src/app/features/webnode/web-node-not-supported/web-node-not-supported.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { Platform } from '@angular/cdk/platform'; -import { sendSentryEvent } from '@shared/helpers/webnode.helper'; +import { iOSversion, sendSentryEvent } from '@shared/helpers/webnode.helper'; +import { safelyExecuteInBrowser } from '@openmina/shared'; const code = [1, 2, 3, 2]; @@ -19,13 +20,23 @@ export class WebNodeNotSupportedComponent { @Output() bypassUnsupportedDevice = new EventEmitter(); + iOSVersion: string = iOSversion().join('.'); devMode: boolean = false; private codeVerifier: number[] = []; constructor(private platform: Platform) {} + addDevKey2(): void { + this.codeVerifier.push(code[this.codeVerifier.length]); + this.checkCode(); + } + addDevKey(key: number): void { this.codeVerifier.push(key); + this.checkCode(); + } + + private checkCode(): void { if (this.codeVerifier.length === code.length) { if (this.codeVerifier.every((v, i) => v === code[i])) { this.devMode = true; @@ -41,13 +52,8 @@ export class WebNodeNotSupportedComponent { this.bypassUnsupportedDevice.emit(); } } -} -function iOSversion(): number { - if (/iP(hone|od|ad)/.test(navigator.platform)) { - // supports iOS 2.0 and later: - const v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/); - return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || '0', 10)][0]; + howToUpdate(): void { + safelyExecuteInBrowser(() => window.open('https://support.apple.com/en-us/118575', '_blank')); } - return 0; } diff --git a/frontend/src/app/features/webnode/webnode.component.ts b/frontend/src/app/features/webnode/webnode.component.ts index a1b79ab680..913ed0c4d0 100644 --- a/frontend/src/app/features/webnode/webnode.component.ts +++ b/frontend/src/app/features/webnode/webnode.component.ts @@ -6,6 +6,7 @@ import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class'; import { getMergedRoute, MergedRoute } from '@openmina/shared'; import { filter } from 'rxjs'; import { WebNodeService } from '@core/services/web-node.service'; +import { iOSversion } from '@shared/helpers/webnode.helper'; @Component({ selector: 'mina-webnode', @@ -50,9 +51,8 @@ export class WebnodeComponent extends StoreDispatcher implements OnInit { } private checkIfDeviceIsSupported(): void { - if (this.platform.IOS) { - this.supported = false; + this.supported = iOSversion()[0] >= 18; this.isPhone = true; return; } diff --git a/frontend/src/app/shared/helpers/webnode.helper.ts b/frontend/src/app/shared/helpers/webnode.helper.ts index 7a223fb132..3b9a2057f1 100644 --- a/frontend/src/app/shared/helpers/webnode.helper.ts +++ b/frontend/src/app/shared/helpers/webnode.helper.ts @@ -4,3 +4,12 @@ import { SeverityLevel } from '@sentry/angular'; export function sendSentryEvent(message: string, level: SeverityLevel = 'error'): void { Sentry.captureEvent({ message: message, level, tags: { type: 'webnode' } }); } + +export function iOSversion(): number[] { + if (/iP(hone|od|ad)/.test(navigator.platform)) { + // supports iOS 2.0 and later: + const v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/); + return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || '0', 10)]; + } + return [0]; +}