diff --git a/crates/lanspread-tauri-deno-ts/src/App.tsx b/crates/lanspread-tauri-deno-ts/src/App.tsx index ebb86ac..b706a57 100644 --- a/crates/lanspread-tauri-deno-ts/src/App.tsx +++ b/crates/lanspread-tauri-deno-ts/src/App.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { invoke } from '@tauri-apps/api/core'; import { listen } from '@tauri-apps/api/event'; import { open } from '@tauri-apps/plugin-dialog'; @@ -8,6 +8,7 @@ import "./App.css"; const FILE_STORAGE = 'launcher-settings.json'; const GAME_DIR_KEY = 'game-directory'; +const CHECKING_PEERS_TIMEOUT_MS = 5000; // enum with install status enum InstallStatus { @@ -38,11 +39,45 @@ const App = () => { const [gameItems, setGameItems] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [gameDir, setGameDir] = useState(''); + const checkingPeersTimeouts = useRef>>({}); const filteredGames = gameItems.filter(item => item.name.toLowerCase().includes(searchTerm.toLowerCase()) ); + const clearCheckingPeersTimeout = (gameId: string) => { + const timeoutId = checkingPeersTimeouts.current[gameId]; + if (timeoutId !== undefined) { + clearTimeout(timeoutId); + delete checkingPeersTimeouts.current[gameId]; + } + }; + + const scheduleCheckingPeersFallback = (gameId: string, fallbackMessage?: string, fallbackLevel?: StatusLevel) => { + clearCheckingPeersTimeout(gameId); + checkingPeersTimeouts.current[gameId] = setTimeout(() => { + setGameItems(prev => prev.map(item => { + if (item.id !== gameId || item.install_status !== InstallStatus.CheckingPeers) { + return item; + } + return { + ...item, + install_status: item.installed ? InstallStatus.Installed : InstallStatus.NotInstalled, + status_message: fallbackMessage ?? 'No peers currently have this game.', + status_level: fallbackLevel ?? 'error', + }; + })); + delete checkingPeersTimeouts.current[gameId]; + }, CHECKING_PEERS_TIMEOUT_MS); + }; + + useEffect(() => { + return () => { + Object.values(checkingPeersTimeouts.current).forEach(clearTimeout); + checkingPeersTimeouts.current = {}; + }; + }, []); + const getInitialGameDir = async () => { // update game directory from storage (if exists) // only if it's not already set @@ -60,6 +95,7 @@ const App = () => { const unlisten = await listen('game-download-failed', (event) => { const game_id = event.payload as string; console.log(`❌ game-download-failed ${game_id} event received`); + clearCheckingPeersTimeout(game_id); setGameItems(prev => prev.map(item => item.id === game_id ? { ...item, @@ -85,6 +121,7 @@ const App = () => { const unlisten = await listen('game-no-peers', (event) => { const game_id = event.payload as string; console.log(`⚠️ game-no-peers ${game_id} event received`); + clearCheckingPeersTimeout(game_id); setGameItems(prev => prev.map(item => item.id === game_id ? { ...item, @@ -107,6 +144,7 @@ const App = () => { const unlisten = await listen('game-unpack-finished', (event) => { const game_id = event.payload as string; console.log(`🗲 game-unpack-finished ${game_id} event received`); + clearCheckingPeersTimeout(game_id); setGameItems(prev => prev.map(item => item.id === game_id ? { ...item, @@ -183,6 +221,7 @@ const App = () => { const unlisten_game_download_begin = await listen('game-download-begin', (event) => { const game_id = event.payload as string; console.log(`🗲 game-download-begin ${game_id} event received`); + clearCheckingPeersTimeout(game_id); setGameItems(prev => prev.map(item => item.id === game_id ? { ...item, @@ -197,6 +236,7 @@ const App = () => { const unlisten_game_download_finished = await listen('game-download-finished', (event) => { const game_id = event.payload as string; console.log(`🗲 game-download-finished ${game_id} event received`); + clearCheckingPeersTimeout(game_id); setGameItems(prev => prev.map(item => item.id === game_id ? { ...item, @@ -247,15 +287,21 @@ const App = () => { const success = await invoke('install_game', { id }); if (success) { console.log(`✅ Game install for id=${id} started...`); + let fallbackMessage: string | undefined; + let fallbackLevel: StatusLevel | undefined; // update install status in gameItems for this game - setGameItems(prev => prev.map(item => item.id === id - ? { - ...item, - install_status: InstallStatus.CheckingPeers, - status_message: undefined, - status_level: undefined, + setGameItems(prev => prev.map(item => { + if (item.id === id) { + fallbackMessage = item.status_message; + fallbackLevel = item.status_level; + return { + ...item, + install_status: InstallStatus.CheckingPeers, + }; } - : item)); + return item; + })); + scheduleCheckingPeersFallback(id, fallbackMessage, fallbackLevel); } else { // game is already being installed console.warn(`🚧 Game with id=${id} is already being installed`); @@ -271,15 +317,21 @@ const App = () => { const success = await invoke('update_game', { id }); if (success) { console.log(`✅ Game update for id=${id} started...`); + let fallbackMessage: string | undefined; + let fallbackLevel: StatusLevel | undefined; // update install status in gameItems for this game - setGameItems(prev => prev.map(item => item.id === id - ? { - ...item, - install_status: InstallStatus.CheckingPeers, - status_message: undefined, - status_level: undefined, + setGameItems(prev => prev.map(item => { + if (item.id === id) { + fallbackMessage = item.status_message; + fallbackLevel = item.status_level; + return { + ...item, + install_status: InstallStatus.CheckingPeers, + }; } - : item)); + return item; + })); + scheduleCheckingPeersFallback(id, fallbackMessage, fallbackLevel); } else { // game is already being installed/updated console.warn(`🚧 Game with id=${id} is already being updated`);