4db65993d5
Visual:
- Light/dark theme via ThemeContext + CSS vars
- New design tokens (surface/fg/accent/line/etc.) across all pages
- Reusable Avatar component (img + letter fallback) wired into UserBar,
ConversationHeader, ChatsPage, FriendsPage, GroupInfoPanel, IncomingCallPanel
Calls:
- Split call UI: IncomingCallPanel, InCallPanel, CallControls,
CallParticipantTile, ScreenShareViewer
- Active speaker hook (useActiveSpeakers)
- Fix: ActiveCallBanner stayed hidden after hangup while peers in room.
- useCallPresence: bind presence callbacks only when we own subscribe
(Supabase forbids .on() after .subscribe() on shared dedup'd channels)
- useCallPresence: never removeChannel — channel is shared with CallContext
so tearing it down on ConversationHeader unmount killed live tracking
- ActiveCallBanner: lastCallConversationId fallback so banner shows
instantly after hangup, auto-dismiss when room confirmed empty
- Drop unused useAnyActiveCall
Bump tauri version 0.5.0 -> 0.6.0
63 lines
1.9 KiB
TypeScript
63 lines
1.9 KiB
TypeScript
import { useEffect } from 'react';
|
|
|
|
import { XIcon } from './icons';
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
title: string;
|
|
onClose: () => void;
|
|
children: React.ReactNode;
|
|
size?: 'md' | 'lg';
|
|
}
|
|
|
|
export function Modal({ open, title, onClose, children, size = 'md' }: Props) {
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
function onKey(e: KeyboardEvent) {
|
|
if (e.key === 'Escape') onClose();
|
|
}
|
|
document.addEventListener('keydown', onKey);
|
|
const prevOverflow = document.body.style.overflow;
|
|
document.body.style.overflow = 'hidden';
|
|
return () => {
|
|
document.removeEventListener('keydown', onKey);
|
|
document.body.style.overflow = prevOverflow;
|
|
};
|
|
}, [open, onClose]);
|
|
|
|
if (!open) return null;
|
|
|
|
const width = size === 'lg' ? 'max-w-xl' : 'max-w-md';
|
|
|
|
return (
|
|
<div
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-label={title}
|
|
onClick={onClose}
|
|
className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-6 backdrop-blur-sm animate-fade-in"
|
|
>
|
|
<div
|
|
onClick={(e) => e.stopPropagation()}
|
|
className={
|
|
'relative w-full animate-slide-up rounded-2xl border border-line bg-surface-3 shadow-xl ' +
|
|
width
|
|
}
|
|
>
|
|
<header className="flex items-center justify-between border-b border-line px-6 py-4">
|
|
<h2 className="font-display text-lg font-semibold text-fg">{title}</h2>
|
|
<button
|
|
type="button"
|
|
onClick={onClose}
|
|
aria-label="Close"
|
|
className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg text-fg-muted transition hover:bg-surface-2 hover:text-fg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/40"
|
|
>
|
|
<XIcon className="h-4 w-4" />
|
|
</button>
|
|
</header>
|
|
<div className="max-h-[75vh] overflow-y-auto p-6">{children}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|