feat: return workshop metadata with mod details
Include the Workshop title and preview image in the items response so the mod header survives a page refresh, not just navigation from search. Resolve both from the same Steam API call.
This commit is contained in:
+4
-4
@@ -1,4 +1,4 @@
|
||||
import type { SearchResult, TTSMod } from '@tts/shared';
|
||||
import type { ModDetails, SearchResult } from '@tts/shared';
|
||||
import { traceImage } from '@tts/http';
|
||||
|
||||
export { traceImage };
|
||||
@@ -21,13 +21,13 @@ export function searchWorkshop(q: string, page = 1): Promise<SearchResult> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a full parsed TTS save.
|
||||
* Fetch a full parsed TTS save plus its Workshop metadata.
|
||||
*/
|
||||
export function fetchMod(id: string, fileUrl?: string): Promise<TTSMod> {
|
||||
export function fetchMod(id: string, fileUrl?: string): Promise<ModDetails> {
|
||||
const params = new URLSearchParams();
|
||||
if (fileUrl) params.set('fileUrl', fileUrl);
|
||||
const qs = params.toString();
|
||||
return getJson<TTSMod>(`/items/${id}${qs ? `?${qs}` : ''}`);
|
||||
return getJson<ModDetails>(`/items/${id}${qs ? `?${qs}` : ''}`);
|
||||
}
|
||||
|
||||
/** Build a URL for the raw save file download. */
|
||||
|
||||
@@ -25,8 +25,8 @@ export default function ModPage() {
|
||||
if (id) load(id, item?.fileUrl);
|
||||
}, [id, item?.fileUrl, load]);
|
||||
|
||||
const tree = useMemo(() => (mod ? buildTree(mod) : []), [mod]);
|
||||
const refs = useMemo(() => (mod ? collectRefs(mod) : []), [mod]);
|
||||
const tree = useMemo(() => (mod ? buildTree(mod.mod) : []), [mod]);
|
||||
const refs = useMemo(() => (mod ? collectRefs(mod.mod) : []), [mod]);
|
||||
|
||||
const selected = useMemo(
|
||||
() => (mod ? findInTree(tree, selectedPath) : undefined),
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { create } from 'zustand';
|
||||
import type { TTSMod } from '@tts/shared';
|
||||
import type { ModDetails } from '@tts/shared';
|
||||
import { fetchMod } from '../api';
|
||||
|
||||
interface ModState {
|
||||
id: string | null;
|
||||
fileUrl?: string;
|
||||
mod: TTSMod | null;
|
||||
mod: ModDetails | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
load: (id: string, fileUrl?: string) => Promise<void>;
|
||||
@@ -21,11 +21,11 @@ export const useModStore = create<ModState>((set) => ({
|
||||
load: async (id, fileUrl) => {
|
||||
set({ loading: true, error: null, id, fileUrl });
|
||||
try {
|
||||
const mod = await fetchMod(id, fileUrl);
|
||||
set({ mod, loading: false });
|
||||
const details = await fetchMod(id, fileUrl);
|
||||
set({ mod: details, loading: false });
|
||||
} catch (err) {
|
||||
set({ loading: false, error: String(err) });
|
||||
}
|
||||
},
|
||||
clear: () => set({ id: null, fileUrl: undefined, mod: null, error: null, loading: false }),
|
||||
clear: () => set({ id: null, fileUrl: undefined, mod: null, loading: false, error: null }),
|
||||
}));
|
||||
Reference in New Issue
Block a user