diff --git a/src/lib/components/collection/AddToCollectionModal.svelte b/src/lib/components/collection/AddToCollectionModal.svelte index 35c45ebc..037e0909 100644 --- a/src/lib/components/collection/AddToCollectionModal.svelte +++ b/src/lib/components/collection/AddToCollectionModal.svelte @@ -19,11 +19,12 @@ type SearchResultItem = SearchPageResult['results'][number] interface Props { + userId: string open?: boolean onOpenChange?: (open: boolean) => void } - let { open = $bindable(false), onOpenChange }: Props = $props() + let { userId, open = $bindable(false), onOpenChange }: Props = $props() // Search state let searchQuery = $state('') @@ -45,7 +46,7 @@ let sentinelEl = $state() // Get IDs of characters already in collection - const collectedIdsQuery = createQuery(() => collectionQueries.collectedCharacterIds()) + const collectedIdsQuery = createQuery(() => collectionQueries.collectedCharacterIds(userId)) // Build filters for search (using SearchFilters type from search.queries) const searchFilters = $derived({ diff --git a/src/routes/(app)/[username]/collection/characters/+page.server.ts b/src/routes/(app)/[username]/collection/characters/+page.server.ts index 5e459269..e7cc668f 100644 --- a/src/routes/(app)/[username]/collection/characters/+page.server.ts +++ b/src/routes/(app)/[username]/collection/characters/+page.server.ts @@ -1,23 +1,12 @@ import type { PageServerLoad } from './$types' -import { error } from '@sveltejs/kit' -import { collectionAdapter } from '$lib/api/adapters/collection.adapter' export const load: PageServerLoad = async ({ parent }) => { const { user, isOwner } = await parent() - try { - // Fetch the user's public character collection - const characters = await collectionAdapter.getPublicCharacters(user.id) - - return { - characters, - isOwner - } - } catch (e: any) { - // 403 means collection is private - if (e?.status === 403) { - throw error(403, 'This collection is private') - } - throw error(e?.status || 502, e?.message || 'Failed to load collection') + // User info comes from layout, collection data is fetched client-side via TanStack Query + // The unified API endpoint handles privacy checks server-side + return { + user, + isOwner } } diff --git a/src/routes/(app)/[username]/collection/characters/+page.svelte b/src/routes/(app)/[username]/collection/characters/+page.svelte index 1a9aac22..4133b055 100644 --- a/src/routes/(app)/[username]/collection/characters/+page.svelte +++ b/src/routes/(app)/[username]/collection/characters/+page.svelte @@ -31,83 +31,30 @@ // Sentinel for infinite scroll let sentinelEl = $state() - // Build filters for query + // Build filters for query - all filters are now server-side for everyone const queryFilters = $derived({ element: elementFilters.length > 0 ? elementFilters : undefined, - rarity: rarityFilters.length > 0 ? rarityFilters : undefined + rarity: rarityFilters.length > 0 ? rarityFilters : undefined, + race: raceFilters.length > 0 ? raceFilters : undefined, + proficiency: proficiencyFilters.length > 0 ? proficiencyFilters : undefined, + gender: genderFilters.length > 0 ? genderFilters : undefined }) - // For owner, use the authenticated collection query with infinite scroll - // For non-owner, use the server-loaded public data - const collectionQuery = createInfiniteQuery(() => ({ - ...collectionQueries.characters(queryFilters), - enabled: data.isOwner, - initialData: data.isOwner - ? undefined - : { - pages: [ - { - results: data.characters || [], - page: 1, - totalPages: 1, - total: data.characters?.length || 0, - perPage: data.characters?.length || 20 - } - ], - pageParams: [1] - } - })) + // Unified query for any user's collection (privacy enforced server-side) + const collectionQuery = createInfiniteQuery(() => { + const userId = data.user.id + const filters = queryFilters + return collectionQueries.characters(userId, filters) + }) // Flatten all characters from pages const allCharacters = $derived.by((): CollectionCharacter[] => { - if (!data.isOwner) { - return data.characters || [] - } if (!collectionQuery.data?.pages) { return [] } return collectionQuery.data.pages.flatMap((page) => page.results ?? []) }) - // Client-side filtering for non-API-supported filters - const filteredCharacters = $derived.by((): CollectionCharacter[] => { - let result = allCharacters - - // Apply element filter (client-side for non-owner) - if (elementFilters.length > 0) { - result = result.filter((c) => elementFilters.includes(c.character?.element ?? 0)) - } - - // Apply rarity filter (client-side for non-owner) - if (rarityFilters.length > 0) { - result = result.filter((c) => rarityFilters.includes(c.character?.rarity ?? 0)) - } - - // Apply race filter (client-side) - race is nested object - if (raceFilters.length > 0) { - result = result.filter((c) => { - const race1 = c.character?.race?.race1 ?? 0 - const race2 = c.character?.race?.race2 ?? 0 - return raceFilters.includes(race1) || (race2 && raceFilters.includes(race2)) - }) - } - - // Apply proficiency filter (client-side) - proficiency is an array - if (proficiencyFilters.length > 0) { - result = result.filter((c) => { - const proficiencies = c.character?.proficiency ?? [] - return proficiencies.some((p) => proficiencyFilters.includes(p)) - }) - } - - // Apply gender filter (client-side) - if (genderFilters.length > 0) { - result = result.filter((c) => genderFilters.includes(c.character?.gender ?? 0)) - } - - return result - }) - // Infinite scroll const inViewport = new IsInViewport(() => sentinelEl, { rootMargin: '200px' @@ -115,7 +62,6 @@ $effect(() => { if ( - data.isOwner && inViewport.current && collectionQuery.hasNextPage && !collectionQuery.isFetchingNextPage && @@ -125,11 +71,9 @@ } }) - const isLoading = $derived(data.isOwner && collectionQuery.isLoading) - const isEmpty = $derived(!isLoading && filteredCharacters.length === 0) - const showSentinel = $derived( - data.isOwner && collectionQuery.hasNextPage && !collectionQuery.isFetchingNextPage - ) + const isLoading = $derived(collectionQuery.isLoading) + const isEmpty = $derived(!isLoading && allCharacters.length === 0) + const showSentinel = $derived(collectionQuery.hasNextPage && !collectionQuery.isFetchingNextPage) function handleFiltersChange(filters: CollectionFilterState) { elementFilters = filters.element @@ -223,7 +167,7 @@ {:else}
- {#each filteredCharacters as character (character.id)} + {#each allCharacters as character (character.id)}