diff --git a/src/api/catalog/ICatalogOptions.ts b/src/api/catalog/ICatalogOptions.ts index 9aeec55..a666d44 100644 --- a/src/api/catalog/ICatalogOptions.ts +++ b/src/api/catalog/ICatalogOptions.ts @@ -1,9 +1,8 @@ -import { ClubGiftInfoParser, ClubOfferData, HabboGroupEntryData, MarketplaceConfigurationMessageParser } from '@nitrots/nitro-renderer'; +import { ClubGiftInfoParser, ClubOfferData, MarketplaceConfigurationMessageParser } from '@nitrots/nitro-renderer'; import { CatalogPetPalette } from './CatalogPetPalette'; export interface ICatalogOptions { - groups?: HabboGroupEntryData[]; petPalettes?: CatalogPetPalette[]; clubOffers?: ClubOfferData[]; clubOffersByWindowId?: Record; diff --git a/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx b/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx index 21aabf4..b7d9cf9 100644 --- a/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx +++ b/src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx @@ -1,8 +1,7 @@ -import { CatalogGroupsComposer } from '@nitrots/nitro-renderer'; -import { FC, useEffect, useState } from 'react'; -import { SanitizeHtml, SendMessageComposer } from '../../../../../api'; +import { FC, useState } from 'react'; +import { SanitizeHtml } from '../../../../../api'; import { Column, Grid, Text } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalog, useUserGroups } from '../../../../../hooks'; import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView'; import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView'; import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView'; @@ -13,13 +12,8 @@ export const CatalogLayouGuildForumView: FC = props => { const { page = null } = props; const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(0); - const { currentOffer = null, setCurrentOffer = null, catalogOptions = null } = useCatalog(); - const { groups = null } = catalogOptions; - - useEffect(() => - { - SendMessageComposer(new CatalogGroupsComposer()); - }, [ page ]); + const { currentOffer = null, setCurrentOffer = null } = useCatalog(); + const { data: groups = null } = useUserGroups(); return ( <> diff --git a/src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx index 15c60a0..d98ba4c 100644 --- a/src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx @@ -1,14 +1,14 @@ -import { CatalogGroupsComposer, StringDataType } from '@nitrots/nitro-renderer'; +import { StringDataType } from '@nitrots/nitro-renderer'; import { FC, useEffect, useMemo, useState } from 'react'; -import { LocalizeText, SendMessageComposer } from '../../../../../api'; +import { LocalizeText } from '../../../../../api'; import { Button, Flex } from '../../../../../common'; -import { useCatalog } from '../../../../../hooks'; +import { useCatalog, useUserGroups } from '../../../../../hooks'; export const CatalogGuildSelectorWidgetView: FC<{}> = props => { const [ selectedGroupIndex, setSelectedGroupIndex ] = useState(0); - const { currentOffer = null, catalogOptions = null, setPurchaseOptions = null } = useCatalog(); - const { groups = null } = catalogOptions; + const { currentOffer = null, setPurchaseOptions = null } = useCatalog(); + const { data: groups = null } = useUserGroups(); const previewStuffData = useMemo(() => { @@ -41,11 +41,6 @@ export const CatalogGuildSelectorWidgetView: FC<{}> = props => }); }, [ currentOffer, previewStuffData, setPurchaseOptions ]); - useEffect(() => - { - SendMessageComposer(new CatalogGroupsComposer()); - }, []); - if(!groups || !groups.length) { return ( diff --git a/src/components/wired/views/conditions/WiredConditionActorIsGroupMemberView.tsx b/src/components/wired/views/conditions/WiredConditionActorIsGroupMemberView.tsx index c6f33af..4479570 100644 --- a/src/components/wired/views/conditions/WiredConditionActorIsGroupMemberView.tsx +++ b/src/components/wired/views/conditions/WiredConditionActorIsGroupMemberView.tsx @@ -1,8 +1,7 @@ -import { CatalogGroupsComposer, GuildMembershipsMessageEvent, HabboGroupEntryData } from '@nitrots/nitro-renderer'; import { FC, useEffect, useMemo, useState } from 'react'; -import { LocalizeText, SendMessageComposer, WiredFurniType } from '../../../../api'; +import { LocalizeText, WiredFurniType } from '../../../../api'; import { Text } from '../../../../common'; -import { useMessageEvent, useWired } from '../../../../hooks'; +import { useUserGroups, useWired } from '../../../../hooks'; import { WiredSourcesSelector } from '../WiredSourcesSelector'; import { WiredConditionBaseView } from './WiredConditionBaseView'; @@ -16,25 +15,13 @@ interface WiredConditionActorIsGroupMemberViewProps export const WiredConditionActorIsGroupMemberView: FC = ({ negative = false }) => { - const [ groups, setGroups ] = useState([]); + const { data: groups = [] } = useUserGroups(); const [ userSource, setUserSource ] = useState(0); const [ groupType, setGroupType ] = useState(GROUP_CURRENT_ROOM); const [ selectedGroupId, setSelectedGroupId ] = useState(0); const [ quantifier, setQuantifier ] = useState(0); const { trigger = null, setIntParams = null } = useWired(); - useMessageEvent(GuildMembershipsMessageEvent, event => - { - const parser = event.getParser(); - - setGroups(parser.groups || []); - }); - - useEffect(() => - { - SendMessageComposer(new CatalogGroupsComposer()); - }, []); - useEffect(() => { if(!trigger) return; diff --git a/src/components/wired/views/selectors/WiredSelectorUsersGroupView.tsx b/src/components/wired/views/selectors/WiredSelectorUsersGroupView.tsx index 3f7ef5b..df5ff86 100644 --- a/src/components/wired/views/selectors/WiredSelectorUsersGroupView.tsx +++ b/src/components/wired/views/selectors/WiredSelectorUsersGroupView.tsx @@ -1,8 +1,7 @@ -import { CatalogGroupsComposer, GuildMembershipsMessageEvent, HabboGroupEntryData } from '@nitrots/nitro-renderer'; import { FC, useEffect, useMemo, useState } from 'react'; -import { LocalizeText, SendMessageComposer } from '../../../../api'; +import { LocalizeText } from '../../../../api'; import { Text } from '../../../../common'; -import { useMessageEvent, useWired } from '../../../../hooks'; +import { useUserGroups, useWired } from '../../../../hooks'; import { WiredSelectorBaseView } from './WiredSelectorBaseView'; const GROUP_CURRENT_ROOM = 0; @@ -10,25 +9,13 @@ const GROUP_SELECTED = 1; export const WiredSelectorUsersGroupView: FC<{}> = () => { - const [ groups, setGroups ] = useState([]); + const { data: groups = [] } = useUserGroups(); const [ groupType, setGroupType ] = useState(GROUP_CURRENT_ROOM); const [ selectedGroupId, setSelectedGroupId ] = useState(0); const [ filterExisting, setFilterExisting ] = useState(false); const [ invert, setInvert ] = useState(false); const { trigger = null, setIntParams = null } = useWired(); - useMessageEvent(GuildMembershipsMessageEvent, event => - { - const parser = event.getParser(); - - setGroups(parser.groups || []); - }); - - useEffect(() => - { - SendMessageComposer(new CatalogGroupsComposer()); - }, []); - useEffect(() => { if(!trigger) return; diff --git a/src/hooks/catalog/useCatalog.ts b/src/hooks/catalog/useCatalog.ts index 1eecbb1..cbd9cff 100644 --- a/src/hooks/catalog/useCatalog.ts +++ b/src/hooks/catalog/useCatalog.ts @@ -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, event => - { - const parser = event.getParser(); - - setCatalogOptions(prevValue => - { - const groups = parser.groups; - - return { ...prevValue, groups }; - }); - }); useMessageEvent(MarketplaceMakeOfferResult, event => { diff --git a/src/hooks/groups/index.ts b/src/hooks/groups/index.ts index 95ef733..f3e74b5 100644 --- a/src/hooks/groups/index.ts +++ b/src/hooks/groups/index.ts @@ -1 +1,2 @@ export * from './useGroup'; +export * from './useUserGroups'; diff --git a/src/hooks/groups/useUserGroups.ts b/src/hooks/groups/useUserGroups.ts new file mode 100644 index 0000000..470e55c --- /dev/null +++ b/src/hooks/groups/useUserGroups.ts @@ -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 => + useNitroQuery({ + key: [ 'nitro', 'user', 'groups' ], + request: () => new CatalogGroupsComposer(), + parser: GuildMembershipsMessageEvent, + select: event => (event.getParser().groups || []), + enabled: options.enabled, + staleTime: Infinity + });