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,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<CatalogLayoutProps> = props =>
{
const { page = null } = props;
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState<number>(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 (
<>
@@ -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<number>(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 (
@@ -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<WiredConditionActorIsGroupMemberViewProps> = ({ negative = false }) =>
{
const [ groups, setGroups ] = useState<HabboGroupEntryData[]>([]);
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>(GuildMembershipsMessageEvent, event =>
{
const parser = event.getParser();
setGroups(parser.groups || []);
});
useEffect(() =>
{
SendMessageComposer(new CatalogGroupsComposer());
}, []);
useEffect(() =>
{
if(!trigger) return;
@@ -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<HabboGroupEntryData[]>([]);
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>(GuildMembershipsMessageEvent, event =>
{
const parser = event.getParser();
setGroups(parser.groups || []);
});
useEffect(() =>
{
SendMessageComposer(new CatalogGroupsComposer());
}, []);
useEffect(() =>
{
if(!trigger) return;