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
This commit is contained in:
NimBold
2026-07-04 05:32:40 +03:30
parent 84a4ff4bfc
commit 4a322196de
7 changed files with 162 additions and 80 deletions
+29 -2
View File
@@ -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 = () => {
</div>
<div>
<label className="block text-[10px] uppercase font-bold tracking-wider text-text-muted mb-1">Cookies</label>
<input type="text" value={cookies} onChange={e=>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" />
<input
type="text"
value={cookies}
onChange={e => {
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"
/>
</div>
<div>
<label className="block text-[10px] uppercase font-bold tracking-wider text-text-muted mb-1">Mirrors</label>
+6 -3
View File
@@ -639,8 +639,8 @@ runEngineChecks(false);
<div className="mac-settings-group">
<label className="mac-settings-row cursor-default">
<div className="settings-row-label">
<span>Show notification when download completes</span>
<small>Alerts you in Notification Center</small>
<span>Show system notification when download completes</span>
<small>Uses your operating system notification settings</small>
</div>
<input
type="checkbox"
@@ -650,7 +650,10 @@ runEngineChecks(false);
/>
</label>
<label className="mac-settings-row cursor-default">
<span className="text-[13px] text-text-primary">Play sound when download completes</span>
<div className="settings-row-label">
<span>Play in-app completion chime</span>
<small>Optional Firelink sound, independent of system notifications</small>
</div>
<input
type="checkbox"
checked={settings.playCompletionSound}