fix(room-preview): make the preview card actually populate + richer
CI / Build & Quality Checks (push) Successful in 10m53s
CI / Trigger Desktop Build (push) Successful in 14s

Two review agents found the preview card degrades to 'id + logo + Join' for real
reasons. Fixes:

- **via was dropped at the summary fetch** (root cause). RoomSummaryLoader now
  accepts + forwards `via` to getRoomSummary (and keys the query on it);
  JoinBeforeNavigate passes viaServers; the lobby Preview chip carries data-via and
  Lobby.handleOpenRoom appends it to the navigated URL. Without this, previews of
  rooms the HS isn't already in came back sparse/404.
- **No loading/error state** -> RoomSummaryLoader now surfaces {loading,error};
  JoinBeforeNavigate shows a Spinner while loading instead of the degraded card.
- **Room id leaked as name AND topic AND header** -> stop using the raw `!id` as
  the topic fallback (show 'No description'); name falls back to canonical_alias
  then alias-localpart; the page header shows the summary name.
- **Richer, membership-aware card**: forward canonical_alias (shown under the name),
  world_readable ('Readable' badge), and membership -> the button now shows Accept
  invite / Requested / Banned correctly instead of a Join that lies; a 'hero' layout
  for the single-preview page (primary Join button, unclamped topic). Removed dead
  `|| undefined ||`.

IRoomSummary re-adds canonical_alias (the SDK type Omit<>s it though MSC3266
returns it). 738 tests pass, build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 14:46:21 -04:00
co-authored by Claude Opus 4.8
parent 7fd3164b1f
commit 0c6003fb87
5 changed files with 157 additions and 78 deletions
+25 -7
View File
@@ -6,24 +6,42 @@ import { useMatrixClient } from '../hooks/useMatrixClient';
import { LocalRoomSummary, useLocalRoomSummary } from '../hooks/useLocalRoomSummary';
import { AsyncState, AsyncStatus } from '../hooks/useAsyncCallback';
export type IRoomSummary = Awaited<ReturnType<MatrixClient['getRoomSummary']>>;
// MSC3266 returns canonical_alias at runtime, but the SDK's RoomSummary type
// Omit<>s it — add it back so callers can use it.
export type IRoomSummary = Awaited<ReturnType<MatrixClient['getRoomSummary']>> & {
canonical_alias?: string;
};
export type RoomSummaryState = { loading: boolean; error: unknown };
type RoomSummaryLoaderProps = {
roomIdOrAlias: string;
children: (roomSummary?: IRoomSummary) => ReactNode;
/**
* Resident servers to route the summary request through. REQUIRED for a room
* the local homeserver isn't already in (the exact case a preview exists for) —
* without it the summary comes back sparse or 404s.
*/
via?: string[];
children: (roomSummary: IRoomSummary | undefined, state: RoomSummaryState) => ReactNode;
};
export function RoomSummaryLoader({ roomIdOrAlias, children }: RoomSummaryLoaderProps) {
export function RoomSummaryLoader({ roomIdOrAlias, via, children }: RoomSummaryLoaderProps) {
const mx = useMatrixClient();
const fetchSummary = useCallback(() => mx.getRoomSummary(roomIdOrAlias), [mx, roomIdOrAlias]);
const fetchSummary = useCallback(
() => mx.getRoomSummary(roomIdOrAlias, via),
[mx, roomIdOrAlias, via],
);
const { data } = useQuery({
queryKey: [roomIdOrAlias, `summary`],
const { data, isLoading, error } = useQuery({
// `via` is part of the key so a no-via failure isn't reused once we have servers.
queryKey: [roomIdOrAlias, 'summary', via ?? []],
queryFn: fetchSummary,
retry: 1,
staleTime: 5 * 60 * 1000,
});
return children(data);
return children(data, { loading: isLoading, error });
}
export function LocalRoomSummaryLoader({