import React from 'react'; import Box from '@mui/material/Box'; import Paper from '@mui/material/Paper'; import IconButton from '@mui/material/IconButton'; import ChatIcon from '@mui/icons-material/Chat'; import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import Typography from '@mui/material/Typography'; import Avatar from '@mui/material/Avatar'; import Badge from '@mui/material/Badge'; import Toolbar from '@mui/material/Toolbar'; import Tooltip from '@mui/material/Tooltip'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import { useLayout } from '../../contexts/LayoutContext'; import { ChatProvider, useChat } from '../../contexts/ChatContext'; import { Link } from 'react-router-dom'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { configApi } from '../../services/config'; import { notificationsApi } from '../../services/notifications'; import { nextcloudApi } from '../../services/nextcloud'; import { safeOpenUrl } from '../../utils/safeOpenUrl'; import ChatRoomList from './ChatRoomList'; import ChatMessageView from './ChatMessageView'; const TRANSITION = 'width 225ms cubic-bezier(0.4, 0, 0.6, 1)'; const COLLAPSED_WIDTH = 64; const EXPANDED_WIDTH = 360; const ChatPanelInner: React.FC = () => { const { chatPanelOpen, setChatPanelOpen } = useLayout(); const { rooms, selectedRoomToken, selectRoom, connected } = useChat(); const queryClient = useQueryClient(); const markedRoomsRef = React.useRef(new Set()); const { data: externalLinks } = useQuery({ queryKey: ['external-links'], queryFn: () => configApi.getExternalLinks(), staleTime: 10 * 60 * 1000, }); const nextcloudUrl = externalLinks?.nextcloud; // Dismiss internal notifications when panel opens React.useEffect(() => { if (!chatPanelOpen) return; notificationsApi.dismissByType('nextcloud_talk').then(() => { queryClient.invalidateQueries({ queryKey: ['notifications'] }); queryClient.invalidateQueries({ queryKey: ['unreadNotificationCount'] }); }).catch(() => {}); }, [chatPanelOpen, queryClient]); // Mark unread rooms as read in Nextcloud whenever panel is open React.useEffect(() => { if (!chatPanelOpen) { markedRoomsRef.current.clear(); return; } const unread = rooms.filter( (r) => r.unreadMessages > 0 && !markedRoomsRef.current.has(r.token), ); if (unread.length === 0) return; unread.forEach((r) => markedRoomsRef.current.add(r.token)); Promise.allSettled(unread.map((r) => nextcloudApi.markAsRead(r.token))).then(() => { queryClient.invalidateQueries({ queryKey: ['nextcloud', 'rooms'] }); }); }, [chatPanelOpen, rooms, queryClient]); // Mark the selected room as read when a conversation is opened React.useEffect(() => { if (!selectedRoomToken) return; nextcloudApi.markAsRead(selectedRoomToken).then(() => { queryClient.invalidateQueries({ queryKey: ['nextcloud', 'rooms'] }); }).catch(() => {}); }, [selectedRoomToken, queryClient]); if (!chatPanelOpen) { return ( setChatPanelOpen(true)} aria-label="Chat öffnen"> {connected && ( {rooms.map((room) => ( { setChatPanelOpen(true); selectRoom(room.token); }} sx={{ p: 0.5 }} > {room.displayName.substring(0, 2).toUpperCase()} ))} )} ); } return ( theme.zIndex.drawer + 2, sm: 'auto' }, height: { xs: '100vh', sm: '100vh' }, display: 'flex', flexDirection: 'column', flexShrink: 0, transition: TRANSITION, overflow: 'hidden', }} > Chat {nextcloudUrl && ( safeOpenUrl(`${nextcloudUrl}/apps/spreed`)} aria-label="In Nextcloud öffnen"> )} setChatPanelOpen(false)} aria-label="Chat einklappen"> {!connected ? ( Nextcloud nicht verbunden. Bitte verbinden Sie sich in den Einstellungen. ) : selectedRoomToken ? ( {/* Compact room sidebar — hidden on mobile */} {rooms.map((room) => { const isSelected = room.token === selectedRoomToken; return ( selectRoom(room.token)} sx={{ display: 'flex', alignItems: 'center', gap: 0.75, px: 1, py: 0.5, cursor: 'pointer', borderRadius: 1, mx: 0.5, bgcolor: isSelected ? 'action.selected' : 'transparent', '&:hover': { bgcolor: isSelected ? 'action.selected' : 'action.hover', }, }} > {room.displayName.substring(0, 2).toUpperCase()} {room.displayName} ); })} ) : ( )} ); }; const ChatPanel: React.FC = () => { return ( ); }; export default ChatPanel;