feat: implement sidebar docked call panel with participant management and sync status handling
This commit is contained in:
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user