🆙 Small fix for the navigator

This mirrors what the old god-hook used to do and what the rest of the codebase still uses for everything else. The TanStack one-shot listener pattern (awaitNitroResponse registers a listener, awaits one matching response, removes itself) is fragile against renderer-bundle quirks — the parser fires but the listener never matches, so the promise never resolves and query.data stays undefined forever. That's exactly the symptom you saw: server logs show the response arriving, client UI stays blank.
This commit is contained in:
duckietm
2026-05-28 13:46:44 +02:00
parent 566d1f655b
commit 05d71dd163
+32 -34
View File
@@ -1,47 +1,45 @@
import { FlatCreatedEvent, NavigatorSearchComposer, NavigatorSearchEvent, import { NavigatorSearchComposer, NavigatorSearchEvent, NavigatorSearchResultSet } from '@nitrots/nitro-renderer';
NavigatorSearchResultSet, RoomSettingsUpdatedEvent } from '@nitrots/nitro-renderer'; import { useEffect, useState } from 'react';
import { useNitroEventInvalidator, useNitroQuery } from '../../api/nitro-query'; import { SendMessageComposer } from '../../api';
import { useMessageEvent } from '../events';
import { useNavigatorUiStore } from './navigatorUiStore'; import { useNavigatorUiStore } from './navigatorUiStore';
const SEARCH_BASE_KEY = [ 'navigator', 'search' ] as const;
/**
* TanStack Query wrapper for navigator search.
*
* Cache key: ['navigator', 'search', tabCode, filter]
* - Fires NavigatorSearchComposer(tabCode, filter) on miss.
* - Listens for NavigatorSearchEvent and resolves with the result.
* - accept-filter: rejects events whose result.code !== tabCode (defends
* against server-side cross-tab pushes resolving the wrong query slot).
* - Disabled when tabCode is '' (initial state, before metadata arrives).
* - Invalidates on FlatCreatedEvent (new room created) and
* RoomSettingsUpdatedEvent (room renamed / settings changed).
*/
export const useNavigatorSearch = () => export const useNavigatorSearch = () =>
{ {
const tabCode = useNavigatorUiStore(s => s.currentTabCode); const tabCode = useNavigatorUiStore(s => s.currentTabCode);
const filter = useNavigatorUiStore(s => s.currentFilter); const filter = useNavigatorUiStore(s => s.currentFilter);
const query = useNitroQuery<NavigatorSearchEvent, NavigatorSearchResultSet | null>({ const [ searchResult, setSearchResult ] = useState<NavigatorSearchResultSet | null>(null);
key: [ ...SEARCH_BASE_KEY, tabCode, filter ], const [ isFetching, setIsFetching ] = useState(false);
request: () => new NavigatorSearchComposer(tabCode, filter),
parser: NavigatorSearchEvent, useEffect(() =>
select: e => e.getParser()?.result ?? null, {
accept: e => if(!tabCode) return;
{
const result = e.getParser()?.result; setIsFetching(true);
return !!result && result.code === tabCode; SendMessageComposer(new NavigatorSearchComposer(tabCode, filter));
}, }, [ tabCode, filter ]);
enabled: !!tabCode,
staleTime: 30_000 useMessageEvent<NavigatorSearchEvent>(NavigatorSearchEvent, event =>
{
const result = event.getParser()?.result;
if(!result) return;
if(tabCode && result.code !== tabCode) return;
setSearchResult(result);
setIsFetching(false);
}); });
useNitroEventInvalidator(FlatCreatedEvent, [ ...SEARCH_BASE_KEY ]);
useNitroEventInvalidator(RoomSettingsUpdatedEvent, [ ...SEARCH_BASE_KEY ]);
return { return {
searchResult: query.data ?? null, searchResult,
isFetching: query.isFetching, isFetching,
refetch: query.refetch refetch: () =>
{
if(!tabCode) return;
setIsFetching(true);
SendMessageComposer(new NavigatorSearchComposer(tabCode, filter));
}
}; };
}; };