useClubGifts + useNitroEventInvalidator: close the catalogOptions bag

This commit drains the last field out of ICatalogOptions (clubGifts)
and deletes the interface — useCatalog no longer owns a catch-all
mutable object that downstream components stuff data into.

Two pieces:

1) New useNitroEventInvalidator(eventType, queryKey, accept?) — a
   small companion to useNitroQuery for the case where the server
   pushes the same event unprompted (e.g. ClubGiftInfoEvent fires
   both as the response to GetClubGiftInfo and again after the user
   claims a gift via SelectClubGiftComposer). It calls
   queryClient.invalidateQueries() on each matching push so the
   next render of any subscriber triggers a fresh queryFn.

2) New useClubGifts() — useNitroQuery on the ClubGiftInfoEvent
   pair, paired with useNitroEventInvalidator so server-driven
   pushes refresh the cache automatically. CatalogLayoutVipGiftsView
   now consumes the query directly. The local optimistic
   'giftsAvailable--' mutation (which side-effected the parser
   object passed back to the catalog state!) is dropped — the
   server's authoritative ClubGiftInfoEvent push is the single
   source of truth via the invalidator.

useCatalog drops the matching listener + the GetClubGiftInfo dispatch
from the catalog-open effect. ICatalogOptions is now empty and
deleted; the catalogOptions / setCatalogOptions state + return-shape
field are removed from useCatalog along with the import.
This commit is contained in:
simoleo89
2026-05-11 22:38:32 +02:00
parent 9a807bf335
commit 7b062299de
8 changed files with 106 additions and 42 deletions
+1
View File
@@ -1 +1,2 @@
export * from './createNitroQuery';
export * from './useNitroEventInvalidator';
@@ -0,0 +1,48 @@
import { IMessageEvent, MessageEvent } from '@nitrots/nitro-renderer';
import { QueryKey, useQueryClient } from '@tanstack/react-query';
import { useMessageEvent } from '../../hooks/events/useMessageEvent';
/**
* Invalidate a TanStack query slot every time the renderer pushes the
* matching parser event. Companion to useNitroQuery for the case where
* the server can push fresh data unprompted (e.g. ClubGiftInfoEvent
* fires both as the response to GetClubGiftInfo and again after the
* user claims a gift via SelectClubGiftComposer).
*
* Usage:
*
* const { data: clubGifts } = useNitroQuery({
* key: ['nitro', 'catalog', 'clubGifts'],
* request: () => new GetClubGiftInfo(),
* parser: ClubGiftInfoEvent,
* select: e => e.getParser(),
* });
*
* // re-fetch on every server push:
* useNitroEventInvalidator(ClubGiftInfoEvent, ['nitro', 'catalog', 'clubGifts']);
*
* Optional `accept` predicate filters out events that don't belong to
* this query slot — useful when the same parser is multiplexed across
* multiple correlated queries (mirrors useNitroQuery.accept).
*
* Implementation: the renderer push triggers `queryClient.invalidateQueries`,
* which marks the slot stale; the next subscriber render triggers a
* fresh fetch via useNitroQuery's queryFn. If nobody is currently
* subscribed, the invalidation is a no-op (TanStack drops stale entries
* with no active observers per its garbage-collection policy).
*/
export const useNitroEventInvalidator = <T extends IMessageEvent>(
eventType: typeof MessageEvent,
queryKey: QueryKey,
accept?: (event: T) => boolean
) =>
{
const queryClient = useQueryClient();
useMessageEvent<T>(eventType, event =>
{
if(accept && !accept(event)) return;
queryClient.invalidateQueries({ queryKey });
});
};