From b8a48771dd96c065093e349564d696ff3835ba78 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 2 Dec 2025 15:31:46 -0800 Subject: [PATCH] update collection adapter/queries for unified api - add userId param to list methods - remove public collection methods (now unified) - update query keys to include userId --- src/lib/api/adapters/collection.adapter.ts | 87 ++++--------- src/lib/api/queries/collection.queries.ts | 142 ++++++++++----------- 2 files changed, 96 insertions(+), 133 deletions(-) diff --git a/src/lib/api/adapters/collection.adapter.ts b/src/lib/api/adapters/collection.adapter.ts index 11adebe7..153d8b90 100644 --- a/src/lib/api/adapters/collection.adapter.ts +++ b/src/lib/api/adapters/collection.adapter.ts @@ -19,8 +19,7 @@ import type { CollectionWeaponInput, CollectionSummonInput, CollectionJobAccessoryInput, - CollectionFilters, - CollectionResponse + CollectionFilters } from '$lib/types/api/collection' /** @@ -57,15 +56,20 @@ export class CollectionAdapter extends BaseAdapter { // ============================================ /** - * Lists the current user's collection characters with optional filters + * Lists a user's collection characters with optional filters + * Works for any user - privacy is enforced server-side */ async listCharacters( + userId: string, params: CollectionListParams = {} ): Promise> { - const response = await this.request('/collection/characters', { - method: 'GET', - query: params - }) + const response = await this.request( + `/users/${userId}/collection/characters`, + { + method: 'GET', + query: params + } + ) return { results: response.characters, @@ -131,17 +135,17 @@ export class CollectionAdapter extends BaseAdapter { } /** - * Gets the IDs of all characters in the current user's collection + * Gets the IDs of all characters in a user's collection * Useful for filtering out already-owned characters in the add modal */ - async getCollectedCharacterIds(): Promise { + async getCollectedCharacterIds(userId: string): Promise { // Fetch all pages to get complete list const allIds: string[] = [] let page = 1 let hasMore = true while (hasMore) { - const response = await this.listCharacters({ page, limit: 100 }) + const response = await this.listCharacters(userId, { page, limit: 100 }) allIds.push(...response.results.map((c) => c.character.id)) hasMore = page < response.totalPages page++ @@ -155,13 +159,17 @@ export class CollectionAdapter extends BaseAdapter { // ============================================ /** - * Lists the current user's collection weapons with optional filters + * Lists a user's collection weapons with optional filters + * Works for any user - privacy is enforced server-side */ - async listWeapons(params: CollectionListParams = {}): Promise> { + async listWeapons( + userId: string, + params: CollectionListParams = {} + ): Promise> { const response = await this.request<{ weapons: CollectionWeapon[] meta: { count: number; totalPages: number; perPage: number; currentPage: number } - }>('/collection/weapons', { + }>(`/users/${userId}/collection/weapons`, { method: 'GET', query: params }) @@ -213,13 +221,17 @@ export class CollectionAdapter extends BaseAdapter { // ============================================ /** - * Lists the current user's collection summons with optional filters + * Lists a user's collection summons with optional filters + * Works for any user - privacy is enforced server-side */ - async listSummons(params: CollectionListParams = {}): Promise> { + async listSummons( + userId: string, + params: CollectionListParams = {} + ): Promise> { const response = await this.request<{ summons: CollectionSummon[] meta: { count: number; totalPages: number; perPage: number; currentPage: number } - }>('/collection/summons', { + }>(`/users/${userId}/collection/summons`, { method: 'GET', query: params }) @@ -304,49 +316,6 @@ export class CollectionAdapter extends BaseAdapter { }) } - // ============================================ - // Public Collection (viewing other users) - // ============================================ - - /** - * Gets a user's public collection (respects privacy settings) - * @param userId - The user's ID - * @param type - Optional type filter: 'characters', 'weapons', 'summons', 'job_accessories' - */ - async getPublicCollection( - userId: string, - type?: 'characters' | 'weapons' | 'summons' | 'job_accessories' - ): Promise { - return this.request(`/users/${userId}/collection`, { - method: 'GET', - query: type ? { type } : undefined - }) - } - - /** - * Gets a user's public character collection - */ - async getPublicCharacters(userId: string): Promise { - const response = await this.getPublicCollection(userId, 'characters') - return response.characters || [] - } - - /** - * Gets a user's public weapon collection - */ - async getPublicWeapons(userId: string): Promise { - const response = await this.getPublicCollection(userId, 'weapons') - return response.weapons || [] - } - - /** - * Gets a user's public summon collection - */ - async getPublicSummons(userId: string): Promise { - const response = await this.getPublicCollection(userId, 'summons') - return response.summons || [] - } - // ============================================ // Cache Management // ============================================ diff --git a/src/lib/api/queries/collection.queries.ts b/src/lib/api/queries/collection.queries.ts index 5e9fb4d1..bb88f0cd 100644 --- a/src/lib/api/queries/collection.queries.ts +++ b/src/lib/api/queries/collection.queries.ts @@ -27,6 +27,14 @@ export interface CollectionPageResult { perPage: number } +/** + * Initial data structure for collection infinite queries + */ +export interface CollectionInitialData { + pages: CollectionPageResult[] + pageParams: number[] +} + /** * Collection query options factory * @@ -35,25 +43,33 @@ export interface CollectionPageResult { * import { createQuery, createInfiniteQuery } from '@tanstack/svelte-query' * import { collectionQueries } from '$lib/api/queries/collection.queries' * - * // Own collection characters - * const characters = createInfiniteQuery(() => collectionQueries.characters()) - * - * // Public collection - * const publicChars = createQuery(() => collectionQueries.publicCharacters(userId)) + * // Any user's collection characters (privacy enforced server-side) + * const characters = createInfiniteQuery(() => collectionQueries.characters(userId)) * * // Collected character IDs (for filtering add modal) - * const ownedIds = createQuery(() => collectionQueries.collectedCharacterIds()) + * const ownedIds = createQuery(() => collectionQueries.collectedCharacterIds(userId)) * ``` */ export const collectionQueries = { /** - * Current user's collection characters with infinite scroll + * User's collection characters with infinite scroll + * Works for any user - privacy is enforced server-side + * + * @param userId - The user whose collection to fetch + * @param filters - Optional filters for element, rarity, etc. + * @param enabled - Whether the query is enabled (default: true) + * @param initialData - Optional initial data for SSR hydration */ - characters: (filters?: CollectionFilters) => + characters: ( + userId: string, + filters?: CollectionFilters, + enabled: boolean = true, + initialData?: CollectionInitialData + ) => infiniteQueryOptions({ - queryKey: ['collection', 'characters', filters] as const, + queryKey: ['collection', 'characters', userId, filters] as const, queryFn: async ({ pageParam }): Promise> => { - const response = await collectionAdapter.listCharacters({ + const response = await collectionAdapter.listCharacters(userId, { ...filters, page: pageParam }) @@ -73,17 +89,20 @@ export const collectionQueries = { return undefined }, staleTime: 1000 * 60 * 2, // 2 minutes - gcTime: 1000 * 60 * 15 // 15 minutes + gcTime: 1000 * 60 * 15, // 15 minutes + enabled, + initialData }), /** - * Current user's collection weapons with infinite scroll + * User's collection weapons with infinite scroll + * Works for any user - privacy is enforced server-side */ - weapons: (filters?: CollectionFilters) => + weapons: (userId: string, filters?: CollectionFilters) => infiniteQueryOptions({ - queryKey: ['collection', 'weapons', filters] as const, + queryKey: ['collection', 'weapons', userId, filters] as const, queryFn: async ({ pageParam }): Promise> => { - const response = await collectionAdapter.listWeapons({ + const response = await collectionAdapter.listWeapons(userId, { ...filters, page: pageParam }) @@ -107,13 +126,14 @@ export const collectionQueries = { }), /** - * Current user's collection summons with infinite scroll + * User's collection summons with infinite scroll + * Works for any user - privacy is enforced server-side */ - summons: (filters?: CollectionFilters) => + summons: (userId: string, filters?: CollectionFilters) => infiniteQueryOptions({ - queryKey: ['collection', 'summons', filters] as const, + queryKey: ['collection', 'summons', userId, filters] as const, queryFn: async ({ pageParam }): Promise> => { - const response = await collectionAdapter.listSummons({ + const response = await collectionAdapter.listSummons(userId, { ...filters, page: pageParam }) @@ -137,13 +157,14 @@ export const collectionQueries = { }), /** - * Get IDs of characters already in the user's collection + * Get IDs of characters already in a user's collection * Used to filter out owned characters in the add modal */ - collectedCharacterIds: () => + collectedCharacterIds: (userId: string) => queryOptions({ - queryKey: ['collection', 'characters', 'ids'] as const, - queryFn: () => collectionAdapter.getCollectedCharacterIds(), + queryKey: ['collection', 'characters', 'ids', userId] as const, + queryFn: () => collectionAdapter.getCollectedCharacterIds(userId), + enabled: !!userId, staleTime: 1000 * 60 * 5, // 5 minutes gcTime: 1000 * 60 * 30 // 30 minutes }), @@ -158,42 +179,6 @@ export const collectionQueries = { enabled: !!id, staleTime: 1000 * 60 * 5, gcTime: 1000 * 60 * 30 - }), - - /** - * Public collection for a user (respects privacy) - */ - publicCharacters: (userId: string) => - queryOptions({ - queryKey: ['collection', 'public', userId, 'characters'] as const, - queryFn: () => collectionAdapter.getPublicCharacters(userId), - enabled: !!userId, - staleTime: 1000 * 60 * 5, - gcTime: 1000 * 60 * 30 - }), - - /** - * Public weapon collection for a user - */ - publicWeapons: (userId: string) => - queryOptions({ - queryKey: ['collection', 'public', userId, 'weapons'] as const, - queryFn: () => collectionAdapter.getPublicWeapons(userId), - enabled: !!userId, - staleTime: 1000 * 60 * 5, - gcTime: 1000 * 60 * 30 - }), - - /** - * Public summon collection for a user - */ - publicSummons: (userId: string) => - queryOptions({ - queryKey: ['collection', 'public', userId, 'summons'] as const, - queryFn: () => collectionAdapter.getPublicSummons(userId), - enabled: !!userId, - staleTime: 1000 * 60 * 5, - gcTime: 1000 * 60 * 30 }) } @@ -210,24 +195,33 @@ export const collectionQueries = { * // Invalidate all collection data * queryClient.invalidateQueries({ queryKey: collectionKeys.all }) * - * // Invalidate only characters - * queryClient.invalidateQueries({ queryKey: collectionKeys.characters() }) + * // Invalidate only characters for a user + * queryClient.invalidateQueries({ queryKey: collectionKeys.characters(userId) }) * ``` */ export const collectionKeys = { all: ['collection'] as const, - characters: () => [...collectionKeys.all, 'characters'] as const, - characterList: (filters?: CollectionFilters) => - [...collectionKeys.characters(), filters] as const, + characters: (userId?: string) => + userId + ? ([...collectionKeys.all, 'characters', userId] as const) + : ([...collectionKeys.all, 'characters'] as const), + characterList: (userId: string, filters?: CollectionFilters) => + [...collectionKeys.characters(userId), filters] as const, character: (id: string) => [...collectionKeys.all, 'character', id] as const, - characterIds: () => [...collectionKeys.characters(), 'ids'] as const, - weapons: () => [...collectionKeys.all, 'weapons'] as const, - weaponList: (filters?: CollectionFilters) => [...collectionKeys.weapons(), filters] as const, - summons: () => [...collectionKeys.all, 'summons'] as const, - summonList: (filters?: CollectionFilters) => [...collectionKeys.summons(), filters] as const, - public: (userId: string) => [...collectionKeys.all, 'public', userId] as const, - publicCharacters: (userId: string) => - [...collectionKeys.public(userId), 'characters'] as const, - publicWeapons: (userId: string) => [...collectionKeys.public(userId), 'weapons'] as const, - publicSummons: (userId: string) => [...collectionKeys.public(userId), 'summons'] as const + characterIds: (userId?: string) => + userId + ? ([...collectionKeys.all, 'characters', 'ids', userId] as const) + : ([...collectionKeys.all, 'characters', 'ids'] as const), + weapons: (userId?: string) => + userId + ? ([...collectionKeys.all, 'weapons', userId] as const) + : ([...collectionKeys.all, 'weapons'] as const), + weaponList: (userId: string, filters?: CollectionFilters) => + [...collectionKeys.weapons(userId), filters] as const, + summons: (userId?: string) => + userId + ? ([...collectionKeys.all, 'summons', userId] as const) + : ([...collectionKeys.all, 'summons'] as const), + summonList: (userId: string, filters?: CollectionFilters) => + [...collectionKeys.summons(userId), filters] as const }