diff --git a/Makefile b/Makefile index ec279e7..6e85b38 100644 --- a/Makefile +++ b/Makefile @@ -25,13 +25,13 @@ help: ## install-deps: Installe les dépendances npm install-deps: - @echo "[1/2] Installation des dépendances npm..." + @echo "[1/2] Installation des dependances npm..." cd $(FRONTEND_DIR) && npm install - @echo "Dépendances installées" + @echo "Dependances installees" @echo "" - @echo "[2/2] Vérification de Go..." + @echo "[2/2] Verification de Go..." @go version - @echo "Go est installé" + @echo "Go est installe" ## frontend: Build le frontend Svelte frontend: diff --git a/flex.sqlite-journal b/flex.sqlite-journal new file mode 100644 index 0000000..a5f868f Binary files /dev/null and b/flex.sqlite-journal differ diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index b603276..2d50616 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -7,9 +7,11 @@ import Sidebar from './components/Sidebar.svelte'; import Toast from './components/Toast.svelte'; import ErrorBanner from './components/ErrorBanner.svelte'; + import SoundManager from './components/SoundManager.svelte'; // State let spots = []; + let previousSpots = []; // Pour détecter les nouveaux spots let filteredSpots = []; let stats = { totalSpots: 0, @@ -23,13 +25,14 @@ filters: { skimmer: false, ft8: false, ft4: false } }; let topSpotters = []; - let watchlist = []; + let watchlist = []; // ✅ Initialisé vide, sera rempli par WebSocket let recentQSOs = []; let logStats = { today: 0, thisWeek: 0, thisMonth: 0, total: 0 }; let dxccProgress = { worked: 0, total: 340, percentage: 0 }; let solarData = { sfi: 'N/A', sunspots: 'N/A', aIndex: 'N/A', kIndex: 'N/A' }; let activeTab = 'stats'; + let showOnlyActive = false; // ✅ État global pour persister entre les onglets let wsStatus = 'disconnected'; let errorMessage = ''; let toastMessage = ''; @@ -58,13 +61,15 @@ band12M: false, band10M: false, band6M: false - }; + }; // WebSocket let ws; let reconnectTimer; let reconnectAttempts = 0; let maxReconnectAttempts = 10; + let soundEnabled = true; + let isShuttingDown = false; // ✅ Flag pour éviter les erreurs pendant le shutdown // Reactive filtered spots $: { @@ -75,6 +80,52 @@ } } + // Détecter les nouveaux spots et jouer les sons appropriés + $: if (spots.length > 0 && soundEnabled) { + checkForNewSpots(spots, previousSpots, watchlist); + previousSpots = [...spots]; + } + + // ✅ SUPPRIMÉ - La watchlist est gérée côté serveur via WebSocket + // Les fonctions addToWatchlist et removeFromWatchlist ne sont plus nécessaires + + function checkForNewSpots(currentSpots, prevSpots, wl) { + // Ne pas jouer de sons au chargement initial + if (prevSpots.length === 0) return; + + // Créer un Set des IDs précédents pour une recherche rapide + const previousIds = new Set(prevSpots.map(s => `${s.DX}-${s.Frequency}-${s.Time}`)); + + // Trouver les nouveaux spots + const newSpots = currentSpots.filter(spot => { + const spotId = `${spot.DX}-${spot.Frequency}-${spot.Time}`; + return !previousIds.has(spotId); + }); + + if (newSpots.length === 0) return; + + // Vérifier s'il y a un nouveau DXCC (priorité maximale) + const hasNewDXCC = newSpots.some(spot => spot.NewDXCC === true); + if (hasNewDXCC) { + playSound('newDXCC'); + return; // Ne jouer qu'un seul son + } + + // Vérifier s'il y a un spot de la watchlist + const hasWatchlistSpot = newSpots.some(spot => + wl.some(pattern => spot.DX === pattern || spot.DX.startsWith(pattern)) + ); + if (hasWatchlistSpot) { + playSound('watchlist'); + } + } + + function playSound(type) { + window.dispatchEvent(new CustomEvent('playSound', { + detail: { type } + })); + } + function applyFilters(allSpots, filters, wl) { const bandFiltersActive = filters.band160M || filters.band80M || filters.band60M || filters.band40M || filters.band30M || filters.band20M || filters.band17M || @@ -255,6 +306,7 @@ topSpotters = message.data || []; break; case 'watchlist': + // ✅ La watchlist est mise à jour par WebSocket watchlist = message.data || []; break; case 'log': @@ -344,13 +396,11 @@ if (data.success) { showToast('FlexDXCluster shutting down...', 'info'); - // Fermer le WebSocket et arrêter les tentatives de reconnexion if (ws) ws.close(); if (reconnectTimer) clearTimeout(reconnectTimer); wsStatus = 'disconnected'; - maxReconnectAttempts = 0; // Empêcher les reconnexions + maxReconnectAttempts = 0; - // Afficher la page de shutdown après 1 seconde setTimeout(() => { document.body.innerHTML = `
@@ -378,10 +428,8 @@ connectWebSocket(); fetchSolarData(); - // Update solar data every 15 minutes const solarInterval = setInterval(fetchSolarData, 15 * 60 * 1000); - // Listen for sendSpot events from watchlist const handleSendSpot = (e) => { sendCallsign(e.detail.callsign, e.detail.frequency, e.detail.mode); }; @@ -397,6 +445,9 @@
+ + + {#if errorMessage} errorMessage = ''} /> {/if} @@ -424,8 +475,8 @@ on:toggleFilter={(e) => toggleFilter(e.detail)} /> -
-
+
+
- showToast(e.detail.message, e.detail.type)} - /> +
+ showToast(e.detail.message, e.detail.type)} + /> +
\ No newline at end of file diff --git a/frontend/src/components/Sidebar.svelte b/frontend/src/components/Sidebar.svelte index 0408f1a..49eeb86 100644 --- a/frontend/src/components/Sidebar.svelte +++ b/frontend/src/components/Sidebar.svelte @@ -11,11 +11,17 @@ export let recentQSOs; export let logStats; export let dxccProgress; + export let showOnlyActive = false; // ✅ Export pour persister l'état const dispatch = createEventDispatcher(); + + // ✅ Propagation des évènements vers le parent + function handleToast(event) { + dispatch('toast', event.detail); + } -
+
+ +
+ + + + updateVolume(e.target.value / 100)} + class="w-20 h-1 bg-slate-700 rounded-lg appearance-none cursor-pointer accent-blue-500" + title="Volume: {Math.round(volume * 100)}%" + /> + {Math.round(volume * 100)}% +
+
+{/if} + + \ No newline at end of file diff --git a/frontend/src/components/WatchlistTab.svelte b/frontend/src/components/WatchlistTab.svelte index 601b417..c3b1d2a 100644 --- a/frontend/src/components/WatchlistTab.svelte +++ b/frontend/src/components/WatchlistTab.svelte @@ -1,25 +1,61 @@ -
+

Watchlist

-
+
{#if displayList.length === 0}
@@ -186,7 +227,7 @@
{#if count > 0} -
+
{#each matchingSpots.slice(0, 10) as spot}