feat: improvements
This commit is contained in:
@@ -3,11 +3,14 @@
|
||||
* Layout and styling aligned with Flux (same flex/overflow, bg-background, theme).
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useRef, forwardRef } from 'react'
|
||||
import { useLocation, useParams } from 'react-router-dom'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState, forwardRef } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useCreateBlockNote } from '@blocknote/react'
|
||||
import { BlockNoteView } from '@blocknote/shadcn'
|
||||
import { useTheme } from '@/lib/themeContext'
|
||||
import { useRecollectionActions } from '../RecollectionActionsContext'
|
||||
import { getLogosContent, setLogosContent, type StoredLogosContent } from '../recollectionStore'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
/** Wraps BlockNoteView so refs go to a div, not the function component (avoids ref warning). */
|
||||
const BlockNoteViewWrapper = forwardRef<HTMLDivElement, React.ComponentProps<typeof BlockNoteView>>(
|
||||
@@ -20,9 +23,6 @@ const BlockNoteViewWrapper = forwardRef<HTMLDivElement, React.ComponentProps<typ
|
||||
)
|
||||
}
|
||||
)
|
||||
import { useRecollectionMenubar } from '../RecollectionMenubarContext'
|
||||
import { getLogosContent, setLogosContent, type StoredLogosContent } from '../recollectionStore'
|
||||
|
||||
const SAVE_DEBOUNCE_MS = 400
|
||||
|
||||
/** Known React ref warning from BlockNote/Radix internals; we can't fix it in our code. Suppress once at load so it's active before first BlockNote render. */
|
||||
@@ -46,41 +46,42 @@ function patchBlockNoteRefWarning() {
|
||||
}
|
||||
|
||||
export function LogosPage() {
|
||||
const { pathname } = useLocation()
|
||||
const { recollectionId } = useParams<{ recollectionId: string }>()
|
||||
const { theme } = useTheme()
|
||||
const { setCustomContent } = useRecollectionMenubar()
|
||||
const isLogosActive = pathname.endsWith('/logos') || pathname.match(/\/recollections\/[^/]+\/?$/)
|
||||
const { setLogosSlot } = useRecollectionActions()
|
||||
const [reloadKey, setReloadKey] = useState(0)
|
||||
const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [saveStatus, setSaveStatus] = useState<'saved' | 'unsaved' | 'saving'>('saved')
|
||||
|
||||
const initialContent = useMemo(
|
||||
() => (recollectionId ? getLogosContent(recollectionId) ?? undefined : undefined),
|
||||
[recollectionId]
|
||||
[recollectionId, reloadKey]
|
||||
)
|
||||
|
||||
const editor = useCreateBlockNote({ initialContent }, [recollectionId])
|
||||
const editor = useCreateBlockNote({ initialContent }, [recollectionId, reloadKey])
|
||||
|
||||
const persistContent = useCallback(() => {
|
||||
if (!recollectionId || !editor) return
|
||||
setSaveStatus('saving')
|
||||
try {
|
||||
const doc = editor.document
|
||||
const serialized = JSON.parse(JSON.stringify(doc)) as StoredLogosContent
|
||||
setLogosContent(recollectionId, serialized)
|
||||
setSaveStatus('saved')
|
||||
} catch {
|
||||
// ignore serialize errors
|
||||
setSaveStatus('unsaved')
|
||||
}
|
||||
}, [recollectionId, editor])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLogosActive) return
|
||||
setCustomContent(null)
|
||||
return () => setCustomContent(null)
|
||||
}, [isLogosActive, setCustomContent])
|
||||
const onSave = useCallback(() => {
|
||||
persistContent()
|
||||
toast.success('Saved')
|
||||
}, [persistContent])
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor || !recollectionId) return
|
||||
const handleChange = () => {
|
||||
setSaveStatus('unsaved')
|
||||
if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current)
|
||||
saveTimeoutRef.current = setTimeout(() => {
|
||||
saveTimeoutRef.current = null
|
||||
@@ -96,6 +97,28 @@ export function LogosPage() {
|
||||
}
|
||||
}, [editor, recollectionId, persistContent])
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor || !recollectionId) return
|
||||
const slot = {
|
||||
saveStatus,
|
||||
onSave,
|
||||
canSave: true,
|
||||
undo: () => (editor as { undo?: () => void }).undo?.(),
|
||||
redo: () => (editor as { redo?: () => void }).redo?.(),
|
||||
canUndo: true,
|
||||
canRedo: true,
|
||||
onRefreshFromStore: () => setReloadKey((k) => k + 1),
|
||||
}
|
||||
setLogosSlot(slot)
|
||||
return () => setLogosSlot(null)
|
||||
}, [
|
||||
setLogosSlot,
|
||||
saveStatus,
|
||||
onSave,
|
||||
editor,
|
||||
recollectionId,
|
||||
])
|
||||
|
||||
if (!recollectionId) {
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-1 flex-col items-center justify-center p-8 text-muted-foreground">
|
||||
|
||||
Reference in New Issue
Block a user