my-multiplayer-app-2 / components /StatePersistence.tsx
Jaimodiji's picture
Sync all client files to fix build
e1753d8 verified
import { useEffect, useMemo } from 'react'
import { useEditor } from 'tldraw'
import throttle from 'lodash.throttle'
export function StatePersistence({ roomId }: { roomId: string }) {
const editor = useEditor()
// Create a throttled save function that persists across renders
const saveRoomState = useMemo(() => throttle((editorInstance: any, key: string) => {
try {
const { x, y, z } = editorInstance.getCamera()
const instanceState = editorInstance.getInstanceState()
const stateToSave = {
camera: { x, y, z },
pageId: editorInstance.getCurrentPageId(),
isGridMode: instanceState.isGridMode,
isFocusMode: instanceState.isFocusMode,
isDebugMode: instanceState.isDebugMode,
isToolLocked: instanceState.isToolLocked,
exportBackground: instanceState.exportBackground
}
localStorage.setItem(key, JSON.stringify(stateToSave))
} catch (e) {
// Ignore errors
}
}, 1000), [])
useEffect(() => {
if (!editor) return
// 1. Load Global Preferences & Sanitize
const globalPrefsStr = localStorage.getItem('tldraw_global_prefs')
if (globalPrefsStr) {
try {
const savedPrefs = JSON.parse(globalPrefsStr)
const sanitizedPrefs: any = {}
// Get the default preferences to know what keys are valid
const defaultPrefs = editor.user.getUserPreferences()
// This is a migration: if the old isDarkMode property is present,
// we want to convert it to the new `colorScheme` property.
if (typeof savedPrefs.isDarkMode === 'boolean') {
savedPrefs.colorScheme = savedPrefs.isDarkMode ? 'dark' : 'light'
delete savedPrefs.isDarkMode
}
// Only copy over the keys that are present in the default preferences
for (const key in defaultPrefs) {
if (key in savedPrefs) {
sanitizedPrefs[key] = savedPrefs[key]
}
}
editor.user.updateUserPreferences(sanitizedPrefs)
} catch (e) {
console.error('Failed to load and apply preferences:', e)
}
}
// 2. Load Room State (Camera, Grid, etc.)
const roomStateKey = `tldraw_room_state_${roomId}`
const savedRoomState = localStorage.getItem(roomStateKey)
if (savedRoomState) {
try {
const state = JSON.parse(savedRoomState)
// Restore Page
if (state.pageId && editor.getPage(state.pageId)) {
editor.setCurrentPage(state.pageId)
}
// Restore Camera
if (state.camera) {
editor.setCamera(state.camera)
}
// Restore Instance State (safely)
// We construct a clean object containing only defined booleans
const newInstanceState: any = {}
if (typeof state.isGridMode === 'boolean') newInstanceState.isGridMode = state.isGridMode
if (typeof state.isFocusMode === 'boolean') newInstanceState.isFocusMode = state.isFocusMode
if (typeof state.isDebugMode === 'boolean') newInstanceState.isDebugMode = state.isDebugMode
if (typeof state.isToolLocked === 'boolean') newInstanceState.isToolLocked = state.isToolLocked
if (typeof state.exportBackground === 'boolean') newInstanceState.exportBackground = state.exportBackground
// Only update if we have keys to update
if (Object.keys(newInstanceState).length > 0) {
editor.updateInstanceState(newInstanceState)
}
} catch (e) {
console.error('Failed to load room state:', e)
}
}
// 3. Setup Listeners to Save State
const cleanupUser = editor.store.listen(() => {
localStorage.setItem('tldraw_global_prefs', JSON.stringify(editor.user.getUserPreferences()))
})
const cleanupRoom = editor.store.listen(() => {
saveRoomState(editor, roomStateKey)
})
return () => {
cleanupUser()
cleanupRoom()
saveRoomState.cancel()
}
}, [editor, roomId, saveRoomState])
return null
}