From 4a322196deb134872586ffb4c6a136b9ac450b1b Mon Sep 17 00:00:00 2001 From: NimBold Date: Sat, 4 Jul 2026 05:32:40 +0330 Subject: [PATCH] feat: improve toast UI, add-modal cookie handling, and completion sounds - Refine toast animations and durations - Prevent extension-captured cookies from being sent across cross-origin redirects - Clear stale extension metadata properly for single-link handoffs - Disable completion sound by default and update settings labels - Handle AudioContext suspension for completion chimes --- src/App.tsx | 12 ++- src/components/AddDownloadsModal.tsx | 31 +++++- src/components/SettingsView.tsx | 9 +- src/contexts/ToastContext.tsx | 153 +++++++++++++++------------ src/store/useDownloadStore.test.ts | 27 +++++ src/store/useDownloadStore.ts | 8 +- src/store/useSettingsStore.ts | 2 +- 7 files changed, 162 insertions(+), 80 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 647829a..12bd593 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -44,13 +44,17 @@ const getScheduledQueueIds = () => { type AudioContextConstructor = typeof AudioContext; -const playCompletionChime = () => { +const playCompletionChime = async () => { const AudioCtor = window.AudioContext || (window as Window & { webkitAudioContext?: AudioContextConstructor }).webkitAudioContext; if (!AudioCtor) return; const context = new AudioCtor(); + if (context.state === 'suspended') { + await context.resume(); + } + const oscillator = context.createOscillator(); const gain = context.createGain(); oscillator.type = 'sine'; @@ -551,11 +555,9 @@ function App() { if (event.payload.status !== 'completed' && event.payload.status !== 'failed') return; const settings = useSettingsStore.getState(); if (event.payload.status === 'completed' && settings.playCompletionSound) { - try { - playCompletionChime(); - } catch (error) { + playCompletionChime().catch(error => { console.error('Completion sound failed:', error); - } + }); } if (!settings.showNotifications) return; diff --git a/src/components/AddDownloadsModal.tsx b/src/components/AddDownloadsModal.tsx index 917a2d5..544a648 100644 --- a/src/components/AddDownloadsModal.tsx +++ b/src/components/AddDownloadsModal.tsx @@ -37,6 +37,16 @@ const formatBytes = (bytes: number) => { return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; }; +const isCrossHostRedirect = (sourceUrl: string, downloadUrl: string) => { + try { + const sourceHost = new URL(sourceUrl).hostname; + const downloadHost = new URL(downloadUrl).hostname; + return downloadHost !== sourceHost && !downloadHost.endsWith(`.${sourceHost}`); + } catch { + return false; + } +}; + export const AddDownloadsModal = () => { const { addToast } = useToast(); const { @@ -85,6 +95,7 @@ export const AddDownloadsModal = () => { const [checksumValue, setChecksumValue] = useState(''); const [headers, setHeaders] = useState(''); const [cookies, setCookies] = useState(''); + const cookiesFromExtensionRef = useRef(false); const [mirrors, setMirrors] = useState(''); useEffect(() => { @@ -111,6 +122,7 @@ export const AddDownloadsModal = () => { pendingAddHeaders ].filter(Boolean).join('\n')); setCookies(pendingAddCookies); + cookiesFromExtensionRef.current = Boolean(pendingAddCookies?.trim()); setMirrors(''); setIsQueueMenuOpen(false); setIsSubmitting(false); @@ -251,6 +263,11 @@ export const AddDownloadsModal = () => { headers: headers?.trim() || null, cookies: cookies?.trim() || null }); + const nextDownloadUrl = meta.url || row.sourceUrl; + if (cookiesFromExtensionRef.current && isCrossHostRedirect(row.sourceUrl, nextDownloadUrl)) { + setCookies(''); + cookiesFromExtensionRef.current = false; + } setParsedItems(current => updateRowIfCurrent( current, row.id, @@ -258,7 +275,7 @@ export const AddDownloadsModal = () => { row.generation, currentRow => ({ ...currentRow, - downloadUrl: meta.url || currentRow.downloadUrl, + downloadUrl: nextDownloadUrl || currentRow.downloadUrl, file: canonicalizeDownloadFileName( current.length === 1 && pendingAddFilename ? pendingAddFilename @@ -1008,7 +1025,17 @@ export const AddDownloadsModal = () => {
- setCookies(e.target.value)} placeholder="name=value; other=value" className="add-download-control w-full px-3 py-1.5 text-xs font-mono" aria-label="Cookies" /> + { + cookiesFromExtensionRef.current = false; + setCookies(e.target.value); + }} + placeholder="name=value; other=value" + className="add-download-control w-full px-3 py-1.5 text-xs font-mono" + aria-label="Cookies" + />
diff --git a/src/components/SettingsView.tsx b/src/components/SettingsView.tsx index 5c946ed..3f3d5d3 100644 --- a/src/components/SettingsView.tsx +++ b/src/components/SettingsView.tsx @@ -639,8 +639,8 @@ runEngineChecks(false);