feat: implement sidebar docked call panel with participant management and sync status handling

This commit is contained in:
2026-02-28 00:54:55 +11:00
parent 29c409fad8
commit b185e0b129
14 changed files with 625 additions and 77 deletions

View File

@@ -4,6 +4,7 @@ import classNames from 'classnames';
import { ContainerColor } from '../../styles/ContainerColor.css';
import * as css from './style.css';
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
import { SidebarDockedCallPanel } from '../../features/call/SidebarDockedCallPanel';
type PageRootProps = {
nav: ReactNode;
@@ -39,6 +40,7 @@ export function PageNav({ size, children }: ClientDrawerLayoutProps & css.PageNa
>
<Box grow="Yes" direction="Column">
{children}
<SidebarDockedCallPanel />
</Box>
</Box>
);

View File

@@ -75,3 +75,33 @@ export const TitleBarDragRegion = style({
WebkitAppRegion: 'drag',
color: color.Surface.OnContainer,
});
export const SyncStatusBadge = style({
position: 'absolute',
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)',
padding: `${config.space.S100} ${config.space.S300}`,
borderRadius: config.radii.R300,
fontSize: '11px',
fontWeight: 500,
whiteSpace: 'nowrap',
zIndex: 1001,
pointerEvents: 'none',
WebkitAppRegion: 'no-drag',
});
export const SyncStatusSuccess = style({
backgroundColor: color.Success.Container,
color: color.Success.OnContainer,
});
export const SyncStatusWarning = style({
backgroundColor: color.Warning.Container,
color: color.Warning.OnContainer,
});
export const SyncStatusCritical = style({
backgroundColor: color.Critical.Container,
color: color.Critical.OnContainer,
});

View File

@@ -1,7 +1,7 @@
import React, { ReactNode, useMemo, useEffect, useState, useCallback, useRef } from 'react';
import { Box, Text } from 'folds';
import { invoke } from '@tauri-apps/api/core';
import { MatrixClient, RoomEvent, ClientEvent, MatrixEvent, Room, IPushRules } from 'matrix-js-sdk';
import { MatrixClient, RoomEvent, ClientEvent, MatrixEvent, Room, IPushRules, SyncState } from 'matrix-js-sdk';
import { useAtomValue } from 'jotai';
import { useNavigate } from 'react-router-dom';
import { WindowControls } from './WindowControls';
@@ -17,6 +17,7 @@ import { RoomNotificationMode } from '../../hooks/useRoomsNotificationPreference
import { AccountDataEvent } from '../../../types/matrix/accountData';
import { getNotificationMode, NotificationMode } from '../../hooks/useNotificationMode';
import { isRoomId } from '../../utils/matrix';
import { useSyncState } from '../../hooks/useSyncState';
type NewMessageNotification = {
id: string;
@@ -42,6 +43,28 @@ export function TitleBar({ mx, children }: TitleBarProps) {
const roomToParents = useAtomValue(roomToParentsAtom);
const mDirects = useAtomValue(mDirectAtom);
// Sync status state
const [syncStateData, setSyncStateData] = useState<{
current: SyncState | null;
previous: SyncState | null | undefined;
}>({
current: null,
previous: undefined,
});
// Track sync state changes
useSyncState(
mx,
useCallback((current, previous) => {
setSyncStateData((s) => {
if (s.current === current && s.previous === previous) {
return s;
}
return { current, previous };
});
}, [])
);
// Notification-related state - only populated when mx is available
const [notificationData, setNotificationData] = useState<{
preferences: { mute: Set<string>; specialMessages: Set<string>; allMessages: Set<string> };
@@ -591,8 +614,38 @@ export function TitleBar({ mx, children }: TitleBarProps) {
return null;
}
// Determine sync status badge
let syncStatusBadge: ReactNode = null;
if (mx) {
if (
(syncStateData.current === SyncState.Prepared ||
syncStateData.current === SyncState.Syncing ||
syncStateData.current === SyncState.Catchup) &&
syncStateData.previous !== SyncState.Syncing
) {
syncStatusBadge = (
<div className={`${css.SyncStatusBadge} ${css.SyncStatusSuccess}`}>
Connecting...
</div>
);
} else if (syncStateData.current === SyncState.Reconnecting) {
syncStatusBadge = (
<div className={`${css.SyncStatusBadge} ${css.SyncStatusWarning}`}>
Reconnecting...
</div>
);
} else if (syncStateData.current === SyncState.Error) {
syncStatusBadge = (
<div className={`${css.SyncStatusBadge} ${css.SyncStatusCritical}`}>
Connection Lost
</div>
);
}
}
return (
<Box className={css.TitleBar} alignItems="Center" justifyContent="SpaceBetween">
{syncStatusBadge}
<Box
className={css.TitleBarDragRegion}
alignItems="Center"