fix: auto-inject edit keys for anonymous party creation

- add mutation factory to auto-inject X-Edit-Key headers
- store edit key after party creation
- works for both /teams/new and Party.svelte
This commit is contained in:
Justin Edmund 2025-11-29 05:41:07 -08:00
parent ddd33d1e53
commit a34e0f939b
2 changed files with 48 additions and 3 deletions

View file

@ -19,6 +19,36 @@ import {
} from '$lib/api/adapters/grid.adapter' } from '$lib/api/adapters/grid.adapter'
import { partyKeys } from '$lib/api/queries/party.queries' import { partyKeys } from '$lib/api/queries/party.queries'
import type { Party, GridWeapon, GridCharacter, GridSummon } from '$lib/types/api/party' import type { Party, GridWeapon, GridCharacter, GridSummon } from '$lib/types/api/party'
import { getEditKey } from '$lib/utils/editKeys'
// ============================================================================
// Mutation Factory
// ============================================================================
/**
* Wraps a grid adapter method to automatically inject edit key headers for anonymous users.
* When a party has an edit key stored in localStorage, it's automatically sent in the X-Edit-Key header.
*
* For anonymous users:
* - Edit key is retrieved from localStorage using party shortcode
* - X-Edit-Key header is automatically injected
*
* For authenticated users:
* - No edit key in localStorage
* - Falls back to Bearer token (existing behavior)
*
* @param adapterMethod - The grid adapter method to wrap
* @returns Wrapped method that automatically handles edit key injection
*/
function createGridMutation<TParams extends { partyId: number | string }>(
adapterMethod: (params: TParams, headers?: Record<string, string>) => Promise<any>
) {
return (params: TParams) => {
const editKey = typeof params.partyId === 'string' ? getEditKey(params.partyId) : null
const headers = editKey ? { 'X-Edit-Key': editKey } : undefined
return adapterMethod(params, headers)
}
}
// ============================================================================ // ============================================================================
// Weapon Mutations // Weapon Mutations
@ -50,7 +80,9 @@ export function useCreateGridWeapon() {
const queryClient = useQueryClient() const queryClient = useQueryClient()
return createMutation(() => ({ return createMutation(() => ({
mutationFn: (params: CreateGridWeaponParams) => gridAdapter.createWeapon(params), mutationFn: createGridMutation((params: CreateGridWeaponParams, headers?: Record<string, string>) =>
gridAdapter.createWeapon(params, headers)
),
onSuccess: (_data, params) => { onSuccess: (_data, params) => {
// Invalidate the party to refetch with new weapon // Invalidate the party to refetch with new weapon
queryClient.invalidateQueries({ queryKey: partyKeys.detail(params.partyId) }) queryClient.invalidateQueries({ queryKey: partyKeys.detail(params.partyId) })
@ -245,7 +277,9 @@ export function useCreateGridCharacter() {
const queryClient = useQueryClient() const queryClient = useQueryClient()
return createMutation(() => ({ return createMutation(() => ({
mutationFn: (params: CreateGridCharacterParams) => gridAdapter.createCharacter(params), mutationFn: createGridMutation((params: CreateGridCharacterParams, headers?: Record<string, string>) =>
gridAdapter.createCharacter(params, headers)
),
onSuccess: (_data, params) => { onSuccess: (_data, params) => {
queryClient.invalidateQueries({ queryKey: partyKeys.detail(params.partyId) }) queryClient.invalidateQueries({ queryKey: partyKeys.detail(params.partyId) })
} }
@ -422,7 +456,9 @@ export function useCreateGridSummon() {
const queryClient = useQueryClient() const queryClient = useQueryClient()
return createMutation(() => ({ return createMutation(() => ({
mutationFn: (params: CreateGridSummonParams) => gridAdapter.createSummon(params), mutationFn: createGridMutation((params: CreateGridSummonParams, headers?: Record<string, string>) =>
gridAdapter.createSummon(params, headers)
),
onSuccess: (_data, params) => { onSuccess: (_data, params) => {
queryClient.invalidateQueries({ queryKey: partyKeys.detail(params.partyId) }) queryClient.invalidateQueries({ queryKey: partyKeys.detail(params.partyId) })
} }

View file

@ -11,6 +11,7 @@
import type { SearchResult } from '$lib/api/adapters' import type { SearchResult } from '$lib/api/adapters'
import { partyAdapter, gridAdapter } from '$lib/api/adapters' import { partyAdapter, gridAdapter } from '$lib/api/adapters'
import { getLocalId } from '$lib/utils/localId' import { getLocalId } from '$lib/utils/localId'
import { storeEditKey } from '$lib/utils/editKeys'
// TanStack Query mutations // TanStack Query mutations
import { useCreateParty } from '$lib/api/mutations/party.mutations' import { useCreateParty } from '$lib/api/mutations/party.mutations'
@ -136,6 +137,14 @@
partyId = createdParty.id partyId = createdParty.id
shortcode = createdParty.shortcode shortcode = createdParty.shortcode
// Store edit key for anonymous editing under BOTH identifiers
// - shortcode: for Party.svelte which uses shortcode as partyId
// - UUID: for /teams/new which uses UUID as partyId
if (createdParty.editKey) {
storeEditKey(createdParty.shortcode, createdParty.editKey)
storeEditKey(createdParty.id, createdParty.editKey)
}
if (!partyId || !shortcode) { if (!partyId || !shortcode) {
throw new Error('Party creation did not return ID or shortcode') throw new Error('Party creation did not return ID or shortcode')
} }