Jaimodiji's picture
Sync all client files to fix build
e1753d8 verified
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<HTMLInputElement>(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 (
<div
onClick={onClick}
onMouseEnter={() => 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,
}}
>
<div style={{
marginRight: 10,
color: isActive ? colors.selected : colors.textMuted,
display: 'flex',
}}>
<FileIcon />
</div>
<div style={{ flex: 1, minWidth: 0 }}>
{isEditing ? (
<input
ref={inputRef}
value={tempName}
onChange={(e) => 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',
}}
/>
) : (
<div
onDoubleClick={(e) => {
e.stopPropagation()
setIsEditing(true)
}}
style={{
fontSize: 13,
fontWeight: isActive ? 600 : 400,
color: theme.text,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{room.name}
</div>
)}
<div style={{
fontSize: 11,
color: colors.textMuted,
marginTop: 2,
}}>
{new Date(room.lastVisited).toLocaleDateString()}
</div>
</div>
{!isEditing && hovered && (
<button
onClick={(e) => {
e.stopPropagation()
onDelete(room.id)
}}
style={{
background: 'none',
border: 'none',
color: colors.textMuted,
cursor: 'pointer',
padding: 4,
display: 'flex',
}}
>
<CloseIcon />
</button>
)}
</div>
)
}
export function RoomList({ currentRoomId, theme, onClose }: { currentRoomId: string, theme: any, onClose: () => void }) {
const navigate = useNavigate()
const [rooms, setRooms] = useState<RoomMetadata[]>([])
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 (
<>
<div style={{ padding: 12 }}>
<button
onClick={handleCreate}
style={{
width: '100%',
padding: '10px 16px',
background: colors.selected,
color: '#fff',
border: 'none',
borderRadius: 8,
fontSize: 14,
fontWeight: 500,
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
}}
>
<PlusIcon />
New Board
</button>
</div>
<div style={{
padding: '0 12px',
fontSize: 11,
fontWeight: 600,
color: colors.textMuted,
textTransform: 'uppercase',
letterSpacing: '0.5px',
marginTop: 12,
}}>
Recent
</div>
<div style={{
flex: 1,
overflowY: 'auto',
padding: '8px 8px 12px',
}}>
{rooms.map(room => (
<FileItem
key={room.id}
room={room}
isActive={room.id === currentRoomId}
theme={theme}
onClick={() => {
if (room.id !== currentRoomId) {
navigate(`/${room.id}`)
onClose()
}
}}
onRename={handleRename}
onDelete={handleDelete}
/>
))}
</div>
</>
)
}