fix: rework compact nav and centralized back routing

Use master-detail layout on skinny windows so server and channel lists share one screen, with back returning to the space list instead of redirect loops. Add useBackRoute for title-bar and page-header back buttons with view transitions.
This commit is contained in:
2026-07-09 04:22:07 +10:00
parent b4258278d8
commit 9c7f71896c
11 changed files with 247 additions and 116 deletions

View File

@@ -1,90 +1,11 @@
import { ReactNode, useCallback } from 'react';
import { matchPath, useLocation, useNavigate } from 'react-router-dom';
import {
decodeRouteParam,
getDirectPath,
getExplorePath,
getHomePath,
getInboxPath,
getSpacePath,
} from '../pages/pathUtils';
import { DIRECT_PATH, EXPLORE_PATH, HOME_PATH, INBOX_PATH, SPACE_PATH } from '../pages/paths';
import { ReactNode } from 'react';
import { useBackRoute } from '../hooks/useBackRoute';
type BackRouteHandlerProps = {
children: (onBack: () => void) => ReactNode;
};
export function BackRouteHandler({ children }: BackRouteHandlerProps) {
const navigate = useNavigate();
const location = useLocation();
const goBack = useCallback(() => {
if (
matchPath(
{
path: HOME_PATH,
caseSensitive: true,
end: false,
},
location.pathname
)
) {
navigate(getHomePath());
return;
}
if (
matchPath(
{
path: DIRECT_PATH,
caseSensitive: true,
end: false,
},
location.pathname
)
) {
navigate(getDirectPath());
return;
}
const spaceMatch = matchPath(
{
path: SPACE_PATH,
caseSensitive: true,
end: false,
},
location.pathname
);
if (spaceMatch?.params.spaceIdOrAlias) {
const spaceIdOrAlias = decodeRouteParam(spaceMatch.params.spaceIdOrAlias);
if (spaceIdOrAlias) {
navigate(getSpacePath(spaceIdOrAlias));
}
return;
}
if (
matchPath(
{
path: EXPLORE_PATH,
caseSensitive: true,
end: false,
},
location.pathname
)
) {
navigate(getExplorePath());
return;
}
if (
matchPath(
{
path: INBOX_PATH,
caseSensitive: true,
end: false,
},
location.pathname
)
) {
navigate(getInboxPath());
}
}, [navigate, location]);
const { goBack } = useBackRoute();
return children(goBack);
}

View File

@@ -5,6 +5,7 @@ import { ContainerColor } from '../../styles/ContainerColor.css';
import * as css from './style.css';
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
import { SidebarDockedCallPanel } from '../../features/call/SidebarDockedCallPanel';
import { useShowCompactMasterView } from '../../hooks/useCompactNav';
type PageRootProps = {
nav: ReactNode;
@@ -13,6 +14,7 @@ type PageRootProps = {
export function PageRoot({ nav, children }: PageRootProps) {
const screenSize = useScreenSizeContext();
const showCompactMaster = useShowCompactMasterView();
return (
<Box grow="Yes" className={ContainerColor({ variant: 'Background' })}>
@@ -20,7 +22,7 @@ export function PageRoot({ nav, children }: PageRootProps) {
{screenSize !== ScreenSize.Mobile && (
<Line variant="Background" size="300" direction="Vertical" />
)}
{children}
{!showCompactMaster && children}
</Box>
);
}

View File

@@ -76,6 +76,11 @@ export const TitleBarDragRegion = style({
color: color.Surface.OnContainer,
});
export const TitleBarBackButton = style({
WebkitAppRegion: 'no-drag',
flexShrink: 0,
});
export const SyncStatusBadge = style({
position: 'absolute',
left: '50%',

View File

@@ -1,5 +1,5 @@
import React, { ReactNode, useMemo, useEffect, useState, useCallback, useRef } from 'react';
import { Box, Text } from 'folds';
import { Box, Text, IconButton } from 'folds';
import { invoke } from '@tauri-apps/api/core';
import { MatrixClient, RoomEvent, ClientEvent, MatrixEvent, Room, IPushRules, SyncState } from 'matrix-js-sdk';
import { useAtomValue } from 'jotai';
@@ -18,6 +18,8 @@ import { AccountDataEvent } from '../../../types/matrix/accountData';
import { getNotificationMode, NotificationMode } from '../../hooks/useNotificationMode';
import { isRoomId } from '../../utils/matrix';
import { useSyncState } from '../../hooks/useSyncState';
import { useBackRoute } from '../../hooks/useBackRoute';
import { Icon, Icons } from '../icons';
type NewMessageNotification = {
id: string;
@@ -42,6 +44,7 @@ export function TitleBar({ mx, children }: TitleBarProps) {
const navigate = useNavigate();
const roomToParents = useAtomValue(roomToParentsAtom);
const mDirects = useAtomValue(mDirectAtom);
const { showInTitleBar, goBack } = useBackRoute(mx);
// Sync status state
const [syncStateData, setSyncStateData] = useState<{
@@ -652,6 +655,20 @@ export function TitleBar({ mx, children }: TitleBarProps) {
grow="Yes"
onMouseDown={handleDragStart}
>
{showInTitleBar && (
<IconButton
className={css.TitleBarBackButton}
size="300"
variant="Surface"
fill="None"
radii="300"
onClick={goBack}
onMouseDown={(e) => e.stopPropagation()}
aria-label="Back"
>
<Icon src={Icons.ArrowLeft} size="200" />
</IconButton>
)}
<Text size="T200" truncate style={{ color: 'inherit' }}>
<span style={{ fontWeight: 700 }}>Paarrot</span>{titleParts.suffix}
</Text>