diff --git a/src/hooks/airdrop/useWebsocket.ts b/src/hooks/airdrop/useWebsocket.ts index 9bdf2ef83..40151f205 100644 --- a/src/hooks/airdrop/useWebsocket.ts +++ b/src/hooks/airdrop/useWebsocket.ts @@ -7,10 +7,10 @@ import { useHandleWsUserIdEvent } from './ws/useHandleWsUserIdEvent'; import { invoke } from '@tauri-apps/api/core'; import { useBlockchainVisualisationStore } from '@app/store/useBlockchainVisualisationStore'; import { useAppStateStore } from '@app/store/appStateStore'; +import { MINING_EVENT_INTERVAL_MS, useShellOfSecretsStore } from '@app/store/useShellOfSecretsStore'; let socket: ReturnType | null; -const MINING_EVENT_INTERVAL = 15000; const MINING_EVENT_NAME = 'mining-status'; interface SignData { @@ -31,6 +31,7 @@ export const useWebsocket = () => { const [connectedSocket, setConnectedSocket] = useState(false); const height = useBlockchainVisualisationStore((s) => s.displayBlockHeight); const applicationsVersions = useAppStateStore((state) => state.applications_versions); + const registerWsConnectionEvent = useShellOfSecretsStore((state) => state.registerWsConnectionEvent); const isMining = useMemo(() => { const isMining = (cpu?.mining.is_mining || gpu?.mining.is_mining) && base_node?.is_connected; @@ -53,23 +54,40 @@ export const useWebsocket = () => { const signatureData = (await invoke('sign_ws_data', { data: transformedPayload, })) as SignData; - await socket.emit(MINING_EVENT_NAME, { - data: payload, - signature: signatureData.signature, - pubKey: signatureData.pubKey, - }); + + const statusResponse: { error?: string; success: boolean } = await socket + .timeout(5000) + .emitWithAck(MINING_EVENT_NAME, { + data: payload, + signature: signatureData.signature, + pubKey: signatureData.pubKey, + }); + if (statusResponse) { + registerWsConnectionEvent({ + state: statusResponse.success ? 'up' : 'error', + error: `shell of secrets mining error - reason: ${statusResponse.error}`, + }); + } } catch (e) { console.error(e); } }, - [connectedSocket, appId, height, applicationsVersions?.tari_universe, network, userId] + [ + connectedSocket, + appId, + height, + applicationsVersions?.tari_universe, + network, + userId, + registerWsConnectionEvent, + ] ); useEffect(() => { if (isMining) { const intervalId = setInterval(() => { handleEmitMiningStatus(isMining); - }, MINING_EVENT_INTERVAL); + }, MINING_EVENT_INTERVAL_MS); return () => clearInterval(intervalId); } else { handleEmitMiningStatus(isMining); @@ -96,11 +114,37 @@ export const useWebsocket = () => { socket.emit('subscribe-to-gem-updates'); socket.on('connect', () => { if (!socket) return; + registerWsConnectionEvent({ + state: 'up', + }); setConnectedSocket(true); socket.emit('auth', airdropToken); socket.on(userId as string, handleWsUserIdEvent); }); + + socket.on('connect_error', (e) => { + registerWsConnectionEvent({ + state: 'error', + error: 'could not connect to server', + }); + }); + socket.on('disconnect', (reason, details) => { + registerWsConnectionEvent({ + state: 'error', + error: 'disconnected from server', + }); + console.error(reason, details); + }); + socket.io.on('reconnect', (e) => { + registerWsConnectionEvent({ + state: 'up', + }); + }); } catch (e) { + registerWsConnectionEvent({ + state: 'error', + error: 'error at initiating connection', + }); console.error(e); } }; @@ -110,10 +154,17 @@ export const useWebsocket = () => { if (socket) { setConnectedSocket(false); handleEmitMiningStatus(isMining); + registerWsConnectionEvent({ + state: 'off', + }); socket.disconnect(); socket = null; } } catch (e) { + registerWsConnectionEvent({ + state: 'error', + error: 'error at disconnecting', + }); console.error(e); } }; diff --git a/src/store/useShellOfSecretsStore.ts b/src/store/useShellOfSecretsStore.ts index 188ba66a2..1a8bfcecb 100644 --- a/src/store/useShellOfSecretsStore.ts +++ b/src/store/useShellOfSecretsStore.ts @@ -2,6 +2,7 @@ import { CrewMember } from '@app/types/ws.ts'; import { create } from './create.ts'; const SOS_GAME_ENDING_DATE = new Date('2025-01-30'); +export const MINING_EVENT_INTERVAL_MS = 15000; // Type for the response structure export interface ReferralsResponse { @@ -11,17 +12,35 @@ export interface ReferralsResponse { toleranceMs: number; } +type WsConnectionStateString = 'up' | 'off' | 'error'; + +export interface WsConnectionEvent { + state: WsConnectionStateString; + error?: string; +} + +interface WsConnectionState { + state: WsConnectionStateString; + error?: { + since: Date; + timeDownMs: number; + lastMessage: string; + }; +} + interface State { referrals?: ReferralsResponse; showWidget: boolean; totalBonusTimeMs: number; revealDate: Date; + wsConnectionState: WsConnectionState; } interface Actions { setReferrals: (referrals: ReferralsResponse) => void; setShowWidget: (showWidget: boolean) => void; setTotalBonusTimeMs: (totalTimeBonusUpdate: number) => void; + registerWsConnectionEvent: (event: WsConnectionEvent) => void; getTimeRemaining: () => { days: number; hours: number; totalRemainingMs: number }; } @@ -30,6 +49,9 @@ const initialState: State = { showWidget: false, totalBonusTimeMs: 0, revealDate: SOS_GAME_ENDING_DATE, + wsConnectionState: { + state: 'off', + }, }; export const useShellOfSecretsStore = create()((set, get) => ({ @@ -37,6 +59,31 @@ export const useShellOfSecretsStore = create()((set, get) => ({ setReferrals: (referrals) => set({ referrals }), setShowWidget: (showWidget) => set({ showWidget }), setTotalBonusTimeMs: (totalTimeBonusUpdate: number) => set({ totalBonusTimeMs: totalTimeBonusUpdate }), + registerWsConnectionEvent: (event: WsConnectionEvent) => + set(({ wsConnectionState }) => { + if (event.state === 'off' || event.state === 'up') { + return { wsConnectionState: { state: event.state }, error: undefined }; + } + + const currentTime = new Date(); + + //in case an error happened + if (event.state === 'error') { + return { + wsConnectionState: { + state: event.state, + error: { + since: wsConnectionState.error?.since ?? new Date(), + timeDownMs: + currentTime.getTime() - + (wsConnectionState.error?.since.getTime() || currentTime.getTime()), + lastMessage: event?.error || '', + }, + }, + }; + } + return { wsConnectionState }; + }), getTimeRemaining: () => { const now = new Date(); const targetDateBroughtForwardByTimeBonus = get().revealDate.getTime() - get().totalBonusTimeMs;