From 2c204ac258f82e81ab0595f56f53aeefc3e02314 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Thu, 23 Jul 2026 18:07:08 +0200 Subject: [PATCH] fix(call-to-play): explain peer startup state Treat an unavailable actor ID or an explicit not-ready result as normal peer startup and tell the user that LAN connection is still in progress. Clear that message when snapshot registration succeeds. Map obsolete, missing-history, and active-history-limit store failures to distinct guidance without marking a healthy transport unavailable. Preserve a generic message for unexpected publish failures. Test Plan: - just frontend-test - just build - git diff --cached --check --- .../src/hooks/useCallToPlay.ts | 18 ++++++++--- .../src/lib/callToPlay.ts | 15 ++++++++++ .../tests/callToPlay.test.ts | 30 +++++++++++++++++++ 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/crates/lanspread-tauri-deno-ts/src/hooks/useCallToPlay.ts b/crates/lanspread-tauri-deno-ts/src/hooks/useCallToPlay.ts index 6e9f92a..c794ec6 100644 --- a/crates/lanspread-tauri-deno-ts/src/hooks/useCallToPlay.ts +++ b/crates/lanspread-tauri-deno-ts/src/hooks/useCallToPlay.ts @@ -3,7 +3,9 @@ import { invoke } from '@tauri-apps/api/core'; import { listen, UnlistenFn } from '@tauri-apps/api/event'; import { + CALL_TO_PLAY_CONNECTING_MESSAGE, callToPlayEvent, + callToPlayPublishErrorMessage, extendDeadline, pruneCallToPlayEvents, reduceCallToPlayEvents, @@ -75,6 +77,11 @@ export const useCallToPlay = (username: string): UseCallToPlay => { const ready = peerId !== null; setActorId(peerId); setTransportReady(ready); + if (ready) { + setError(current => + current === CALL_TO_PLAY_CONNECTING_MESSAGE ? null : current + ); + } if (ready && retry !== undefined) { window.clearInterval(retry); retry = undefined; @@ -104,7 +111,10 @@ export const useCallToPlay = (username: string): UseCallToPlay => { } } catch (err) { console.error('Failed to register Call to Play listener:', err); - if (!cancelled) setError('Call to Play networking is unavailable.'); + if (!cancelled) { + setTransportReady(false); + setError('Call to Play networking is unavailable.'); + } } }; @@ -122,7 +132,7 @@ export const useCallToPlay = (username: string): UseCallToPlay => { ): Promise => { if (actorId === null) { setTransportReady(false); - setError('Call to Play needs an active LAN peer. Choose a game folder first.'); + setError(CALL_TO_PLAY_CONNECTING_MESSAGE); return false; } const event = callToPlayEvent(callId, actorId, actor, action); @@ -130,7 +140,7 @@ export const useCallToPlay = (username: string): UseCallToPlay => { const accepted = await invoke('publish_call_to_play', { event }); if (!accepted) { setTransportReady(false); - setError('Call to Play needs an active LAN peer. Choose a game folder first.'); + setError(CALL_TO_PLAY_CONNECTING_MESSAGE); return false; } setTransportReady(true); @@ -138,7 +148,7 @@ export const useCallToPlay = (username: string): UseCallToPlay => { return true; } catch (err) { console.error('publish_call_to_play failed:', err); - setError('Could not send this Call to Play update.'); + setError(callToPlayPublishErrorMessage(err)); return false; } }, [actor, actorId]); diff --git a/crates/lanspread-tauri-deno-ts/src/lib/callToPlay.ts b/crates/lanspread-tauri-deno-ts/src/lib/callToPlay.ts index 275a1f1..921aaec 100644 --- a/crates/lanspread-tauri-deno-ts/src/lib/callToPlay.ts +++ b/crates/lanspread-tauri-deno-ts/src/lib/callToPlay.ts @@ -8,6 +8,21 @@ import { export const CHECKIN_LEAD_MS = 15 * 60_000; export const EXPIRED_RETENTION_MS = 5 * 60_000; export const TERMINAL_RETENTION_MS = 15 * 60_000; +export const CALL_TO_PLAY_CONNECTING_MESSAGE = + 'Call to Play is still connecting to the LAN. Try again in a moment.'; + +export const callToPlayPublishErrorMessage = (error: unknown): string => { + const detail = error instanceof Error ? error.message : String(error); + if (detail.includes('Call to Play event is obsolete') + || detail.includes('Call to Play history is missing') + ) { + return 'This Call to Play has expired or already finished.'; + } + if (detail.includes('Call to Play event history is full')) { + return 'Call to Play has reached its active update limit. Start or cancel an active call, then try again.'; + } + return 'Could not send this Call to Play update.'; +}; export const extendDeadline = ( now: number, diff --git a/crates/lanspread-tauri-deno-ts/tests/callToPlay.test.ts b/crates/lanspread-tauri-deno-ts/tests/callToPlay.test.ts index 025fe4c..16fca28 100644 --- a/crates/lanspread-tauri-deno-ts/tests/callToPlay.test.ts +++ b/crates/lanspread-tauri-deno-ts/tests/callToPlay.test.ts @@ -1,8 +1,10 @@ import { + CALL_TO_PLAY_CONNECTING_MESSAGE, CHECKIN_LEAD_MS, EXPIRED_RETENTION_MS, TERMINAL_RETENTION_MS, activeCallCount, + callToPlayPublishErrorMessage, extendDeadline, phaseOf, bumpTime, @@ -178,6 +180,34 @@ Deno.test('adding time extends from the current deadline or the current time', ( ); }); +Deno.test('publish failures distinguish startup and store outcomes', () => { + assertEquals( + CALL_TO_PLAY_CONNECTING_MESSAGE, + 'Call to Play is still connecting to the LAN. Try again in a moment.', + 'peer startup message', + ); + assertEquals( + callToPlayPublishErrorMessage('Call to Play event is obsolete'), + 'This Call to Play has expired or already finished.', + 'obsolete call message', + ); + assertEquals( + callToPlayPublishErrorMessage('Call to Play history is missing'), + 'This Call to Play has expired or already finished.', + 'missing expired history message', + ); + assertEquals( + callToPlayPublishErrorMessage(new Error('Call to Play event history is full')), + 'Call to Play has reached its active update limit. Start or cancel an active call, then try again.', + 'active history limit message', + ); + assertEquals( + callToPlayPublishErrorMessage('channel closed'), + 'Could not send this Call to Play update.', + 'unexpected failure message', + ); +}); + Deno.test('reduction is order-independent and deduplicates events and messages', () => { const message = event('message-event', 'Bob', { SendMessage: { message_id: 'message-1', text: 'Ready?' },