update for shape rendering in prod

This commit is contained in:
Jeff Emmett 2025-11-10 18:43:52 -08:00
parent bda2523e3b
commit b5463d4d64
1 changed files with 120 additions and 9 deletions

View File

@ -1,6 +1,6 @@
import { useAutomergeSync } from "@/automerge/useAutomergeSync"
import { AutomergeHandleProvider } from "@/context/AutomergeHandleContext"
import { useMemo, useEffect, useState } from "react"
import { useMemo, useEffect, useState, useRef } from "react"
import { Tldraw, Editor, TLShapeId } from "tldraw"
import { useParams } from "react-router-dom"
import { ChatBoxTool } from "@/tools/ChatBoxTool"
@ -212,6 +212,9 @@ export function Board() {
const automergeHandle = (storeWithHandle as any).handle
const [editor, setEditor] = useState<Editor | null>(null)
// Ref for production polling interval
const statusCheckIntervalRef = useRef<NodeJS.Timeout | null>(null)
useEffect(() => {
const value = localStorage.getItem("makereal_settings_2")
if (value) {
@ -235,20 +238,40 @@ export function Board() {
const checkAndFixMissingShapes = () => {
if (!editor || !store.store) return
// Only check if store is synced
if (store.status !== 'synced-remote') {
// In production, be more lenient - check if store has shapes even if not fully synced
// This handles timing issues where shapes are loaded but status hasn't updated yet
const storeShapes = store.store.allRecords().filter((r: any) => r.typeName === 'shape') || []
const hasShapes = storeShapes.length > 0
// Only check if store is synced OR if we have shapes (production workaround)
// In dev, we can be strict, but in production we need to be more lenient
const isProduction = import.meta.env.PROD || import.meta.env.MODE === 'production'
if (store.status !== 'synced-remote' && (!isProduction || !hasShapes)) {
return
}
const editorShapes = editor.getCurrentPageShapes()
const storeShapes = store.store.allRecords().filter((r: any) => r.typeName === 'shape') || []
const currentPageId = editor.getCurrentPageId()
// Get shapes on current page from store
const storeShapesOnCurrentPage = storeShapes.filter((s: any) => s.parentId === currentPageId)
// Debug: Log page information
const allPages = store.store.allRecords().filter((r: any) => r.typeName === 'page')
console.log(`📊 Board: Current page ID: ${currentPageId}`)
console.log(`📊 Board: Available pages:`, allPages.map((p: any) => ({ id: p.id, name: p.name })))
console.log(`📊 Board: Store has ${storeShapes.length} total shapes, ${storeShapesOnCurrentPage.length} on current page. Editor sees ${editorShapes.length} shapes on current page.`)
// Debug: Log shape parent IDs to see if there's a mismatch
if (storeShapes.length > 0 && editorShapes.length === 0) {
const parentIdCounts = new Map<string, number>()
storeShapes.forEach((s: any) => {
const pid = s.parentId || 'no-parent'
parentIdCounts.set(pid, (parentIdCounts.get(pid) || 0) + 1)
})
console.log(`📊 Board: Shape parent ID distribution:`, Array.from(parentIdCounts.entries()))
}
// Check if there are shapes in store on current page that editor can't see
if (storeShapesOnCurrentPage.length > editorShapes.length) {
const editorShapeIds = new Set(editorShapes.map(s => s.id))
@ -279,6 +302,28 @@ export function Board() {
} else {
// Shapes don't exist in editor - might be a sync issue
console.error(`📊 Board: ${missingShapes.length} shapes are in store but don't exist in editor - possible sync issue`)
// Try to force a refresh by updating the store
// This might help if shapes are stuck in a validation error state
console.log(`📊 Board: Attempting to refresh store to make shapes visible`)
try {
// Force a store update by reading and re-putting the shapes
const shapesToRefresh = missingShapes.slice(0, 10) // Limit to first 10 to avoid performance issues
const refreshedShapes = shapesToRefresh.map((s: any) => {
const shapeFromStore = store.store.get(s.id)
if (shapeFromStore) {
return shapeFromStore
}
return null
}).filter((s): s is NonNullable<typeof s> => s !== null)
if (refreshedShapes.length > 0) {
console.log(`📊 Board: Refreshing ${refreshedShapes.length} shapes in store`)
store.store.put(refreshedShapes)
}
} catch (error) {
console.error(`📊 Board: Error refreshing shapes:`, error)
}
}
// Check if shapes are outside viewport
@ -315,7 +360,7 @@ export function Board() {
}
// Also check for shapes on other pages
const shapesOnOtherPages = storeShapes.filter((s: any) => s.parentId !== currentPageId)
const shapesOnOtherPages = storeShapes.filter((s: any) => s.parentId && s.parentId !== currentPageId)
if (shapesOnOtherPages.length > 0) {
console.log(`📊 Board: ${shapesOnOtherPages.length} shapes exist on other pages (not current page ${currentPageId})`)
@ -327,6 +372,23 @@ export function Board() {
}
})
// Also check for shapes with no parentId or invalid parentId
const shapesWithInvalidParent = storeShapes.filter((s: any) => !s.parentId || (s.parentId && !allPages.find((p: any) => p.id === s.parentId)))
if (shapesWithInvalidParent.length > 0) {
console.warn(`📊 Board: ${shapesWithInvalidParent.length} shapes have invalid or missing parentId. Fixing...`)
// Fix shapes with invalid parentId by assigning them to current page
const fixedShapes = shapesWithInvalidParent.map((s: any) => ({
...s,
parentId: currentPageId
}))
try {
store.store.put(fixedShapes)
console.log(`📊 Board: Fixed ${fixedShapes.length} shapes by assigning them to current page ${currentPageId}`)
} catch (error) {
console.error(`📊 Board: Error fixing shapes with invalid parentId:`, error)
}
}
// Find the page with the most shapes
let maxShapes = 0
let pageWithMostShapes: string | null = null
@ -345,6 +407,7 @@ export function Board() {
// Focus camera on shapes after switching
setTimeout(() => {
const newPageShapes = editor.getCurrentPageShapes()
console.log(`📊 Board: After page switch, editor sees ${newPageShapes.length} shapes on page ${pageWithMostShapes}`)
if (newPageShapes.length > 0) {
const bounds = editor.getShapePageBounds(newPageShapes[0])
if (bounds) {
@ -354,6 +417,27 @@ export function Board() {
z: editor.getCamera().z
}, { animation: { duration: 300 } })
}
} else {
// Still no shapes after switching - might be a validation issue
console.warn(`📊 Board: After switching to page ${pageWithMostShapes}, still no shapes visible. Checking store...`)
const shapesOnNewPage = storeShapes.filter((s: any) => s.parentId === pageWithMostShapes)
console.log(`📊 Board: Store has ${shapesOnNewPage.length} shapes on page ${pageWithMostShapes}`)
if (shapesOnNewPage.length > 0) {
// Try to manually add shapes that might have validation issues
console.log(`📊 Board: Attempting to force visibility by selecting all shapes on page`)
const shapeIds = shapesOnNewPage.map((s: any) => s.id).filter((id): id is TLShapeId => id !== undefined)
if (shapeIds.length > 0) {
// Try to get shapes from editor to see if they exist
const existingShapes = shapeIds
.map(id => editor.getShape(id))
.filter((s): s is NonNullable<typeof s> => s !== undefined)
console.log(`📊 Board: ${existingShapes.length} of ${shapeIds.length} shapes exist in editor`)
if (existingShapes.length > 0) {
editor.setSelectedShapes(existingShapes.map(s => s.id))
editor.zoomToFit()
}
}
}
}
}, 100)
} catch (error) {
@ -369,18 +453,45 @@ export function Board() {
}
}
// Initial check
checkAndFixMissingShapes()
// Initial check - with delay in production to handle timing issues
const isProduction = import.meta.env.PROD || import.meta.env.MODE === 'production'
const initialDelay = isProduction ? 1000 : 0
setTimeout(checkAndFixMissingShapes, initialDelay)
// Listen to store changes to continuously monitor for missing shapes
// Listen to ALL sources (user, remote, etc.) to catch shapes loaded from Automerge
const unsubscribe = store.store.listen(() => {
// Debounce the check to avoid excessive calls
setTimeout(checkAndFixMissingShapes, 500)
// In production, use longer debounce to ensure shapes are fully loaded
const debounceDelay = isProduction ? 1000 : 500
setTimeout(checkAndFixMissingShapes, debounceDelay)
})
// Also listen to store status changes - critical for production
// In production, shapes might load after status changes
if (isProduction) {
// Poll every 2 seconds in production until shapes are visible
statusCheckIntervalRef.current = setInterval(() => {
const storeShapes = store.store?.allRecords().filter((r: any) => r.typeName === 'shape') || []
const editorShapes = editor?.getCurrentPageShapes() || []
if (storeShapes.length > 0 && editorShapes.length === 0) {
console.log(`📊 Production: Store has ${storeShapes.length} shapes but editor sees 0. Running fix...`)
checkAndFixMissingShapes()
} else if (editorShapes.length > 0) {
// Shapes are visible, stop polling
if (statusCheckIntervalRef.current) {
clearInterval(statusCheckIntervalRef.current)
statusCheckIntervalRef.current = null
}
}
}, 2000)
}
return () => {
unsubscribe()
if (statusCheckIntervalRef.current) {
clearInterval(statusCheckIntervalRef.current)
statusCheckIntervalRef.current = null
}
}
}, [editor, store.store, store.status])