feat: Implement forum feed layout and refactor space navigation logic to support new routing structure

This commit is contained in:
2026-07-06 15:35:21 +10:00
parent 09db721b7e
commit 3ee95a295d
17 changed files with 296 additions and 135 deletions

View File

@@ -37,7 +37,7 @@ import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { mDirectAtom } from '../../../state/mDirectList';
import { roomToParentsAtom } from '../../../state/room/roomToParents';
import { allRoomsAtom } from '../../../state/room-list/roomList';
import { getHomePath, getSpaceLobbyPath, getSpacePath, joinPathComponent } from '../../pathUtils';
import { getHomePath, getSpaceDefaultPath, getSpacePath, joinPathComponent } from '../../pathUtils';
import {
SidebarAvatar,
SidebarItem,
@@ -1067,7 +1067,7 @@ export function SpaceTabs({ scrollRef }: SpaceTabsProps) {
return;
}
navigate(getSpaceLobbyPath(getCanonicalAliasOrRoomId(mx, targetSpaceId)));
navigate(getSpaceDefaultPath(mx, targetSpaceId));
};
const handleFolderToggle: MouseEventHandler<HTMLButtonElement> = (evt) => {
@@ -1206,7 +1206,7 @@ export function HiddenSpacesTabs() {
return;
}
navigate(getSpaceLobbyPath(getCanonicalAliasOrRoomId(mx, targetSpaceId)));
navigate(getSpaceDefaultPath(mx, targetSpaceId));
};
const handleHomeClick = () => {

View File

@@ -335,7 +335,12 @@ export function Space() {
const getToLink = (roomId: string) =>
getSpaceRoomPath(spaceIdOrAlias, getCanonicalAliasOrRoomId(mx, roomId));
if (shouldShowForumLobby(space)) {
const isForum = shouldShowForumLobby(space);
// Forum feed unless user opened manage, search, or a room (not tied to URL alias encoding).
const feedActive = isForum && !lobbySelected && !searchSelected && !selectedRoomId;
const showForumFeedSidebar = feedActive;
if (showForumFeedSidebar) {
return (
<PageNav className={forumBoardCss.ForumPageNav}>
<SpaceHeader />
@@ -346,6 +351,8 @@ export function Space() {
);
}
const spacePath = getCanonicalAliasOrRoomId(mx, space.roomId);
return (
<PageNav>
<SpaceHeader />
@@ -358,38 +365,42 @@ export function Space() {
/>
)}
<NavCategory>
<NavItem variant="Background" radii="400" aria-selected={lobbySelected}>
<NavLink to={getSpaceLobbyPath(getCanonicalAliasOrRoomId(mx, space.roomId))}>
<NavItemContent>
<Box as="span" grow="Yes" alignItems="Center" gap="200">
<Avatar size="200" radii="400">
<Icon src={Icons.Flag} size="100" filled={lobbySelected} />
</Avatar>
<Box as="span" grow="Yes">
<Text as="span" size="Inherit" truncate>
Lobby
</Text>
</Box>
</Box>
</NavItemContent>
</NavLink>
</NavItem>
<NavItem variant="Background" radii="400" aria-selected={searchSelected}>
<NavLink to={getSpaceSearchPath(getCanonicalAliasOrRoomId(mx, space.roomId))}>
<NavItemContent>
<Box as="span" grow="Yes" alignItems="Center" gap="200">
<Avatar size="200" radii="400">
<Icon src={Icons.Search} size="100" filled={searchSelected} />
</Avatar>
<Box as="span" grow="Yes">
<Text as="span" size="Inherit" truncate>
Message Search
</Text>
</Box>
</Box>
</NavItemContent>
</NavLink>
</NavItem>
{!isForum && (
<>
<NavItem variant="Background" radii="400" aria-selected={lobbySelected}>
<NavLink to={getSpaceLobbyPath(spacePath)}>
<NavItemContent>
<Box as="span" grow="Yes" alignItems="Center" gap="200">
<Avatar size="200" radii="400">
<Icon src={Icons.Flag} size="100" filled={lobbySelected} />
</Avatar>
<Box as="span" grow="Yes">
<Text as="span" size="Inherit" truncate>
Lobby
</Text>
</Box>
</Box>
</NavItemContent>
</NavLink>
</NavItem>
<NavItem variant="Background" radii="400" aria-selected={searchSelected}>
<NavLink to={getSpaceSearchPath(spacePath)}>
<NavItemContent>
<Box as="span" grow="Yes" alignItems="Center" gap="200">
<Avatar size="200" radii="400">
<Icon src={Icons.Search} size="100" filled={searchSelected} />
</Avatar>
<Box as="span" grow="Yes">
<Text as="span" size="Inherit" truncate>
Message Search
</Text>
</Box>
</Box>
</NavItemContent>
</NavLink>
</NavItem>
</>
)}
<PluginNavSlot location="channel-list" />
</NavCategory>
<NavCategory

View File

@@ -0,0 +1,26 @@
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { useSpace } from '../../../hooks/useSpace';
import { shouldShowForumLobby } from '../../../utils/room';
import { getSpaceDefaultPath } from '../../pathUtils';
import { ForumFeedPage } from '../../../features/forum/ForumFeedPage';
/** Sends /:spaceId/ to the forum feed or space lobby. */
export function SpaceIndexRedirect() {
const mx = useMatrixClient();
const space = useSpace();
const navigate = useNavigate();
const isForum = shouldShowForumLobby(space);
useEffect(() => {
navigate(getSpaceDefaultPath(mx, space.roomId), { replace: true });
}, [mx, navigate, space.roomId]);
// Avoid a blank main pane while redirecting forum spaces to /feed/.
if (isForum) {
return <ForumFeedPage />;
}
return null;
}

View File

@@ -1,6 +1,7 @@
import React, { ReactNode } from 'react';
import { useParams } from 'react-router-dom';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { useAutoJoinOnSpaceVisit } from '../../../hooks/useAutoJoinSpaceRooms';
import { useSpaces } from '../../../state/hooks/roomList';
import { allRoomsAtom } from '../../../state/room-list/roomList';
import { useSelectedSpace } from '../../../hooks/router/useSelectedSpace';
@@ -21,6 +22,8 @@ export function RouteSpaceProvider({ children }: RouteSpaceProviderProps) {
const selectedSpaceId = useSelectedSpace();
const space = mx.getRoom(selectedSpaceId);
useAutoJoinOnSpaceVisit(space ?? undefined);
if (!space || !joinedSpaces.includes(space.roomId)) {
return <JoinBeforeNavigate roomIdOrAlias={spaceIdOrAlias ?? ''} viaServers={viaServers} />;
}

View File

@@ -1,3 +1,4 @@
export * from './SpaceIndexRedirect';
export * from './SpaceProvider';
export * from './Space';
export * from './Search';