import { useState, useEffect, useRef } from 'react' import { useNavigate } from 'react-router-dom' import { uniqueId } from 'tldraw' // uniqueId from tldraw import { RoomMetadata, getRooms, saveRoom, updateRoomName, deleteRoom } from '../pages/storageUtils' import { colors } from '../constants/theme' // Corrected path import { PlusIcon, FileIcon, CloseIcon } from './Icons' import { SERVER_URL } from '../config' // Added import import { customConfirm } from '../utils/uiUtils' // Added customConfirm interface FileItemProps { room: RoomMetadata isActive: boolean theme: { bg: string; border: string; hover: string; text: string } onClick: () => void onRename: (id: string, name: string) => void onDelete: (id: string) => void } function FileItem({ room, isActive, theme, onClick, onRename, onDelete }: FileItemProps) { const [isEditing, setIsEditing] = useState(false) const [tempName, setTempName] = useState(room.name) const [hovered, setHovered] = useState(false) const inputRef = useRef(null) useEffect(() => { if (isEditing && inputRef.current) { inputRef.current.focus() inputRef.current.select() } }, [isEditing]) const handleFinish = () => { if (tempName.trim()) { onRename(room.id, tempName.trim()) } else { setTempName(room.name) } setIsEditing(false) } return (
setHovered(true)} onMouseLeave={() => setHovered(false)} style={{ display: 'flex', alignItems: 'center', padding: '8px 10px', borderRadius: 6, background: isActive ? theme.hover : (hovered ? theme.hover : 'transparent'), cursor: 'pointer', marginBottom: 2, }} >
{isEditing ? ( setTempName(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') handleFinish() if (e.key === 'Escape') { setTempName(room.name) setIsEditing(false) } }} onBlur={handleFinish} onClick={(e) => e.stopPropagation()} style={{ width: '100%', padding: '2px 6px', background: theme.bg, border: `2px solid ${colors.selected}`, borderRadius: 4, fontSize: 13, color: theme.text, outline: 'none', }} /> ) : (
{ e.stopPropagation() setIsEditing(true) }} style={{ fontSize: 13, fontWeight: isActive ? 600 : 400, color: theme.text, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', }} > {room.name}
)}
{new Date(room.lastVisited).toLocaleDateString()}
{!isEditing && hovered && ( )}
) } export function RoomList({ currentRoomId, theme, onClose }: { currentRoomId: string, theme: any, onClose: () => void }) { const navigate = useNavigate() const [rooms, setRooms] = useState([]) useEffect(() => { setRooms(getRooms().sort((a, b) => b.lastVisited - a.lastVisited)) }, []) const handleCreate = () => { const newId = uniqueId() saveRoom(newId, `Untitled Board ${rooms.length + 1}`) onClose() navigate(`/${newId}`) } const handleRename = (id: string, newName: string) => { const updated = updateRoomName(id, newName) setRooms(updated.sort((a, b) => b.lastVisited - a.lastVisited)) // Sync to server fetch(`${SERVER_URL}/api/meta/${id}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: newName }) }).catch(err => console.error('Failed to sync room name:', err)) } const handleDelete = async (id: string) => { if (await customConfirm('Delete this board?')) { const updated = deleteRoom(id) setRooms(updated) if (id === currentRoomId) navigate('/') } } return ( <>
Recent
{rooms.map(room => ( { if (room.id !== currentRoomId) { navigate(`/${room.id}`) onClose() } }} onRename={handleRename} onDelete={handleDelete} /> ))}
) }