fix: dynamic note restore without page reload + fix note list sync bugs
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 44s

- NoteHistoryModal: remove window.location.reload(), use onRestored(restored) callback
- NotesTabsView: revert sync derivation to useEffect, add prevNotesRef to detect
  server-side content changes (restore) vs local edits — fixes note disappear bug
  and cross-notebook notes appearing after refresh
- NoteInlineEditor key: include updatedAt so restoration remounts editor with fresh content
- note-card: render title/content/labels from note prop directly, not optimisticNote
This commit is contained in:
2026-05-02 20:18:18 +02:00
parent a3651f1c96
commit f93752de14

View File

@@ -643,46 +643,50 @@ export function NotesTabsView({
const prevNotesRef = useRef<Note[]>(notes) const prevNotesRef = useRef<Note[]>(notes)
if (notes !== prevNotesRef.current) { useEffect(() => {
const prevIds = items.map((n) => n.id).join(',') setItems((prev) => {
const incomingIds = notes.map((n) => n.id).join(',') const prevIds = prev.map((n) => n.id).join(',')
const incomingIds = notes.map((n) => n.id).join(',')
const merge = (fresh: Note, local: Note, oldFresh?: Note) => { const merge = (fresh: Note, local: Note) => {
const labelsChanged = JSON.stringify(fresh.labels?.sort()) !== JSON.stringify(local.labels?.sort()) // Detect if the server explicitly changed content since last sync
const prevServer = prevNotesRef.current.find((n) => n.id === fresh.id)
const serverContentChanged = prevServer ? prevServer.content !== fresh.content : false
const serverTitleChanged = prevServer ? prevServer.title !== fresh.title : false
const serverCheckItemsChanged = prevServer
? JSON.stringify(prevServer.checkItems) !== JSON.stringify(fresh.checkItems)
: false
const labelsChanged =
JSON.stringify(fresh.labels?.slice().sort()) !==
JSON.stringify(local.labels?.slice().sort())
const contentChangedOnServer = oldFresh && oldFresh.content !== fresh.content return {
const titleChangedOnServer = oldFresh && oldFresh.title !== fresh.title ...fresh,
const checkItemsChangedOnServer = oldFresh && JSON.stringify(oldFresh.checkItems) !== JSON.stringify(fresh.checkItems) title: serverTitleChanged ? fresh.title : local.title || fresh.title,
content: serverContentChanged ? fresh.content : local.content,
return { checkItems: serverCheckItemsChanged ? fresh.checkItems : local.checkItems,
...fresh, labels: labelsChanged ? fresh.labels : local.labels,
title: titleChangedOnServer ? fresh.title : (local.title || fresh.title), }
content: contentChangedOnServer ? fresh.content : local.content,
checkItems: checkItemsChangedOnServer ? fresh.checkItems : local.checkItems,
labels: labelsChanged ? fresh.labels : local.labels
} }
}
let newItems: Note[] let result: Note[]
if (prevIds === incomingIds) { if (prevIds === incomingIds) {
newItems = items.map((local) => { result = prev.map((local) => {
const fresh = notes.find((n) => n.id === local.id) const fresh = notes.find((n) => n.id === local.id)
if (!fresh) return local return fresh ? merge(fresh, local) : local
const oldFresh = prevNotesRef.current.find((n) => n.id === local.id) })
return merge(fresh, local, oldFresh) } else {
}) result = notes.map((fresh) => {
} else { const local = prev.find((p) => p.id === fresh.id)
newItems = notes.map((fresh) => { return local ? merge(fresh, local) : fresh
const local = items.find((p) => p.id === fresh.id) })
if (!local) return fresh }
const oldFresh = prevNotesRef.current.find((n) => n.id === fresh.id)
return merge(fresh, local, oldFresh) return result
}) })
}
setItems(newItems)
prevNotesRef.current = notes prevNotesRef.current = notes
} }, [notes])
useEffect(() => { useEffect(() => {
if (items.length === 0) { if (items.length === 0) {