useUserGroups: consolidate 4 dedup'd CatalogGroupsComposer call sites

Four independent components used to send 'new CatalogGroupsComposer()'
on mount and listen for GuildMembershipsMessageEvent:

  - useCatalog (writing into catalogOptions.groups)
  - CatalogLayoutGuildForumView
  - CatalogGuildSelectorWidgetView
  - WiredSelectorUsersGroupView
  - WiredConditionActorIsGroupMemberView

Each fired its own request and re-listened independently. With four
of them mounted in the wired-tools panel during a builder session,
the same packet went out four times.

New useUserGroups() hook wraps the request/response pair with
useNitroQuery (queryKey ['nitro', 'user', 'groups'], staleTime
Infinity — guild membership is session-stable). All four consumers
now read 'const { data: groups = [] } = useUserGroups()' and React
Query dedups: one composer at the first mount, all subsequent mounts
get the cached array.

Drops 'groups' from ICatalogOptions and the corresponding listener +
prev-state-merge from useCatalog — no remaining consumer reads it.
This commit is contained in:
simoleo89
2026-05-11 22:14:39 +02:00
parent eeb9cc66a5
commit 2d9785e931
8 changed files with 51 additions and 67 deletions
+1 -12
View File
@@ -1,4 +1,4 @@
import { BuildersClubFurniCountMessageEvent, BuildersClubPlaceRoomItemMessageComposer, BuildersClubPlaceWallItemMessageComposer, BuildersClubQueryFurniCountMessageComposer, BuildersClubSubscriptionStatusMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent, CatalogPublishedMessageEvent, ClubGiftInfoEvent, CreateLinkEvent, FrontPageItem, FurniturePlaceComposer, FurniturePlacePaintComposer, GetCatalogIndexComposer, GetCatalogPageComposer, GetClubGiftInfo, GetRoomEngine, GetSessionDataManager, GetTickerTime, GuildMembershipsMessageEvent, HabboClubOffersMessageEvent, LegacyDataType, LimitedEditionSoldOutEvent, MarketplaceMakeOfferResult, NodeData, ProductOfferEvent, PurchaseErrorMessageEvent, PurchaseFromCatalogComposer, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, RoomControllerLevel, RoomEngineObjectPlacedEvent, RoomObjectCategory, RoomObjectPlacementSource, RoomObjectType, RoomObjectVariable, RoomPreviewer, SellablePetPalettesMessageEvent, Vector3d } from '@nitrots/nitro-renderer';
import { BuildersClubFurniCountMessageEvent, BuildersClubPlaceRoomItemMessageComposer, BuildersClubPlaceWallItemMessageComposer, BuildersClubQueryFurniCountMessageComposer, BuildersClubSubscriptionStatusMessageEvent, CatalogPageMessageEvent, CatalogPagesListEvent, CatalogPublishedMessageEvent, ClubGiftInfoEvent, CreateLinkEvent, FrontPageItem, FurniturePlaceComposer, FurniturePlacePaintComposer, GetCatalogIndexComposer, GetCatalogPageComposer, GetClubGiftInfo, GetRoomEngine, GetSessionDataManager, GetTickerTime, HabboClubOffersMessageEvent, LegacyDataType, LimitedEditionSoldOutEvent, MarketplaceMakeOfferResult, NodeData, ProductOfferEvent, PurchaseErrorMessageEvent, PurchaseFromCatalogComposer, PurchaseNotAllowedMessageEvent, PurchaseOKMessageEvent, RoomControllerLevel, RoomEngineObjectPlacedEvent, RoomObjectCategory, RoomObjectPlacementSource, RoomObjectType, RoomObjectVariable, RoomPreviewer, SellablePetPalettesMessageEvent, Vector3d } from '@nitrots/nitro-renderer';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useBetween } from 'use-between';
import { BuilderFurniPlaceableStatus, CatalogNode, CatalogPage, CatalogPetPalette, CatalogType, DispatchUiEvent, FurniCategory, GetFurnitureData, GetProductDataForLocalization, GetRoomSession, ICatalogNode, ICatalogOptions, ICatalogPage, IPageLocalization, IProduct, IPurchasableOffer, IPurchaseOptions, LocalizeText, NotificationAlertType, Offer, PageLocalization, PlacedObjectPurchaseData, PlaySound, Product, ProductTypeEnum, RequestedPage, SearchResult, SendMessageComposer, SoundNames } from '../../api';
@@ -756,17 +756,6 @@ const useCatalogState = () =>
});
});
useMessageEvent<GuildMembershipsMessageEvent>(GuildMembershipsMessageEvent, event =>
{
const parser = event.getParser();
setCatalogOptions(prevValue =>
{
const groups = parser.groups;
return { ...prevValue, groups };
});
});
useMessageEvent<MarketplaceMakeOfferResult>(MarketplaceMakeOfferResult, event =>
{
+1
View File
@@ -1 +1,2 @@
export * from './useGroup';
export * from './useUserGroups';
+32
View File
@@ -0,0 +1,32 @@
import { CatalogGroupsComposer, GuildMembershipsMessageEvent, HabboGroupEntryData } from '@nitrots/nitro-renderer';
import { UseQueryResult } from '@tanstack/react-query';
import { useNitroQuery } from '../../api/nitro-query';
/**
* The list of guilds the current Habbo belongs to, as returned by
* the `CatalogGroupsComposer` → `GuildMembershipsMessageEvent`
* request/response pair.
*
* Cached at session level: the membership list is stable for the
* session unless the user joins/leaves a guild (in which case the
* relevant flow should invalidate the `['nitro', 'user', 'groups']`
* query key, which today nobody does — re-mounting the consumer
* refetches via React Query's default behavior).
*
* Replaces three duplicate request+listener pairs that previously
* each issued their own CatalogGroupsComposer:
* - useCatalog (catalogOptions.groups)
* - WiredSelectorUsersGroupView
* - WiredConditionActorIsGroupMemberView
*/
export const useUserGroups = (
options: { enabled?: boolean } = {}
): UseQueryResult<HabboGroupEntryData[]> =>
useNitroQuery<GuildMembershipsMessageEvent, HabboGroupEntryData[]>({
key: [ 'nitro', 'user', 'groups' ],
request: () => new CatalogGroupsComposer(),
parser: GuildMembershipsMessageEvent,
select: event => (event.getParser().groups || []),
enabled: options.enabled,
staleTime: Infinity
});