fix(workshop): keep download progress across tab switches

The workshop is unmounted when the Installed tab is opened, so its local download state was discarded and returning to the Community tab showed no progress even though the install was still running. Active downloads and percentages now live in a shared module that outlives the component, and in-flight downloads keep updating it.
This commit is contained in:
HorizonCode committed 2026-11-03 19:03:58 +01:00
1 parent 809fca6f29
commit 4b039c9b79
2 files changed
+49 -28

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
/**
* Install progress lives outside the workshop component because switching to
* the Installed tab unmounts it. Keeping the state here lets the workshop show
* the current progress again when it is remounted, while the in-flight
* download keeps running.
*/
export const activeThemeDownloads = new SvelteSet<string>();
export const themeDownloadProgress = new SvelteMap<string, number>();
@@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import DefaultThemePreview from '$assets/default_preview.png'; import DefaultThemePreview from '$assets/default_preview.png';
import { activeThemeDownloads, themeDownloadProgress } from '$lib/communityThemeDownloads';
import { import {
fetchCommunityTags, fetchCommunityTags,
fetchCommunityThemes, fetchCommunityThemes,
@@ -84,7 +85,7 @@
} from '@lucide/svelte'; } from '@lucide/svelte';
import { sileo } from 'sileo'; import { sileo } from 'sileo';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { SvelteMap, SvelteSet } from 'svelte/reactivity'; import { SvelteMap } from 'svelte/reactivity';
let { let {
onApplyTheme, onApplyTheme,
@@ -119,8 +120,6 @@
let error = $state<string | null>(null); let error = $state<string | null>(null);
let latestRequestId = 0; let latestRequestId = 0;
let downloadingIds = new SvelteSet<string>();
let downloadProgressById = new SvelteMap<string, number>();
let forkingId = $state<string | null>(null); let forkingId = $state<string | null>(null);
let deletingId = $state<string | null>(null); let deletingId = $state<string | null>(null);
let moderatingId = $state<string | null>(null); let moderatingId = $state<string | null>(null);
@@ -415,14 +414,14 @@
} }
async function handleDownloadTheme(theme: CommunityTheme) { async function handleDownloadTheme(theme: CommunityTheme) {
if (theme.deletedAt || downloadingIds.has(theme.id)) return; if (theme.deletedAt || activeThemeDownloads.has(theme.id)) return;
downloadingIds.add(theme.id); activeThemeDownloads.add(theme.id);
downloadProgressById.set(theme.id, 10); themeDownloadProgress.set(theme.id, 10);
try { try {
await downloadAndInstallCommunityTheme(theme, (_status, progress) => { await downloadAndInstallCommunityTheme(theme, (_status, progress) => {
downloadProgressById.set(theme.id, progress); themeDownloadProgress.set(theme.id, progress);
}); });
sileo.success({ sileo.success({
@@ -436,8 +435,8 @@
description: err instanceof Error ? err.message : String(err), description: err instanceof Error ? err.message : String(err),
}); });
} finally { } finally {
downloadingIds.delete(theme.id); activeThemeDownloads.delete(theme.id);
downloadProgressById.delete(theme.id); themeDownloadProgress.delete(theme.id);
} }
} }
@@ -458,18 +457,24 @@
} }
async function handleEditTheme(theme: CommunityTheme) { async function handleEditTheme(theme: CommunityTheme) {
if (theme.deletedAt || downloadingIds.has(theme.id) || forkingId || deletingId || moderatingId) if (
theme.deletedAt ||
activeThemeDownloads.has(theme.id) ||
forkingId ||
deletingId ||
moderatingId
)
return; return;
let installed = let installed =
localThemeMap.get(theme.id.toLowerCase()) || localThemeMap.get(theme.name.toLowerCase()); localThemeMap.get(theme.id.toLowerCase()) || localThemeMap.get(theme.name.toLowerCase());
if (!installed) { if (!installed) {
downloadingIds.add(theme.id); activeThemeDownloads.add(theme.id);
downloadProgressById.set(theme.id, 10); themeDownloadProgress.set(theme.id, 10);
try { try {
await downloadAndInstallCommunityTheme(theme, (_status, progress) => { await downloadAndInstallCommunityTheme(theme, (_status, progress) => {
downloadProgressById.set(theme.id, progress); themeDownloadProgress.set(theme.id, progress);
}); });
installed = $custom_themes.find( installed = $custom_themes.find(
(candidate) => candidate.folder_name.toLowerCase() === theme.id.toLowerCase() (candidate) => candidate.folder_name.toLowerCase() === theme.id.toLowerCase()
@@ -481,8 +486,8 @@
}); });
return; return;
} finally { } finally {
downloadingIds.delete(theme.id); activeThemeDownloads.delete(theme.id);
downloadProgressById.delete(theme.id); themeDownloadProgress.delete(theme.id);
} }
} }
@@ -492,7 +497,13 @@
} }
async function handleRefreshOwnedTheme(theme: CommunityTheme) { async function handleRefreshOwnedTheme(theme: CommunityTheme) {
if (theme.deletedAt || downloadingIds.has(theme.id) || forkingId || deletingId || moderatingId) if (
theme.deletedAt ||
activeThemeDownloads.has(theme.id) ||
forkingId ||
deletingId ||
moderatingId
)
return; return;
const installed = const installed =
@@ -511,11 +522,11 @@
); );
if (!confirmed) return; if (!confirmed) return;
downloadingIds.add(theme.id); activeThemeDownloads.add(theme.id);
downloadProgressById.set(theme.id, 10); themeDownloadProgress.set(theme.id, 10);
try { try {
const res = await downloadAndInstallCommunityTheme(theme, (_status, progress) => { const res = await downloadAndInstallCommunityTheme(theme, (_status, progress) => {
downloadProgressById.set(theme.id, progress); themeDownloadProgress.set(theme.id, progress);
}); });
const refreshed = $custom_themes.find( const refreshed = $custom_themes.find(
(candidate) => candidate.folder_name.toLowerCase() === res.folderName.toLowerCase() (candidate) => candidate.folder_name.toLowerCase() === res.folderName.toLowerCase()
@@ -534,8 +545,8 @@
description: err instanceof Error ? err.message : String(err), description: err instanceof Error ? err.message : String(err),
}); });
} finally { } finally {
downloadingIds.delete(theme.id); activeThemeDownloads.delete(theme.id);
downloadProgressById.delete(theme.id); themeDownloadProgress.delete(theme.id);
} }
} }
@@ -732,10 +743,10 @@
let installed: Theme | undefined; let installed: Theme | undefined;
try { try {
await loadWorkshopThemes(); await loadWorkshopThemes();
downloadingIds.add(fork.id); activeThemeDownloads.add(fork.id);
downloadProgressById.set(fork.id, 10); themeDownloadProgress.set(fork.id, 10);
const res = await downloadAndInstallCommunityTheme(fork, (_status, progress) => { const res = await downloadAndInstallCommunityTheme(fork, (_status, progress) => {
downloadProgressById.set(fork.id, progress); themeDownloadProgress.set(fork.id, progress);
}); });
installed = $custom_themes.find( installed = $custom_themes.find(
(candidate) => candidate.folder_name.toLowerCase() === res.folderName.toLowerCase() (candidate) => candidate.folder_name.toLowerCase() === res.folderName.toLowerCase()
@@ -746,8 +757,8 @@
description: `${err instanceof Error ? err.message : String(err)} You can download it from the workshop.`, description: `${err instanceof Error ? err.message : String(err)} You can download it from the workshop.`,
}); });
} finally { } finally {
downloadingIds.delete(fork.id); activeThemeDownloads.delete(fork.id);
downloadProgressById.delete(fork.id); themeDownloadProgress.delete(fork.id);
} }
if (installed) { if (installed) {
@@ -1600,8 +1611,8 @@
{@const isActive = Boolean( {@const isActive = Boolean(
localTheme && $active_custom_theme?.folder_name === localTheme.folder_name localTheme && $active_custom_theme?.folder_name === localTheme.folder_name
)} )}
{@const isDownloading = downloadingIds.has(theme.id)} {@const isDownloading = activeThemeDownloads.has(theme.id)}
{@const downloadPercent = downloadProgressById.get(theme.id) ?? 0} {@const downloadPercent = themeDownloadProgress.get(theme.id) ?? 0}
{@const isDeleted = Boolean(theme.deletedAt)} {@const isDeleted = Boolean(theme.deletedAt)}
{@const hasUpdate = Boolean( {@const hasUpdate = Boolean(
!isDeleted && localTheme && localTheme.version !== theme.version !isDeleted && localTheme && localTheme.version !== theme.version