Files
Nitro-V3/src/components/catalog/views/page/layout/CatalogLayoutRoomAdsView.tsx
T
simoleo89 388fb8ed34 Migrate CatalogLayoutRoomAdsView's room-ad fetch to useNitroQuery
Second concrete adoption of proposal #2 (first was OfferView).

Before
- A useState<RoomEntryData[]>([]) for availableRooms.
- A useMessageEvent<RoomAdPurchaseInfoEvent> handler that
  set the state on each parser event.
- A useEffect on mount that dispatched two composers, one of which
  was GetRoomAdPurchaseInfoComposer paired with the parser above.

After
- A single useNitroQuery call wires the request and parser as one
  read-only query. The select extracts parser.rooms with a default
  empty array.
- staleTime is 60s — opening the same panel within a minute reuses
  the cached value; the composer is not re-dispatched. Useful here
  because the user navigates between catalog tabs.
- The mount-only useEffect no longer dispatches the room-ad composer;
  the second composer (GetUserEventCatsMessageComposer) stays where
  it was — that one feeds useNavigator state and isn't a
  request-response pair this component owns.

Why this file
- It was the cleanest pattern in the catalog tree: no correlation
  keys, no conditional filter on the parser, no other writes to
  availableRooms. The pure derive-from-event case useNitroQuery is
  built for.
- The big god-hook useCatalog (1100 LOC) still owns most of the
  catalog data layer; migrating that needs the data/uiState/actions
  split first.

Verification
- yarn test: 49/49 still passing.
- yarn eslint on the touched file: 1 error (the pre-existing
  set-state-in-effect on line 36, unchanged — baseline matches).
- The previous useMessageEvent import was removed cleanly.
2026-05-11 16:40:43 +00:00

121 lines
5.5 KiB
TypeScript

import { GetRoomAdPurchaseInfoComposer, GetUserEventCatsMessageComposer, PurchaseRoomAdMessageComposer, RoomAdPurchaseInfoEvent, RoomEntryData } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react';
import { LocalizeText, SendMessageComposer } from '../../../../../api';
import { useNitroQuery } from '../../../../../api/nitro-query';
import { Button, Column, Text } from '../../../../../common';
import { useCatalog, useNavigator, useRoomPromote } from '../../../../../hooks';
import { NitroInput } from '../../../../../layout';
import { CatalogLayoutProps } from './CatalogLayout.types';
let isPurchasingAd = false;
export const CatalogLayoutRoomAdsView: FC<CatalogLayoutProps> = props =>
{
const { page = null } = props;
const [ eventName, setEventName ] = useState<string>('');
const [ eventDesc, setEventDesc ] = useState<string>('');
const [ roomId, setRoomId ] = useState<number>(-1);
const [ extended, setExtended ] = useState<boolean>(false);
const [ categoryId, setCategoryId ] = useState<number>(1);
const { categories = null } = useNavigator();
const { setIsVisible = null } = useCatalog();
const { promoteInformation, isExtended, setIsExtended } = useRoomPromote();
const { data: availableRooms = [] } = useNitroQuery<RoomAdPurchaseInfoEvent, RoomEntryData[]>({
key: [ 'nitro', 'catalog', 'room-ad-purchase-info' ],
request: () => new GetRoomAdPurchaseInfoComposer(),
parser: RoomAdPurchaseInfoEvent,
select: e => e.getParser()?.rooms ?? [],
staleTime: 60_000
});
useEffect(() =>
{
if(isExtended)
{
setRoomId(promoteInformation.data.flatId);
setEventName(promoteInformation.data.eventName);
setEventDesc(promoteInformation.data.eventDescription);
setCategoryId(promoteInformation.data.categoryId);
setExtended(isExtended); // This is for sending to packet
setIsExtended(false); // This is from hook useRoomPromotte
}
}, [ isExtended, eventName, eventDesc, categoryId, promoteInformation.data, setIsExtended ]);
const resetData = () =>
{
setRoomId(-1);
setEventName('');
setEventDesc('');
setCategoryId(1);
setIsExtended(false);
setIsVisible(false);
};
const purchaseAd = () =>
{
if(isPurchasingAd) return;
isPurchasingAd = true;
const pageId = page.pageId;
const offerId = page.offers.length >= 1 ? page.offers[0].offerId : -1;
const flatId = roomId;
const name = eventName;
const desc = eventDesc;
const catId = categoryId;
SendMessageComposer(new PurchaseRoomAdMessageComposer(pageId, offerId, flatId, name, extended, desc, catId));
resetData();
};
useEffect(() =>
{
// TODO: someone needs to fix this for morningstar
SendMessageComposer(new GetUserEventCatsMessageComposer());
}, []);
return (<>
<Text bold center>{ LocalizeText('roomad.catalog_header') }</Text>
<Column className="text-black" overflow="hidden" size={ 12 }>
<div>{ LocalizeText('roomad.catalog_text', [ 'duration' ], [ '120' ]) }</div>
<div className="p-1 rounded bg-muted">
<Column gap={ 2 }>
<Text bold>{ LocalizeText('navigator.category') }</Text>
<select className="form-select form-select-sm" disabled={ extended } value={ categoryId } onChange={ event => setCategoryId(parseInt(event.target.value)) }>
{ categories && categories.map((cat, index) => <option key={ index } value={ cat.id }>{ LocalizeText(cat.name) }</option>) }
</select>
</Column>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('roomad.catalog_name') }</Text>
<NitroInput maxLength={ 64 } readOnly={ extended } value={ eventName } onChange={ event => setEventName(event.target.value) } />
</div>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('roomad.catalog_description') }</Text>
<textarea className="min-h-[calc(1.5em+ .5rem+2px)] px-[.5rem] py-[.25rem] rounded-[.2rem] form-control-sm" maxLength={ 64 } readOnly={ extended } value={ eventDesc } onChange={ event => setEventDesc(event.target.value) } />
</div>
<div className="flex flex-col gap-1">
<Text bold>{ LocalizeText('roomad.catalog_roomname') }</Text>
<select className="form-select form-select-sm" disabled={ extended } value={ roomId } onChange={ event => setRoomId(parseInt(event.target.value)) }>
<option disabled value={ -1 }>{ LocalizeText('roomad.catalog_roomname') }</option>
{ availableRooms && availableRooms.map((room, index) => <option key={ index } value={ room.roomId }>{ room.roomName }</option>) }
</select>
</div>
<div className="flex flex-col gap-1">
<Button disabled={ (!eventName || !eventDesc || roomId === -1) } variant={ (!eventName || !eventDesc || roomId === -1) ? 'danger' : 'success' } onClick={ purchaseAd }>{ extended ? LocalizeText('roomad.extend.event') : LocalizeText('buy') }</Button>
</div>
</div>
</Column>
</>
);
};
interface INavigatorCategory
{
id: number;
name: string;
visible: boolean;
}