mirror of
https://github.com/cudr/slate-collaborative.git
synced 2024-10-27 20:34:06 +00:00
feat: add ability to reset docSet for client on reconnect (resetOnReconnect)
This commit is contained in:
parent
581d68b142
commit
d0a7930484
@ -254,8 +254,6 @@ export default class AutomergeCollaboration {
|
|||||||
opCount: collabActions.length
|
opCount: collabActions.length
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(JSON.stringify(data.payload, null, 2))
|
|
||||||
|
|
||||||
this.onTrace(metaData, () => {
|
this.onTrace(metaData, () => {
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case 'operation':
|
case 'operation':
|
||||||
|
@ -4,9 +4,16 @@ import fs from 'fs'
|
|||||||
import isEqual from 'lodash/isEqual'
|
import isEqual from 'lodash/isEqual'
|
||||||
import { createEditor, Editor, Element, Node, Transforms } from 'slate'
|
import { createEditor, Editor, Element, Node, Transforms } from 'slate'
|
||||||
import { createDoc, SyncDoc, toJS, toSlateOp } from '@hiveteams/collab-bridge'
|
import { createDoc, SyncDoc, toJS, toSlateOp } from '@hiveteams/collab-bridge'
|
||||||
import AutomergeCollaboration from '@hiveteams/collab-backend/lib/AutomergeCollaboration'
|
import AutomergeCollaboration, {
|
||||||
|
IAutomergeMetaData
|
||||||
|
} from '@hiveteams/collab-backend/lib/AutomergeCollaboration'
|
||||||
import withIOCollaboration from './withIOCollaboration'
|
import withIOCollaboration from './withIOCollaboration'
|
||||||
import { AutomergeOptions, SocketIOPluginOptions } from './interfaces'
|
import {
|
||||||
|
AutomergeEditor,
|
||||||
|
AutomergeOptions,
|
||||||
|
SocketIOPluginOptions,
|
||||||
|
WithSocketIOEditor
|
||||||
|
} from './interfaces'
|
||||||
import { getTarget } from '@hiveteams/collab-bridge/src/path'
|
import { getTarget } from '@hiveteams/collab-bridge/src/path'
|
||||||
import getActiveConnections from '@hiveteams/collab-backend/src/utils/getActiveConnections'
|
import getActiveConnections from '@hiveteams/collab-backend/src/utils/getActiveConnections'
|
||||||
|
|
||||||
@ -41,12 +48,8 @@ const server = createServer(function(req, res) {
|
|||||||
res.end()
|
res.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
const defaultSlateJson = [
|
const defaultSlateJson = [{ type: 'paragraph', children: [{ text: '' }] }]
|
||||||
{
|
let operationTraces: IAutomergeMetaData[] = []
|
||||||
type: 'paragraph',
|
|
||||||
children: [{ text: 'hello world' }, { text: 'goodbye world' }]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const collabBackend = new AutomergeCollaboration({
|
const collabBackend = new AutomergeCollaboration({
|
||||||
entry: server,
|
entry: server,
|
||||||
defaultValue: defaultSlateJson,
|
defaultValue: defaultSlateJson,
|
||||||
@ -58,9 +61,7 @@ const collabBackend = new AutomergeCollaboration({
|
|||||||
return defaultSlateJson
|
return defaultSlateJson
|
||||||
},
|
},
|
||||||
onTrace(metaData, computationFn) {
|
onTrace(metaData, computationFn) {
|
||||||
if (metaData.opCount && metaData.opCount > 100) {
|
operationTraces.push(metaData)
|
||||||
}
|
|
||||||
console.log('metaData', metaData)
|
|
||||||
computationFn()
|
computationFn()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -72,7 +73,12 @@ describe('automerge editor client tests', () => {
|
|||||||
server.listen(5000, () => done())
|
server.listen(5000, () => done())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let collabEditors: (Editor & WithSocketIOEditor & AutomergeEditor)[] = []
|
||||||
afterEach(done => {
|
afterEach(done => {
|
||||||
|
operationTraces = []
|
||||||
|
collabEditors.forEach(editor => editor.destroy())
|
||||||
|
collabEditors = []
|
||||||
|
|
||||||
waitForCondition(() => !collabBackend.backend.getDocument(docId)).then(done)
|
waitForCondition(() => !collabBackend.backend.getDocument(docId)).then(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -98,6 +104,7 @@ describe('automerge editor client tests', () => {
|
|||||||
})
|
})
|
||||||
editor.connect()
|
editor.connect()
|
||||||
|
|
||||||
|
collabEditors.push(editor)
|
||||||
await promise
|
await promise
|
||||||
return editor
|
return editor
|
||||||
}
|
}
|
||||||
@ -117,8 +124,6 @@ describe('automerge editor client tests', () => {
|
|||||||
const serverDoc = toJS(collabBackend.backend.getDocument(docId))
|
const serverDoc = toJS(collabBackend.backend.getDocument(docId))
|
||||||
return serverDoc.children.length === 2
|
return serverDoc.children.length === 2
|
||||||
})
|
})
|
||||||
|
|
||||||
editor.destroy()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should sync updates across two clients', async () => {
|
it('should sync updates across two clients', async () => {
|
||||||
@ -131,9 +136,6 @@ describe('automerge editor client tests', () => {
|
|||||||
const serverDoc = toJS(collabBackend.backend.getDocument(docId))
|
const serverDoc = toJS(collabBackend.backend.getDocument(docId))
|
||||||
return serverDoc.children.length === 2 && editor2.children.length === 2
|
return serverDoc.children.length === 2 && editor2.children.length === 2
|
||||||
})
|
})
|
||||||
|
|
||||||
editor1.destroy()
|
|
||||||
editor2.destroy()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should sync offline changes on reconnect', async () => {
|
it('should sync offline changes on reconnect', async () => {
|
||||||
@ -159,9 +161,6 @@ describe('automerge editor client tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
expect(Node.string(editor2.children[2])).toEqual('offline')
|
expect(Node.string(editor2.children[2])).toEqual('offline')
|
||||||
|
|
||||||
editor1.destroy()
|
|
||||||
editor2.destroy()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should work with concurrent edits', async () => {
|
it('should work with concurrent edits', async () => {
|
||||||
@ -182,9 +181,6 @@ describe('automerge editor client tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
expect(isEqual(editor1.children, editor2.children)).toBeTruthy()
|
expect(isEqual(editor1.children, editor2.children)).toBeTruthy()
|
||||||
|
|
||||||
editor1.destroy()
|
|
||||||
editor2.destroy()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should work with concurrent insert text operations', async () => {
|
it('should work with concurrent insert text operations', async () => {
|
||||||
@ -208,9 +204,6 @@ describe('automerge editor client tests', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
expect(isEqual(editor1.children, editor2.children)).toBeTruthy()
|
expect(isEqual(editor1.children, editor2.children)).toBeTruthy()
|
||||||
|
|
||||||
editor1.destroy()
|
|
||||||
editor2.destroy()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not throw deep nested tree error', () => {
|
it('should not throw deep nested tree error', () => {
|
||||||
@ -248,8 +241,6 @@ describe('automerge editor client tests', () => {
|
|||||||
expect(editor.children.length).toEqual(2)
|
expect(editor.children.length).toEqual(2)
|
||||||
expect(Node.string(editor.children[0])).toEqual('new')
|
expect(Node.string(editor.children[0])).toEqual('new')
|
||||||
expect(Node.string(editor.children[1])).toEqual('nodes')
|
expect(Node.string(editor.children[1])).toEqual('nodes')
|
||||||
|
|
||||||
editor.destroy()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('set node for children with missing value should not throw error', () => {
|
it('set node for children with missing value should not throw error', () => {
|
||||||
@ -283,8 +274,8 @@ describe('automerge editor client tests', () => {
|
|||||||
expect(target).toEqual(null)
|
expect(target).toEqual(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
it.only('should work with concurrent insert text operations', async () => {
|
it('should reconnect with no opCount', async () => {
|
||||||
const editor1 = await createCollabEditor()
|
const editor1 = await createCollabEditor({ resetOnReconnect: true })
|
||||||
console.log('----\neditor1 disconnect\n-----')
|
console.log('----\neditor1 disconnect\n-----')
|
||||||
await waitForCondition(() => {
|
await waitForCondition(() => {
|
||||||
return getActiveConnections(collabBackend.backend, docId) === 1
|
return getActiveConnections(collabBackend.backend, docId) === 1
|
||||||
@ -303,14 +294,17 @@ describe('automerge editor client tests', () => {
|
|||||||
() => getActiveConnections(collabBackend.backend, docId) === 1
|
() => getActiveConnections(collabBackend.backend, docId) === 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Wait for a few seconds to allow the client and server to synchronize their
|
||||||
|
// document states
|
||||||
await new Promise(res => setTimeout(res, 3000))
|
await new Promise(res => setTimeout(res, 3000))
|
||||||
|
|
||||||
console.log('destroying last editor')
|
// Expect that reconnecting with resetOnReconnect option set to true
|
||||||
editor1.destroy()
|
// does not result in any operations being sent from the client to the server
|
||||||
|
expect(
|
||||||
await waitForCondition(() => {
|
operationTraces.some(
|
||||||
return getActiveConnections(collabBackend.backend, docId) === 0
|
trace => trace.opCount !== undefined && trace.opCount > 0
|
||||||
})
|
)
|
||||||
|
).toBeFalsy
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
|
@ -38,6 +38,7 @@ export interface SocketIOPluginOptions {
|
|||||||
onConnect?: () => void
|
onConnect?: () => void
|
||||||
onDisconnect?: () => void
|
onDisconnect?: () => void
|
||||||
onError?: (msg: string | Error, data: any) => void
|
onError?: (msg: string | Error, data: any) => void
|
||||||
|
resetOnReconnect?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WithSocketIOEditor {
|
export interface WithSocketIOEditor {
|
||||||
|
@ -17,7 +17,14 @@ const withSocketIO = <T extends AutomergeEditor>(
|
|||||||
slateEditor: T,
|
slateEditor: T,
|
||||||
options: SocketIOPluginOptions & AutomergeOptions
|
options: SocketIOPluginOptions & AutomergeOptions
|
||||||
) => {
|
) => {
|
||||||
const { onConnect, onDisconnect, connectOpts, url } = options
|
const {
|
||||||
|
onConnect,
|
||||||
|
onDisconnect,
|
||||||
|
connectOpts,
|
||||||
|
url,
|
||||||
|
docId,
|
||||||
|
resetOnReconnect
|
||||||
|
} = options
|
||||||
const editor = slateEditor as T & WithSocketIOEditor & AutomergeEditor
|
const editor = slateEditor as T & WithSocketIOEditor & AutomergeEditor
|
||||||
let socket: SocketIOClient.Socket
|
let socket: SocketIOClient.Socket
|
||||||
|
|
||||||
@ -31,6 +38,17 @@ const withSocketIO = <T extends AutomergeEditor>(
|
|||||||
// On socket io connect, open a new automerge connection
|
// On socket io connect, open a new automerge connection
|
||||||
socket.on('connect', () => {
|
socket.on('connect', () => {
|
||||||
editor.clientId = socket.id
|
editor.clientId = socket.id
|
||||||
|
|
||||||
|
// If the resetOnReconnect option is true we should close our connection
|
||||||
|
// and remove our document from the docSet if the user has already received
|
||||||
|
// a document from our collab server
|
||||||
|
if (resetOnReconnect && editor.docSet.getDoc(docId)) {
|
||||||
|
if (editor.connection) {
|
||||||
|
editor.connection.close()
|
||||||
|
}
|
||||||
|
editor.docSet.removeDoc(docId)
|
||||||
|
}
|
||||||
|
|
||||||
editor.openConnection()
|
editor.openConnection()
|
||||||
onConnect && onConnect()
|
onConnect && onConnect()
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user