From ebc5fbf9c4298e747464d24010747ae9301e1dd0 Mon Sep 17 00:00:00 2001 From: HorizonCode Date: Tue, 3 Nov 2026 14:58:28 +0100 Subject: [PATCH] feat(themes): polish workshop moderation and theme controls Add the in-app tag editor, installed-theme reporting, readable moderation and report dialogs, and notification layering fixes. Persist server-assigned workshop IDs for stable republishes, hide self-reporting, fix report-button contrast, and disable macOS autocorrect and spell checking across launcher windows. --- src/app.html | 8 +- src/lib/communityThemes.ts | 33 +- .../notifications/NotificationHub.svelte | 3 + .../theme/CommunityWorkshopView.svelte | 591 +++++++++++++----- .../components/theme/ThemeEditorWindow.svelte | 27 +- .../components/theme/ThemeReportDialog.svelte | 175 ++++++ .../ui/date-picker/date-picker.svelte | 7 + .../ui/dialog/dialog-content.svelte | 29 + .../ui/dialog/dialog-description.svelte | 16 + .../ui/dialog/dialog-overlay.svelte | 19 + .../components/ui/dialog/dialog-title.svelte | 18 + src/lib/components/ui/dialog/dialog.svelte | 10 + src/lib/components/ui/dialog/index.ts | 24 + src/lib/themeV2.ts | 12 + src/lib/themes.ts | 41 ++ src/routes/+layout.svelte | 45 ++ src/screens/Launch.svelte | 62 ++ 17 files changed, 964 insertions(+), 156 deletions(-) create mode 100644 src/lib/components/theme/ThemeReportDialog.svelte create mode 100644 src/lib/components/ui/dialog/dialog-content.svelte create mode 100644 src/lib/components/ui/dialog/dialog-description.svelte create mode 100644 src/lib/components/ui/dialog/dialog-overlay.svelte create mode 100644 src/lib/components/ui/dialog/dialog-title.svelte create mode 100644 src/lib/components/ui/dialog/dialog.svelte create mode 100644 src/lib/components/ui/dialog/index.ts diff --git a/src/app.html b/src/app.html index 175adf5..db3308c 100644 --- a/src/app.html +++ b/src/app.html @@ -7,7 +7,13 @@ %sveltekit.head% - +
%sveltekit.body%
diff --git a/src/lib/communityThemes.ts b/src/lib/communityThemes.ts index 90178c0..f15a8b7 100644 --- a/src/lib/communityThemes.ts +++ b/src/lib/communityThemes.ts @@ -3,7 +3,12 @@ import { listen } from '@tauri-apps/api/event'; import * as path from '@tauri-apps/api/path'; import * as fs from '@tauri-apps/plugin-fs'; import { config } from './config'; -import { reloadThemes, themesDirectory } from './themes'; +import { + reloadThemes, + themesDirectory, + updateInstalledThemeTags, + updateInstalledThemeWorkshopId, +} from './themes'; export interface CommunityThemeAuthor { id: number; @@ -644,6 +649,8 @@ export async function setCommunityThemeVisibility( export async function publishThemeToWorkshop(options: { themeFolder: string; themeId: string; + /** Existing server-assigned ID when updating an already-published theme. */ + workshopId?: string; name: string; description?: string; version?: string; @@ -651,7 +658,7 @@ export async function publishThemeToWorkshop(options: { tags?: string[]; previewImageBase64?: string; onProgress?: (status: string, percentage: number) => void; -}): Promise<{ success: boolean; theme: unknown }> { +}): Promise<{ success: boolean; themeId: string; theme: unknown }> { const { onProgress = () => {} } = options; const baseUrl = await getThemesServiceUrl(); const authHeaders = await getAuthHeaders(); @@ -699,7 +706,9 @@ export async function publishThemeToWorkshop(options: { ...authHeaders, }, body: JSON.stringify({ - themeId: options.themeId, + // The service uses this only when it belongs to an existing theme owned + // by the caller. Otherwise it assigns a fresh UUID and ignores it. + themeId: options.workshopId || options.themeId, name: options.name, description: options.description || '', version: options.version || '1.0.0', @@ -768,10 +777,16 @@ export async function publishThemeToWorkshop(options: { throw new Error(errData.error || `Finalizing upload failed (HTTP ${completeRes.status})`); } - const completeData = await completeRes.json(); + const completeData = (await completeRes.json()) as Record & { + themeId?: unknown; + }; onProgress('Theme published successfully!', 100); - return { success: true, theme: completeData }; + return { + success: true, + themeId: typeof completeData.themeId === 'string' ? completeData.themeId : '', + theme: completeData, + }; } finally { // Clean up temporary local zip if (await fs.exists(destinationZip)) { @@ -829,6 +844,14 @@ export async function downloadAndInstallCommunityTheme( } } + try { + // Keep the installed manifest aligned with the workshop record. + await updateInstalledThemeTags(folderName, theme.tags); + await updateInstalledThemeWorkshopId(folderName, theme.id); + } catch (err) { + console.warn('[Workshop] Could not write workshop metadata into the installed theme:', err); + } + reportProgress('Theme installed!', 100); // Refresh themes in launcher state diff --git a/src/lib/components/notifications/NotificationHub.svelte b/src/lib/components/notifications/NotificationHub.svelte index 0df76d1..cbee43d 100644 --- a/src/lib/components/notifications/NotificationHub.svelte +++ b/src/lib/components/notifications/NotificationHub.svelte @@ -1,5 +1,6 @@ + + + + {#if theme} +
+
+ + Report theme + + + Reporting {theme.name} by {theme.author.name}. Reports + are visible to moderators only. + +
+ +
+ +
+
+ + { + if (value) category = value as ThemeReportCategory; + }} + > + + {REPORT_CATEGORIES.find((item) => item.id === category)?.label ?? 'Other'} + + + {#each REPORT_CATEGORIES as item (item.id)} + + {/each} + + +
+ +
+ +