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
This commit is contained in:
parent
35b0560749
commit
b8a48771dd
2 changed files with 96 additions and 133 deletions
|
|
@ -19,8 +19,7 @@ import type {
|
||||||
CollectionWeaponInput,
|
CollectionWeaponInput,
|
||||||
CollectionSummonInput,
|
CollectionSummonInput,
|
||||||
CollectionJobAccessoryInput,
|
CollectionJobAccessoryInput,
|
||||||
CollectionFilters,
|
CollectionFilters
|
||||||
CollectionResponse
|
|
||||||
} from '$lib/types/api/collection'
|
} 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(
|
async listCharacters(
|
||||||
|
userId: string,
|
||||||
params: CollectionListParams = {}
|
params: CollectionListParams = {}
|
||||||
): Promise<PaginatedResponse<CollectionCharacter>> {
|
): Promise<PaginatedResponse<CollectionCharacter>> {
|
||||||
const response = await this.request<CollectionCharacterListResponse>('/collection/characters', {
|
const response = await this.request<CollectionCharacterListResponse>(
|
||||||
method: 'GET',
|
`/users/${userId}/collection/characters`,
|
||||||
query: params
|
{
|
||||||
})
|
method: 'GET',
|
||||||
|
query: params
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
results: response.characters,
|
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
|
* Useful for filtering out already-owned characters in the add modal
|
||||||
*/
|
*/
|
||||||
async getCollectedCharacterIds(): Promise<string[]> {
|
async getCollectedCharacterIds(userId: string): Promise<string[]> {
|
||||||
// Fetch all pages to get complete list
|
// Fetch all pages to get complete list
|
||||||
const allIds: string[] = []
|
const allIds: string[] = []
|
||||||
let page = 1
|
let page = 1
|
||||||
let hasMore = true
|
let hasMore = true
|
||||||
|
|
||||||
while (hasMore) {
|
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))
|
allIds.push(...response.results.map((c) => c.character.id))
|
||||||
hasMore = page < response.totalPages
|
hasMore = page < response.totalPages
|
||||||
page++
|
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<PaginatedResponse<CollectionWeapon>> {
|
async listWeapons(
|
||||||
|
userId: string,
|
||||||
|
params: CollectionListParams = {}
|
||||||
|
): Promise<PaginatedResponse<CollectionWeapon>> {
|
||||||
const response = await this.request<{
|
const response = await this.request<{
|
||||||
weapons: CollectionWeapon[]
|
weapons: CollectionWeapon[]
|
||||||
meta: { count: number; totalPages: number; perPage: number; currentPage: number }
|
meta: { count: number; totalPages: number; perPage: number; currentPage: number }
|
||||||
}>('/collection/weapons', {
|
}>(`/users/${userId}/collection/weapons`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
query: params
|
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<PaginatedResponse<CollectionSummon>> {
|
async listSummons(
|
||||||
|
userId: string,
|
||||||
|
params: CollectionListParams = {}
|
||||||
|
): Promise<PaginatedResponse<CollectionSummon>> {
|
||||||
const response = await this.request<{
|
const response = await this.request<{
|
||||||
summons: CollectionSummon[]
|
summons: CollectionSummon[]
|
||||||
meta: { count: number; totalPages: number; perPage: number; currentPage: number }
|
meta: { count: number; totalPages: number; perPage: number; currentPage: number }
|
||||||
}>('/collection/summons', {
|
}>(`/users/${userId}/collection/summons`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
query: params
|
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<CollectionResponse> {
|
|
||||||
return this.request<CollectionResponse>(`/users/${userId}/collection`, {
|
|
||||||
method: 'GET',
|
|
||||||
query: type ? { type } : undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a user's public character collection
|
|
||||||
*/
|
|
||||||
async getPublicCharacters(userId: string): Promise<CollectionCharacter[]> {
|
|
||||||
const response = await this.getPublicCollection(userId, 'characters')
|
|
||||||
return response.characters || []
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a user's public weapon collection
|
|
||||||
*/
|
|
||||||
async getPublicWeapons(userId: string): Promise<CollectionWeapon[]> {
|
|
||||||
const response = await this.getPublicCollection(userId, 'weapons')
|
|
||||||
return response.weapons || []
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a user's public summon collection
|
|
||||||
*/
|
|
||||||
async getPublicSummons(userId: string): Promise<CollectionSummon[]> {
|
|
||||||
const response = await this.getPublicCollection(userId, 'summons')
|
|
||||||
return response.summons || []
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// Cache Management
|
// Cache Management
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,14 @@ export interface CollectionPageResult<T> {
|
||||||
perPage: number
|
perPage: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initial data structure for collection infinite queries
|
||||||
|
*/
|
||||||
|
export interface CollectionInitialData<T> {
|
||||||
|
pages: CollectionPageResult<T>[]
|
||||||
|
pageParams: number[]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collection query options factory
|
* Collection query options factory
|
||||||
*
|
*
|
||||||
|
|
@ -35,25 +43,33 @@ export interface CollectionPageResult<T> {
|
||||||
* import { createQuery, createInfiniteQuery } from '@tanstack/svelte-query'
|
* import { createQuery, createInfiniteQuery } from '@tanstack/svelte-query'
|
||||||
* import { collectionQueries } from '$lib/api/queries/collection.queries'
|
* import { collectionQueries } from '$lib/api/queries/collection.queries'
|
||||||
*
|
*
|
||||||
* // Own collection characters
|
* // Any user's collection characters (privacy enforced server-side)
|
||||||
* const characters = createInfiniteQuery(() => collectionQueries.characters())
|
* const characters = createInfiniteQuery(() => collectionQueries.characters(userId))
|
||||||
*
|
|
||||||
* // Public collection
|
|
||||||
* const publicChars = createQuery(() => collectionQueries.publicCharacters(userId))
|
|
||||||
*
|
*
|
||||||
* // Collected character IDs (for filtering add modal)
|
* // Collected character IDs (for filtering add modal)
|
||||||
* const ownedIds = createQuery(() => collectionQueries.collectedCharacterIds())
|
* const ownedIds = createQuery(() => collectionQueries.collectedCharacterIds(userId))
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export const collectionQueries = {
|
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<CollectionCharacter>
|
||||||
|
) =>
|
||||||
infiniteQueryOptions({
|
infiniteQueryOptions({
|
||||||
queryKey: ['collection', 'characters', filters] as const,
|
queryKey: ['collection', 'characters', userId, filters] as const,
|
||||||
queryFn: async ({ pageParam }): Promise<CollectionPageResult<CollectionCharacter>> => {
|
queryFn: async ({ pageParam }): Promise<CollectionPageResult<CollectionCharacter>> => {
|
||||||
const response = await collectionAdapter.listCharacters({
|
const response = await collectionAdapter.listCharacters(userId, {
|
||||||
...filters,
|
...filters,
|
||||||
page: pageParam
|
page: pageParam
|
||||||
})
|
})
|
||||||
|
|
@ -73,17 +89,20 @@ export const collectionQueries = {
|
||||||
return undefined
|
return undefined
|
||||||
},
|
},
|
||||||
staleTime: 1000 * 60 * 2, // 2 minutes
|
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({
|
infiniteQueryOptions({
|
||||||
queryKey: ['collection', 'weapons', filters] as const,
|
queryKey: ['collection', 'weapons', userId, filters] as const,
|
||||||
queryFn: async ({ pageParam }): Promise<CollectionPageResult<CollectionWeapon>> => {
|
queryFn: async ({ pageParam }): Promise<CollectionPageResult<CollectionWeapon>> => {
|
||||||
const response = await collectionAdapter.listWeapons({
|
const response = await collectionAdapter.listWeapons(userId, {
|
||||||
...filters,
|
...filters,
|
||||||
page: pageParam
|
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({
|
infiniteQueryOptions({
|
||||||
queryKey: ['collection', 'summons', filters] as const,
|
queryKey: ['collection', 'summons', userId, filters] as const,
|
||||||
queryFn: async ({ pageParam }): Promise<CollectionPageResult<CollectionSummon>> => {
|
queryFn: async ({ pageParam }): Promise<CollectionPageResult<CollectionSummon>> => {
|
||||||
const response = await collectionAdapter.listSummons({
|
const response = await collectionAdapter.listSummons(userId, {
|
||||||
...filters,
|
...filters,
|
||||||
page: pageParam
|
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
|
* Used to filter out owned characters in the add modal
|
||||||
*/
|
*/
|
||||||
collectedCharacterIds: () =>
|
collectedCharacterIds: (userId: string) =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: ['collection', 'characters', 'ids'] as const,
|
queryKey: ['collection', 'characters', 'ids', userId] as const,
|
||||||
queryFn: () => collectionAdapter.getCollectedCharacterIds(),
|
queryFn: () => collectionAdapter.getCollectedCharacterIds(userId),
|
||||||
|
enabled: !!userId,
|
||||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||||
gcTime: 1000 * 60 * 30 // 30 minutes
|
gcTime: 1000 * 60 * 30 // 30 minutes
|
||||||
}),
|
}),
|
||||||
|
|
@ -158,42 +179,6 @@ export const collectionQueries = {
|
||||||
enabled: !!id,
|
enabled: !!id,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
gcTime: 1000 * 60 * 30
|
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
|
* // Invalidate all collection data
|
||||||
* queryClient.invalidateQueries({ queryKey: collectionKeys.all })
|
* queryClient.invalidateQueries({ queryKey: collectionKeys.all })
|
||||||
*
|
*
|
||||||
* // Invalidate only characters
|
* // Invalidate only characters for a user
|
||||||
* queryClient.invalidateQueries({ queryKey: collectionKeys.characters() })
|
* queryClient.invalidateQueries({ queryKey: collectionKeys.characters(userId) })
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export const collectionKeys = {
|
export const collectionKeys = {
|
||||||
all: ['collection'] as const,
|
all: ['collection'] as const,
|
||||||
characters: () => [...collectionKeys.all, 'characters'] as const,
|
characters: (userId?: string) =>
|
||||||
characterList: (filters?: CollectionFilters) =>
|
userId
|
||||||
[...collectionKeys.characters(), filters] as const,
|
? ([...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,
|
character: (id: string) => [...collectionKeys.all, 'character', id] as const,
|
||||||
characterIds: () => [...collectionKeys.characters(), 'ids'] as const,
|
characterIds: (userId?: string) =>
|
||||||
weapons: () => [...collectionKeys.all, 'weapons'] as const,
|
userId
|
||||||
weaponList: (filters?: CollectionFilters) => [...collectionKeys.weapons(), filters] as const,
|
? ([...collectionKeys.all, 'characters', 'ids', userId] as const)
|
||||||
summons: () => [...collectionKeys.all, 'summons'] as const,
|
: ([...collectionKeys.all, 'characters', 'ids'] as const),
|
||||||
summonList: (filters?: CollectionFilters) => [...collectionKeys.summons(), filters] as const,
|
weapons: (userId?: string) =>
|
||||||
public: (userId: string) => [...collectionKeys.all, 'public', userId] as const,
|
userId
|
||||||
publicCharacters: (userId: string) =>
|
? ([...collectionKeys.all, 'weapons', userId] as const)
|
||||||
[...collectionKeys.public(userId), 'characters'] as const,
|
: ([...collectionKeys.all, 'weapons'] as const),
|
||||||
publicWeapons: (userId: string) => [...collectionKeys.public(userId), 'weapons'] as const,
|
weaponList: (userId: string, filters?: CollectionFilters) =>
|
||||||
publicSummons: (userId: string) => [...collectionKeys.public(userId), 'summons'] as const
|
[...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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue