From 107c6fccf655725565aae68a74aaf1e29c79235b Mon Sep 17 00:00:00 2001 From: rouggy Date: Sat, 29 Aug 2026 01:36:06 +0200 Subject: [PATCH] fix(watchlist): remove on a single click, like the bell and trophy beside it The two-click arm goes: removing an entry is cheap to undo (type it again), so it does not earn a confirmation its neighbours do not have. The bin reddens on hover and the green line confirms the removal. --- frontend/src/components/WatchlistTab.tsx | 23 ++++++----------------- frontend/src/lib/i18n.tsx | 4 ++-- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/WatchlistTab.tsx b/frontend/src/components/WatchlistTab.tsx index e6f8e53..c9af876 100644 --- a/frontend/src/components/WatchlistTab.tsx +++ b/frontend/src/components/WatchlistTab.tsx @@ -94,7 +94,7 @@ export function WatchlistTab({ spots, spotStatus, onSpotSelect, onSpotClick }: P setNotice(isError ? '' : msg); noticeTimer.current = window.setTimeout(() => { setError(''); setNotice(''); }, 4000) as unknown as number; }; - const [removeArm, setRemoveArm] = useState(''); + // worked answer per "call|band|modeclass|contest" key. const [worked, setWorked] = useState>({}); @@ -186,22 +186,11 @@ export function WatchlistTab({ spots, spotStatus, onSpotSelect, onSpotClick }: P } catch (e: any) { flash(String(e?.message ?? e), true); } }; - // Armed through a REF, checked synchronously: the state variable is captured - // at render time, so a quick double-click read the pre-arm value twice and - // both clicks merely armed — the bin turned red and nothing was ever removed. - const removeArmRef = useRef(''); + // One click, like the bell and the trophy beside it: removing a watchlist + // entry is cheap to undo (type it again), so it does not earn a confirmation + // the other two buttons do not have. const remove = async (call: string) => { - if (removeArmRef.current !== call) { - removeArmRef.current = call; - setRemoveArm(call); - window.setTimeout(() => { - if (removeArmRef.current === call) { removeArmRef.current = ''; setRemoveArm(''); } - }, 3000); - return; - } - removeArmRef.current = ''; - setRemoveArm(''); - try { await WatchlistRemove(call); await refresh(); } + try { await WatchlistRemove(call); flash(t('wl.removed', { call }), false); await refresh(); } catch (e: any) { flash(String(e?.message ?? e), true); } }; @@ -376,7 +365,7 @@ export function WatchlistTab({ spots, spotStatus, onSpotSelect, onSpotClick }: P diff --git a/frontend/src/lib/i18n.tsx b/frontend/src/lib/i18n.tsx index 890c626..7e1393c 100644 --- a/frontend/src/lib/i18n.tsx +++ b/frontend/src/lib/i18n.tsx @@ -49,7 +49,7 @@ const en: Dict = { 'wl.nNeeded': '{n} needed', 'wl.nToday': '{n} today', 'wl.allWorked': 'All worked', 'wl.workedToday': 'Worked today', 'wl.totalSpots': '{n} spots', 'wl.toggleContest': 'Contest entry: judged per UTC day', 'wl.notifyOn': 'Alert me when this station is spotted', 'wl.notifyOff': 'Stop alerting for this station', - 'wl.remove': 'Remove (click twice)', 'wl.noSpots': 'No live spots', + 'wl.remove': 'Remove', 'wl.removed': '{call} removed from the watchlist.', 'wl.noSpots': 'No live spots', 'wl.newDxcc': 'NEW DXCC', 'wl.worked': 'Worked', 'wl.needed': 'Needed!', 'wl.todayOk': 'Today ✓', 'wl.workToday': 'Work today!', 'wl.spotTip': 'Click: fill the callsign · double-click: tune and work', 'tools.net': 'NET Control', 'tools.alerts': 'Alert management…', 'tools.contest': 'Contest mode', @@ -573,7 +573,7 @@ const fr: Dict = { 'wl.nNeeded': '{n} manquants', 'wl.nToday': "{n} aujourd'hui", 'wl.allWorked': 'Tout contacté', 'wl.workedToday': "Contacté aujourd'hui", 'wl.totalSpots': '{n} spots', 'wl.toggleContest': 'Entrée contest : jugée par jour UTC', 'wl.notifyOn': "M'alerter quand cette station est spottée", 'wl.notifyOff': "Ne plus alerter pour cette station", - 'wl.remove': 'Supprimer (cliquer deux fois)', 'wl.noSpots': 'Aucun spot en cours', + 'wl.remove': 'Supprimer', 'wl.removed': '{call} retiré de la watchlist.', 'wl.noSpots': 'Aucun spot en cours', 'wl.newDxcc': 'NOUV DXCC', 'wl.worked': 'Contacté', 'wl.needed': 'Manquant !', 'wl.todayOk': "Auj. ✓", 'wl.workToday': "À faire auj. !", 'wl.spotTip': "Clic : remplir l'indicatif · double-clic : régler la radio et travailler", 'tools.net': 'Contrôle de NET', 'tools.alerts': 'Gestion des alertes…', 'tools.contest': 'Mode contest',