mirror of
https://github.com/cudr/slate-collaborative.git
synced 2024-10-27 20:34:06 +00:00
fix: root level children update
This commit is contained in:
parent
a887e26391
commit
b5bd7fe750
@ -5,7 +5,6 @@ interface TestDoc {
|
|||||||
status: string
|
status: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: delete this?
|
|
||||||
describe('old state error replication', () => {
|
describe('old state error replication', () => {
|
||||||
const clientDocSet = new Automerge.DocSet()
|
const clientDocSet = new Automerge.DocSet()
|
||||||
const serverDocSet = new Automerge.DocSet()
|
const serverDocSet = new Automerge.DocSet()
|
||||||
|
@ -23,11 +23,36 @@ const opSet = (op: Automerge.Diff, [map, ops]: any, doc: any) => {
|
|||||||
const { link, value, path, obj, key } = op
|
const { link, value, path, obj, key } = op
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// no slate op needed for root key cursor updates
|
// We can ignore any root level cursor updates since those
|
||||||
|
// will not correspond to any slate operations
|
||||||
if (obj === rootKey && key === 'cursors') {
|
if (obj === rootKey && key === 'cursors') {
|
||||||
return [map, ops]
|
return [map, ops]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle updates received for the root children array
|
||||||
|
if (obj === rootKey && key === 'children' && map[value]) {
|
||||||
|
// First remove all existing child nodes
|
||||||
|
for (let i = doc.children.length - 1; i >= 0; i--) {
|
||||||
|
ops.push((map: any) => ({
|
||||||
|
type: 'remove_node',
|
||||||
|
path: [i],
|
||||||
|
node: doc.children[i]
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then add all the newly defined nodes
|
||||||
|
const newChildren: Node[] = map[value]
|
||||||
|
newChildren.forEach((child, index) => {
|
||||||
|
ops.push((map: any) => ({
|
||||||
|
type: 'insert_node',
|
||||||
|
path: [index],
|
||||||
|
node: child
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
return [map, ops]
|
||||||
|
}
|
||||||
|
|
||||||
if (path && path[0] !== 'cursors') {
|
if (path && path[0] !== 'cursors') {
|
||||||
ops.push(setDataOp(op, doc))
|
ops.push(setDataOp(op, doc))
|
||||||
} else if (map[obj]) {
|
} else if (map[obj]) {
|
||||||
|
@ -137,13 +137,15 @@ export const AutomergeConnector = {
|
|||||||
e.handleError(err, {
|
e.handleError(err, {
|
||||||
type: 'applyOperation - toSlateOp',
|
type: 'applyOperation - toSlateOp',
|
||||||
operations,
|
operations,
|
||||||
current: Automerge.save(current)
|
current: Automerge.save(current),
|
||||||
|
updated: Automerge.save(updated)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
e.isRemote = true
|
e.isRemote = true
|
||||||
|
|
||||||
Editor.withoutNormalizing(e, () => {
|
Editor.withoutNormalizing(e, () => {
|
||||||
|
try {
|
||||||
if (HistoryEditor.isHistoryEditor(e) && !preserveExternalHistory) {
|
if (HistoryEditor.isHistoryEditor(e) && !preserveExternalHistory) {
|
||||||
HistoryEditor.withoutSaving(e, () => {
|
HistoryEditor.withoutSaving(e, () => {
|
||||||
slateOps.forEach((o: Operation) => e.apply(o))
|
slateOps.forEach((o: Operation) => e.apply(o))
|
||||||
@ -151,6 +153,15 @@ export const AutomergeConnector = {
|
|||||||
} else {
|
} else {
|
||||||
slateOps.forEach((o: Operation) => e.apply(o))
|
slateOps.forEach((o: Operation) => e.apply(o))
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
e.handleError(err, {
|
||||||
|
type: 'applyOperation - slateOps apply',
|
||||||
|
operations,
|
||||||
|
slateOps,
|
||||||
|
current: Automerge.save(current),
|
||||||
|
updated: Automerge.save(updated)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
e.onCursor && e.onCursor(updated.cursors)
|
e.onCursor && e.onCursor(updated.cursors)
|
||||||
})
|
})
|
||||||
@ -180,7 +191,7 @@ export const AutomergeConnector = {
|
|||||||
if (!doc) return
|
if (!doc) return
|
||||||
|
|
||||||
const changed = Automerge.change<SyncDoc>(doc, (d: any) => {
|
const changed = Automerge.change<SyncDoc>(doc, (d: any) => {
|
||||||
delete d.cursors
|
d.cursors = {}
|
||||||
})
|
})
|
||||||
|
|
||||||
e.docSet.setDoc(docId, changed as any)
|
e.docSet.setDoc(docId, changed as any)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Automerge, { Frontend } from 'automerge'
|
import Automerge from 'automerge'
|
||||||
import { createServer } from 'http'
|
import { createServer } from 'http'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import isEqual from 'lodash/isEqual'
|
import isEqual from 'lodash/isEqual'
|
||||||
@ -60,9 +60,17 @@ describe('automerge editor client tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const createCollabEditor = async (
|
const createCollabEditor = async (
|
||||||
editorOptions: AutomergeOptions & SocketIOPluginOptions = options
|
editorOptions?: Partial<AutomergeOptions> & Partial<SocketIOPluginOptions>
|
||||||
) => {
|
) => {
|
||||||
const editor = withIOCollaboration(createEditor(), editorOptions)
|
// Given a docId we an generate the collab url
|
||||||
|
if (editorOptions?.docId) {
|
||||||
|
editorOptions.url = `http://localhost:5000${editorOptions?.docId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const editor = withIOCollaboration(createEditor(), {
|
||||||
|
...options,
|
||||||
|
...editorOptions
|
||||||
|
})
|
||||||
|
|
||||||
const oldReceiveDocument = editor.receiveDocument
|
const oldReceiveDocument = editor.receiveDocument
|
||||||
const promise = new Promise<void>(resolve => {
|
const promise = new Promise<void>(resolve => {
|
||||||
@ -188,8 +196,8 @@ describe('automerge editor client tests', () => {
|
|||||||
editor2.destroy()
|
editor2.destroy()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('deep nested tree error', () => {
|
it('should not throw deep nested tree error', () => {
|
||||||
// Ready from our test json file for the deep tree error
|
// Read from our test json file for the deep tree error
|
||||||
// This allows us to easily reproduce real production errors
|
// This allows us to easily reproduce real production errors
|
||||||
// and create test cases that resolve those errors
|
// and create test cases that resolve those errors
|
||||||
const rawData = fs.readFileSync(
|
const rawData = fs.readFileSync(
|
||||||
@ -205,6 +213,26 @@ describe('automerge editor client tests', () => {
|
|||||||
toSlateOp(operations, currentDoc)
|
toSlateOp(operations, currentDoc)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should update children for a root level children operation', async () => {
|
||||||
|
const editor = await createCollabEditor()
|
||||||
|
|
||||||
|
const oldDoc = collabBackend.backend.documentSetMap[docId].getDoc(docId)
|
||||||
|
const newDoc = Automerge.change(oldDoc, changed => {
|
||||||
|
// @ts-ignore
|
||||||
|
changed.children = [
|
||||||
|
{ type: 'paragraph', children: [{ text: 'new' }] },
|
||||||
|
{ type: 'paragraph', children: [{ text: 'nodes' }] }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
collabBackend.backend.documentSetMap[docId].setDoc(docId, newDoc)
|
||||||
|
|
||||||
|
await waitForCondition(() => editor.children.length === 2)
|
||||||
|
|
||||||
|
expect(editor.children.length).toEqual(2)
|
||||||
|
expect(Node.string(editor.children[0])).toEqual('new')
|
||||||
|
expect(Node.string(editor.children[1])).toEqual('nodes')
|
||||||
|
})
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
collabBackend.destroy()
|
collabBackend.destroy()
|
||||||
server.close()
|
server.close()
|
||||||
|
@ -16,8 +16,16 @@ const useCursor = (
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
e.onCursor = (data: Cursors) => {
|
e.onCursor = (data: Cursors) => {
|
||||||
if (!mountedRef.current) return
|
if (!mountedRef.current) return
|
||||||
|
|
||||||
const ranges: Cursor[] = []
|
const ranges: Cursor[] = []
|
||||||
|
|
||||||
|
// If the cursor data is null or undefined, unset all active cursors
|
||||||
|
if (!data) {
|
||||||
|
setCursorData(ranges)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
const cursors = toJS(data)
|
const cursors = toJS(data)
|
||||||
|
|
||||||
for (let cursor in cursors) {
|
for (let cursor in cursors) {
|
||||||
@ -30,6 +38,13 @@ const useCursor = (
|
|||||||
if (mountedRef.current) {
|
if (mountedRef.current) {
|
||||||
setCursorData(ranges)
|
setCursorData(ranges)
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
e.handleError(err, {
|
||||||
|
type: 'onCursor',
|
||||||
|
data,
|
||||||
|
ranges
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user