Files
Nitro-V3/src/hooks/navigator/useNavigatorStore.ts
T
simoleo89 ee3736474d refactor(navigator): remove search ownership from useNavigatorStore
P2 core surgery: search result + NavigatorSearchEvent listener +
sendSearch + reloadCurrentSearch all leave useNavigatorStore. The new
useNavigatorSearch query hook owns the cache. useNavigatorActions is
deleted entirely — the only two actions it exposed are gone, and no
consumer outside Navigator depended on it.

NavigatorMetadataEvent handler now seeds the UI store's currentTabCode
on first arrival, activating the query the moment top-level contexts
land.

useNavigatorData: searchResult removed from closure and return.
useNavigatorUiState: currentTabCode + currentFilter added.
index.ts: useNavigatorActions removed, useNavigatorSearch added.

NavigatorView.tsx is intentionally broken at this commit and gets
fixed in the next.
2026-05-27 19:20:27 +02:00

288 lines
12 KiB
TypeScript

import { CanCreateRoomEventEvent, CantConnectMessageParser, CreateLinkEvent,
FavouriteChangedEvent, FavouritesEvent, FlatCreatedEvent,
FollowFriendMessageComposer, GenericErrorEvent, GetGuestRoomMessageComposer,
GetGuestRoomResultEvent, GetRoomSessionManager, GetSessionDataManager,
GetUserEventCatsMessageComposer, GetUserFlatCatsMessageComposer,
HabboWebTools, LegacyExternalInterface, NavigatorCategoryDataParser,
NavigatorEventCategoryDataParser, NavigatorHomeRoomEvent,
NavigatorMetadataEvent, NavigatorOpenRoomCreatorEvent, NavigatorSavedSearch,
NavigatorSearchesEvent,
NavigatorTopLevelContext, NitroEventType,
RoomDataParser, RoomEnterErrorEvent, RoomEntryInfoMessageEvent,
RoomForwardEvent, RoomScoreEvent,
SecurityLevel, UserEventCatsEvent, UserFlatCatsEvent,
UserInfoEvent, UserPermissionsEvent } from '@nitrots/nitro-renderer';
import { useCallback, useState } from 'react';
import { CreateRoomSession, GetConfigurationValue, INavigatorData,
LocalizeText, NotificationAlertType, SendMessageComposer,
TryVisitRoom, VisitDesktop } from '../../api';
import { useMessageEvent, useNitroEvent } from '../events';
import { useNotification } from '../notification';
import { useNavigatorUiStore } from './navigatorUiStore';
export const useNavigatorStore = () =>
{
const [ categories, setCategories ] = useState<NavigatorCategoryDataParser[]>(null);
const [ eventCategories, setEventCategories ] = useState<NavigatorEventCategoryDataParser[]>(null);
const [ favouriteRoomIds, setFavouriteRoomIds ] = useState<number[]>([]);
const [ topLevelContext, setTopLevelContext ] = useState<NavigatorTopLevelContext>(null);
const [ topLevelContexts, setTopLevelContexts ] = useState<NavigatorTopLevelContext[]>(null);
const [ navigatorSearches, setNavigatorSearches ] = useState<NavigatorSavedSearch[]>(null);
const [ navigatorData, setNavigatorData ] = useState<INavigatorData>({
settingsReceived: false,
homeRoomId: 0,
enteredGuestRoom: null,
currentRoomOwner: false,
currentRoomId: 0,
currentRoomIsStaffPick: false,
createdFlatId: 0,
avatarId: 0,
roomPicker: false,
eventMod: false,
currentRoomRating: 0,
canRate: true
});
const { simpleAlert = null } = useNotification();
useMessageEvent<FavouritesEvent>(FavouritesEvent, useCallback(event =>
{
const parser = event.getParser();
const favoriteIds = (parser.favoriteRoomIds || []).map((x: any) => Number(x));
setFavouriteRoomIds(favoriteIds);
}, []));
useMessageEvent<FavouriteChangedEvent>(FavouriteChangedEvent, useCallback(event =>
{
const parser = event.getParser();
const roomId = Number(parser.flatId);
const added = !!parser.added;
setFavouriteRoomIds(prev =>
{
const ids = (prev || []).map((x: any) => Number(x));
if(added) return ids.includes(roomId) ? ids : [ ...ids, roomId ];
return ids.filter(id => id !== roomId);
});
}, []));
useMessageEvent<CanCreateRoomEventEvent>(CanCreateRoomEventEvent, useCallback(event =>
{
const parser = event.getParser();
if(parser.canCreate) return;
simpleAlert(LocalizeText(`navigator.cannotcreateevent.error.${ parser.errorCode }`), null, null, null, LocalizeText('navigator.cannotcreateevent.title'));
}, [ simpleAlert ]));
useMessageEvent<UserInfoEvent>(UserInfoEvent, useCallback(event =>
{
SendMessageComposer(new GetUserFlatCatsMessageComposer());
SendMessageComposer(new GetUserEventCatsMessageComposer());
}, []));
useMessageEvent<UserPermissionsEvent>(UserPermissionsEvent, useCallback(event =>
{
const parser = event.getParser();
setNavigatorData(prev => ({
...prev,
eventMod: parser.securityLevel >= SecurityLevel.MODERATOR,
roomPicker: parser.securityLevel >= SecurityLevel.COMMUNITY
}));
}, []));
useMessageEvent<RoomForwardEvent>(RoomForwardEvent, useCallback(event =>
{
const parser = event.getParser();
TryVisitRoom(parser.roomId);
}, []));
useMessageEvent<RoomEntryInfoMessageEvent>(RoomEntryInfoMessageEvent, useCallback(event =>
{
const parser = event.getParser();
setNavigatorData(prev => ({
...prev,
enteredGuestRoom: null,
currentRoomOwner: parser.isOwner,
currentRoomId: parser.roomId
}));
SendMessageComposer(new GetGuestRoomMessageComposer(parser.roomId, true, false));
if(LegacyExternalInterface.available) LegacyExternalInterface.call('legacyTrack', 'navigator', 'private', [ parser.roomId ]);
}, []));
useMessageEvent<GetGuestRoomResultEvent>(GetGuestRoomResultEvent, useCallback(event =>
{
const parser = event.getParser();
if(parser.roomEnter)
{
setNavigatorData(prev =>
{
const next = { ...prev };
next.enteredGuestRoom = parser.data;
next.currentRoomIsStaffPick = parser.staffPick;
const isCreated = next.createdFlatId === parser.data.roomId;
if(!isCreated && parser.data.displayRoomEntryAd)
{
if(GetConfigurationValue<boolean>('roomenterad.habblet.enabled', false)) HabboWebTools.openRoomEnterAd();
}
next.createdFlatId = 0;
return next;
});
return;
}
if(parser.roomForward)
{
// Door-mode branches (DOORBELL_STATE / PASSWORD_STATE) are handled by useDoorState — skip them here.
const isOwner = parser.data.ownerName === GetSessionDataManager().userName;
if(!isOwner && !parser.isGroupMember)
{
if(parser.data.doorMode === RoomDataParser.DOORBELL_STATE) return;
if(parser.data.doorMode === RoomDataParser.PASSWORD_STATE) return;
}
if((parser.data.doorMode === RoomDataParser.NOOB_STATE) && !GetSessionDataManager().isAmbassador && !GetSessionDataManager().isRealNoob && !GetSessionDataManager().isModerator) return;
CreateRoomSession(parser.data.roomId);
return;
}
setNavigatorData(prev => ({
...prev,
enteredGuestRoom: parser.data,
currentRoomIsStaffPick: parser.staffPick
}));
}, []));
useMessageEvent<RoomScoreEvent>(RoomScoreEvent, useCallback(event =>
{
const parser = event.getParser();
setNavigatorData(prev => ({
...prev,
currentRoomRating: parser.totalLikes,
canRate: parser.canLike
}));
}, []));
useMessageEvent<GenericErrorEvent>(GenericErrorEvent, useCallback(event =>
{
const parser = event.getParser();
// -100002 (wrong password) is handled by useDoorState — skip it here.
switch(parser.errorCode)
{
case 4009:
simpleAlert(LocalizeText('navigator.alert.need.to.be.vip'), NotificationAlertType.DEFAULT, null, null, LocalizeText('generic.alert.title'));
return;
case 4010:
simpleAlert(LocalizeText('navigator.alert.invalid_room_name'), NotificationAlertType.DEFAULT, null, null, LocalizeText('generic.alert.title'));
return;
case 4011:
simpleAlert(LocalizeText('navigator.alert.cannot_perm_ban'), NotificationAlertType.DEFAULT, null, null, LocalizeText('generic.alert.title'));
return;
case 4013:
simpleAlert(LocalizeText('navigator.alert.room_in_maintenance'), NotificationAlertType.DEFAULT, null, null, LocalizeText('generic.alert.title'));
return;
}
}, [ simpleAlert ]));
useMessageEvent<NavigatorMetadataEvent>(NavigatorMetadataEvent, useCallback(event =>
{
const parser = event.getParser();
setTopLevelContexts(parser.topLevelContexts);
setTopLevelContext(parser.topLevelContexts.length ? parser.topLevelContexts[0] : null);
// Seed the query's tab code so useNavigatorSearch activates immediately
useNavigatorUiStore.getState().setTab(parser.topLevelContexts[0]?.code ?? '');
}, []));
useMessageEvent<UserFlatCatsEvent>(UserFlatCatsEvent, useCallback(event =>
{
const parser = event.getParser();
setCategories(parser.categories);
}, []));
useMessageEvent<UserEventCatsEvent>(UserEventCatsEvent, useCallback(event =>
{
const parser = event.getParser();
setEventCategories(parser.categories);
}, []));
useMessageEvent<FlatCreatedEvent>(FlatCreatedEvent, useCallback(event =>
{
const parser = event.getParser();
CreateRoomSession(parser.roomId);
}, []));
useNitroEvent(NitroEventType.SOCKET_RECONNECTING, useCallback(() =>
{
setNavigatorData(prev => ({ ...prev, settingsReceived: false }));
}, []));
useMessageEvent<NavigatorHomeRoomEvent>(NavigatorHomeRoomEvent, useCallback(event =>
{
const parser = event.getParser();
let prevSettingsReceived = false;
setNavigatorData(prev =>
{
prevSettingsReceived = prev.settingsReceived;
return { ...prev, homeRoomId: parser.homeRoomId, settingsReceived: true };
});
if(prevSettingsReceived) return;
if(GetRoomSessionManager().viewerSession) return;
let forwardType = -1;
let forwardId = -1;
if((GetConfigurationValue<string>('friend.id') !== undefined) && (parseInt(GetConfigurationValue<string>('friend.id')) > 0))
{
forwardType = 0;
SendMessageComposer(new FollowFriendMessageComposer(parseInt(GetConfigurationValue<string>('friend.id'))));
}
if((GetConfigurationValue<number>('forward.type') !== undefined) && (GetConfigurationValue<number>('forward.id') !== undefined))
{
forwardType = parseInt(GetConfigurationValue<string>('forward.type'));
forwardId = parseInt(GetConfigurationValue<string>('forward.id'));
}
if(forwardType === 2)
{
TryVisitRoom(forwardId);
}
else if((forwardType === -1) && (parser.roomIdToEnter > 0))
{
CreateLinkEvent('navigator/close');
CreateRoomSession(parser.roomIdToEnter !== parser.homeRoomId ? parser.roomIdToEnter : parser.homeRoomId);
}
}, []));
useMessageEvent<RoomEnterErrorEvent>(RoomEnterErrorEvent, useCallback(event =>
{
const parser = event.getParser();
switch(parser.reason)
{
case CantConnectMessageParser.REASON_FULL:
simpleAlert(LocalizeText('navigator.guestroomfull.text'), NotificationAlertType.DEFAULT, null, null, LocalizeText('navigator.guestroomfull.title'));
break;
case CantConnectMessageParser.REASON_QUEUE_ERROR:
simpleAlert(LocalizeText(`room.queue.error.${ parser.parameter }`), NotificationAlertType.DEFAULT, null, null, LocalizeText('room.queue.error.title'));
break;
case CantConnectMessageParser.REASON_BANNED:
simpleAlert(LocalizeText('navigator.banned.text'), NotificationAlertType.DEFAULT, null, null, LocalizeText('navigator.banned.title'));
break;
default:
simpleAlert(LocalizeText('room.queue.error.title'), NotificationAlertType.DEFAULT, null, null, LocalizeText('room.queue.error.title'));
break;
}
if(GetRoomSessionManager().isReconnecting) return;
VisitDesktop();
}, [ simpleAlert ]));
useMessageEvent<NavigatorOpenRoomCreatorEvent>(NavigatorOpenRoomCreatorEvent, useCallback(_event =>
{
CreateLinkEvent('navigator/show');
}, []));
useMessageEvent<NavigatorSearchesEvent>(NavigatorSearchesEvent, useCallback(event =>
{
const parser = event.getParser();
if(!parser) return;
setNavigatorSearches(parser.searches);
}, []));
return {
categories, eventCategories, favouriteRoomIds,
topLevelContext, topLevelContexts,
navigatorSearches, navigatorData
};
};