import { useState, useRef, useEffect, ChangeEvent } from 'react' import { useEditor, TldrawUiMenuItem, uniqueId } from 'tldraw' import { useAuth } from '../hooks/useAuth' import { useBackups } from '../hooks/useBackups' import { exportToImage } from '../utils/exportUtils' import { triggerSvgImport } from '../utils/svgImport' import { Filesystem, Directory, Encoding } from '@capacitor/filesystem' import { Share } from '@capacitor/share' import { Capacitor } from '@capacitor/core' export function BackupMenuItem({ roomId }: { roomId: string }) { const editor = useEditor() const { isAuthenticated } = useAuth() const { createBackup } = useBackups() const [isBackingUp, setIsBackingUp] = useState(false) if (!isAuthenticated) return null const handleBackup = async () => { if (isBackingUp) return const confirmBackup = window.confirm("Save a backup of this board to the cloud?") if (!confirmBackup) return setIsBackingUp(true) try { const snapshot = editor.getSnapshot() // Get room name if available (from local storage or metadata?) // We can try to get it from the store if we stored it there, // otherwise just use the ID or ask the user. // For now let's check localStorage for the name we saved in Lobby/RoomPage // Try to find name in local storage first let roomName = 'Untitled' try { const storedRooms = localStorage.getItem('tldraw_saved_rooms') if (storedRooms) { const parsed = JSON.parse(storedRooms) const room = parsed.find((r: any) => r.id === roomId) if (room) roomName = room.name } } catch (e) {} await createBackup(snapshot, roomName, roomId, 'tldraw') alert('Backup saved successfully!') } catch (e: any) { console.error(e) alert('Failed to save backup: ' + e.message) } finally { setIsBackingUp(false) } } return ( ) } export function DownloadMenuItem({ roomId }: { roomId: string }) { const editor = useEditor() const handleDownload = async () => { try { const snapshot = editor.getSnapshot() const jsonStr = JSON.stringify({ snapshot, roomId, timestamp: Date.now(), source: 'tldraw-multiplayer' }, null, 2) const fileName = `tldraw-room-${roomId}-${new Date().toISOString().slice(0,10)}.json` if (Capacitor.isNativePlatform()) { // Native (Android/iOS): Use Filesystem + Share try { const savedFile = await Filesystem.writeFile({ path: fileName, data: jsonStr, directory: Directory.Cache, encoding: Encoding.UTF8 }) await Share.share({ title: 'Backup Board JSON', files: [savedFile.uri], }) } catch (err: any) { console.error('Native save failed', err) alert('Failed to save file: ' + err.message) } } else { // Web: Use anchor tag download const blob = new Blob([jsonStr], { type: 'application/json' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = fileName document.body.appendChild(link) link.click() document.body.removeChild(link) URL.revokeObjectURL(url) } } catch (e: any) { console.error('Failed to download backup', e) alert('Failed to download backup: ' + e.message) } } return ( ) } export function RestoreMenuItem() { const handleRestoreClick = () => { window.dispatchEvent(new CustomEvent('tldraw-trigger-file-restore')) } return ( ) } export function RestoreFileHandler({ roomId: _roomId }: { roomId: string }) { const editor = useEditor() const inputRef = useRef(null) useEffect(() => { const handleTrigger = () => { if (inputRef.current) { inputRef.current.click() } } window.addEventListener('tldraw-trigger-file-restore', handleTrigger) return () => window.removeEventListener('tldraw-trigger-file-restore', handleTrigger) }, []) const handleFileChange = (e: ChangeEvent) => { const file = e.target.files?.[0] if (!file) return const reader = new FileReader() reader.onload = (event) => { try { const jsonStr = event.target?.result as string const data = JSON.parse(jsonStr) // Handle both raw snapshot or our wrapped format const snapshot = data.snapshot || data const mode = window.confirm('Restore as a NEW board? (Click Cancel to OVERWRITE the current board)') if (mode) { // Restore as New const newId = uniqueId() const name = data.roomName || 'Restored Board' // Store for the new room to pick up localStorage.setItem(`restore_data_${newId}`, JSON.stringify({ snapshot, roomName: name })) // Save to recents const storedRooms = localStorage.getItem('tldraw_saved_rooms') let recentRooms = [] try { if (storedRooms) recentRooms = JSON.parse(storedRooms) } catch (e) {} recentRooms.push({ id: newId, name: name, lastVisited: Date.now() }) localStorage.setItem('tldraw_saved_rooms', JSON.stringify(recentRooms)) // Open in new tab or navigate? // Let's navigate to keep it simple, or open in new tab if requested. // Implementation plan said "Creates new Room -> Loads snapshot -> Navigates to room" window.location.assign(`/#/${newId}`) } else { // Overwrite Current if (window.confirm('WARNING: This will permanently overwrite the current board content for everyone. Are you sure?')) { editor.loadSnapshot(snapshot) } } } catch (e: any) { console.error('Failed to parse backup file', e) alert('Failed to restore backup: Invalid file format (' + e.message + ')') } finally { // Reset input if (inputRef.current) inputRef.current.value = '' } } reader.onerror = (err) => { alert('FileReader Error: ' + err) } reader.readAsText(file) } return ( ) } export function PdfExportMenuItem({ roomId }: { roomId: string }) { const editor = useEditor() const [isExporting, setIsExporting] = useState(false) const handleExport = async () => { if (isExporting) return setIsExporting(true) try { await exportToImage(editor, roomId, 'pdf') } catch (e) { console.error(e) } finally { setIsExporting(false) } } return ( ) } export function ImportSvgMenuItem() { const editor = useEditor() const handleImport = () => { triggerSvgImport(editor) } return ( ) }