fix(room-preview): make the preview card actually populate + richer
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:
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box, Icon, IconButton, Icons, Scroll, Text, toRem } from 'folds';
|
||||
import { Box, Icon, IconButton, Icons, Scroll, Spinner, Text, toRem } from 'folds';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { RoomCard } from '../../components/room-card';
|
||||
import { RoomTopicViewer } from '../../components/room-topic-viewer';
|
||||
@@ -32,53 +32,68 @@ export function JoinBeforeNavigate({
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<PageHeader balance>
|
||||
<Box grow="Yes" gap="200">
|
||||
<Box shrink="No">
|
||||
{screenSize === ScreenSize.Mobile && (
|
||||
<BackRouteHandler>
|
||||
{(onBack) => (
|
||||
<IconButton onClick={onBack} aria-label="Back">
|
||||
<Icon src={Icons.ArrowLeft} />
|
||||
</IconButton>
|
||||
)}
|
||||
</BackRouteHandler>
|
||||
)}
|
||||
</Box>
|
||||
<Box grow="Yes" justifyContent="Center" alignItems="Center" gap="200">
|
||||
<Text size="H3" truncate>
|
||||
{roomIdOrAlias}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</PageHeader>
|
||||
<Box grow="Yes">
|
||||
<Scroll hideTrack visibility="Hover" size="0">
|
||||
<Box style={{ height: '100%' }} grow="Yes" alignItems="Center" justifyContent="Center">
|
||||
<RoomSummaryLoader roomIdOrAlias={roomIdOrAlias}>
|
||||
{(summary) => (
|
||||
<RoomCard
|
||||
style={{ maxWidth: toRem(364), width: '100%' }}
|
||||
roomIdOrAlias={roomIdOrAlias}
|
||||
allRooms={allRooms}
|
||||
avatarUrl={summary?.avatar_url}
|
||||
name={summary?.name}
|
||||
topic={summary?.topic}
|
||||
memberCount={summary?.num_joined_members}
|
||||
roomType={summary?.room_type}
|
||||
joinRule={summary?.join_rule}
|
||||
encrypted={!!summary?.['im.nheko.summary.encryption']}
|
||||
viaServers={viaServers}
|
||||
renderTopicViewer={(name, topic, requestClose) => (
|
||||
<RoomTopicViewer name={name} topic={topic} requestClose={requestClose} />
|
||||
<RoomSummaryLoader roomIdOrAlias={roomIdOrAlias} via={viaServers}>
|
||||
{(summary, state) => (
|
||||
<>
|
||||
<PageHeader balance>
|
||||
<Box grow="Yes" gap="200">
|
||||
<Box shrink="No">
|
||||
{screenSize === ScreenSize.Mobile && (
|
||||
<BackRouteHandler>
|
||||
{(onBack) => (
|
||||
<IconButton onClick={onBack} aria-label="Back">
|
||||
<Icon src={Icons.ArrowLeft} />
|
||||
</IconButton>
|
||||
)}
|
||||
</BackRouteHandler>
|
||||
)}
|
||||
onView={handleView}
|
||||
/>
|
||||
)}
|
||||
</RoomSummaryLoader>
|
||||
</Box>
|
||||
</Scroll>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box grow="Yes" justifyContent="Center" alignItems="Center" gap="200">
|
||||
<Text size="H3" truncate>
|
||||
{summary?.name || summary?.canonical_alias || roomIdOrAlias}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</PageHeader>
|
||||
<Box grow="Yes">
|
||||
<Scroll hideTrack visibility="Hover" size="0">
|
||||
<Box
|
||||
style={{ height: '100%' }}
|
||||
grow="Yes"
|
||||
alignItems="Center"
|
||||
justifyContent="Center"
|
||||
>
|
||||
{state.loading && !summary ? (
|
||||
<Spinner size="600" variant="Secondary" />
|
||||
) : (
|
||||
<RoomCard
|
||||
hero
|
||||
style={{ maxWidth: toRem(420), width: '100%' }}
|
||||
roomIdOrAlias={roomIdOrAlias}
|
||||
allRooms={allRooms}
|
||||
avatarUrl={summary?.avatar_url}
|
||||
name={summary?.name}
|
||||
canonicalAlias={summary?.canonical_alias}
|
||||
topic={summary?.topic}
|
||||
memberCount={summary?.num_joined_members}
|
||||
roomType={summary?.room_type}
|
||||
joinRule={summary?.join_rule}
|
||||
encrypted={!!summary?.['im.nheko.summary.encryption']}
|
||||
worldReadable={summary?.world_readable}
|
||||
membership={summary?.membership}
|
||||
viaServers={viaServers}
|
||||
renderTopicViewer={(name, topic, requestClose) => (
|
||||
<RoomTopicViewer name={name} topic={topic} requestClose={requestClose} />
|
||||
)}
|
||||
onView={handleView}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Scroll>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</RoomSummaryLoader>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import { useCategoryHandler } from '../../hooks/useCategoryHandler';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { allRoomsAtom } from '../../state/room-list/roomList';
|
||||
import { getCanonicalAliasOrRoomId, rateLimitedActions } from '../../utils/matrix';
|
||||
import { getSpaceRoomPath } from '../../pages/pathUtils';
|
||||
import { getSpaceRoomPath, withSearchParam } from '../../pages/pathUtils';
|
||||
import { StateEvent } from '../../../types/matrix/room';
|
||||
import { CanDropCallback, useDnDMonitor } from './DnD';
|
||||
import { ASCIILexicalTable, orderKeys } from '../../utils/ASCIILexicalTable';
|
||||
@@ -411,8 +411,13 @@ export function Lobby() {
|
||||
const handleOpenRoom: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||
const rId = evt.currentTarget.getAttribute('data-room-id');
|
||||
if (!rId) return;
|
||||
// Preview chips carry the room's resident servers so getRoomSummary can
|
||||
// resolve a room the homeserver isn't already in (else the preview is sparse).
|
||||
const via = evt.currentTarget.getAttribute('data-via');
|
||||
const pSpaceIdOrAlias = getCanonicalAliasOrRoomId(mx, space.roomId);
|
||||
navigate(getSpaceRoomPath(pSpaceIdOrAlias, getCanonicalAliasOrRoomId(mx, rId)));
|
||||
let path = getSpaceRoomPath(pSpaceIdOrAlias, getCanonicalAliasOrRoomId(mx, rId));
|
||||
if (via) path = withSearchParam(path, { viaServers: via });
|
||||
navigate(path);
|
||||
};
|
||||
|
||||
const togglePinToSidebar = useCallback(
|
||||
|
||||
@@ -102,14 +102,17 @@ function RoomJoinButton({ roomId, via }: RoomJoinButtonProps) {
|
||||
// room isn't joined — renders the preview card instead of the timeline.
|
||||
function RoomPreviewChip({
|
||||
roomId,
|
||||
via,
|
||||
onOpen,
|
||||
}: {
|
||||
roomId: string;
|
||||
via?: string[];
|
||||
onOpen: MouseEventHandler<HTMLButtonElement>;
|
||||
}) {
|
||||
return (
|
||||
<Chip
|
||||
data-room-id={roomId}
|
||||
data-via={via && via.length > 0 ? via.join(',') : undefined}
|
||||
onClick={onOpen}
|
||||
variant="Secondary"
|
||||
fill="None"
|
||||
@@ -391,7 +394,7 @@ export const RoomItemCard = as<'div', RoomItemCardProps>(
|
||||
</Box>
|
||||
) : (
|
||||
<Box shrink="No" gap="100" alignItems="Center">
|
||||
<RoomPreviewChip roomId={roomId} onOpen={onOpen} />
|
||||
<RoomPreviewChip roomId={roomId} via={content.via} onOpen={onOpen} />
|
||||
<RoomJoinButton roomId={roomId} via={content.via} />
|
||||
</Box>
|
||||
)
|
||||
@@ -439,7 +442,7 @@ export const RoomItemCard = as<'div', RoomItemCardProps>(
|
||||
joinRule={summary.join_rule}
|
||||
options={
|
||||
<Box shrink="No" gap="100" alignItems="Center">
|
||||
<RoomPreviewChip roomId={roomId} onOpen={onOpen} />
|
||||
<RoomPreviewChip roomId={roomId} via={content.via} onOpen={onOpen} />
|
||||
<RoomJoinButton roomId={roomId} via={content.via} />
|
||||
</Box>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user