diff --git a/packages/backend/src/Connection.ts b/packages/backend/src/Connection.ts index 3e88218..3b6b8e2 100644 --- a/packages/backend/src/Connection.ts +++ b/packages/backend/src/Connection.ts @@ -40,10 +40,18 @@ class Connection { } private saveDoc = throttle(pathname => { - if (this.options.onDocumentSave) { - const doc = this.docSet.getDoc(pathname) + try { + if (this.options.onDocumentSave) { + const doc = this.docSet.getDoc(pathname) - this.options.onDocumentSave(pathname, toJS(doc)) + const data = toJS(doc) + + delete data.cursors + + this.options.onDocumentSave(pathname, data) + } + } catch (e) { + console.log(e) } }, (this.options && this.options.saveTreshold) || 2000) diff --git a/packages/bridge/src/apply/annotation.ts b/packages/bridge/src/apply/annotation.ts index 8650aed..e58bb85 100644 --- a/packages/bridge/src/apply/annotation.ts +++ b/packages/bridge/src/apply/annotation.ts @@ -1,17 +1,17 @@ import { Operation, SyncDoc } from '../model' export const addAnnotation = (doc: SyncDoc, op: Operation): SyncDoc => { - console.log('addAnnotation!!!', op) + console.log('addAnnotation!!!', op.toJS()) return doc } export const removeAnnotation = (doc: SyncDoc, op: Operation): SyncDoc => { - console.log('removeAnnotation!!!', op) + console.log('removeAnnotation!!!', op.toJS()) return doc } export const setAnnotation = (doc: SyncDoc, op: Operation): SyncDoc => { - console.log('setAnnotation!!!', op) + console.log('setAnnotation!!!', op.toJS()) return doc } diff --git a/packages/bridge/src/apply/index.ts b/packages/bridge/src/apply/index.ts index 1a19ae1..fdd3983 100644 --- a/packages/bridge/src/apply/index.ts +++ b/packages/bridge/src/apply/index.ts @@ -3,43 +3,19 @@ import { Operation, Operations, SyncDoc } from '../model' import node from './node' import mark from './mark' import text from './text' +import selection from './selection' import annotation from './annotation' -const setSelection = (doc, op, { id, selection }) => { - console.log('selection', selection.toJSON()) - if (!doc.cursors) { - doc.cursors = {} - } - - // console.log('setSelection', op.toJSON(), id) - const operation = op.toJSON() - - if (!doc.cursors[id]) { - doc.cursors[id] = { - key: id, - type: 'collaborative_selection' - } - } - - const cursor = doc.cursors[id] - const { focus, anchor } = operation.newProperties - - if (focus) cursor.focus = focus - if (anchor) cursor.anchor = anchor - - return doc -} - -const setValue = (doc, op) => doc +const setSelection = doc => doc +const setValue = doc => doc const opType: any = { ...text, ...annotation, ...node, ...mark, - - set_selection: setSelection, - set_value: setValue + ...selection + // set_value: setValue } export const applyOperation = meta => ( @@ -49,7 +25,7 @@ export const applyOperation = meta => ( try { const applyOp = opType[op.type] - if (!applyOp) throw new TypeError('Invalid operation type!') + if (!applyOp) throw new TypeError('Unsupported operation type!') return applyOp(doc, op, meta) } catch (e) { diff --git a/packages/bridge/src/apply/selection.ts b/packages/bridge/src/apply/selection.ts new file mode 100644 index 0000000..82e2359 --- /dev/null +++ b/packages/bridge/src/apply/selection.ts @@ -0,0 +1,35 @@ +import { toJS } from '../utils' + +const setSelection = (doc, op, { id, selection, annotationType }) => { + if (!doc.cursors) { + doc.cursors = {} + } + + const operation = op.toJS() + + if (!doc.cursors[id]) { + doc.cursors[id] = { + key: id, + type: annotationType, + data: {} + } + } + + const cursor = doc.cursors[id] + const { focus, anchor } = operation.newProperties + + if (focus) cursor.focus = focus + if (anchor) cursor.anchor = anchor + + const cursorPath = (anchor && anchor.path) || (focus && focus.path) + + if (cursorPath) cursor.data.cursorPath = toJS(cursorPath) + + cursor.data.isBackward = selection.isBackward + + return doc +} + +export default { + set_selection: setSelection +} diff --git a/packages/client/src/Connection.ts b/packages/client/src/Connection.ts index a9374d7..427efba 100644 --- a/packages/client/src/Connection.ts +++ b/packages/client/src/Connection.ts @@ -87,29 +87,23 @@ class Connection { setCursors = cursors => { if (!cursors) return - // console.log('setCursors', cursors) + const { value: { annotations } } = this.editor const keyMap = {} + console.log('cursors', cursors) + this.editor.withoutSaving(() => { annotations.forEach(anno => { - // if (cursors[anno.key]) { - // console.log('set cursor', anno, ) - // this.editor.setAnnotation(anno, cursors[anno.key]) - // keyMap[anno.key] = true - // } else { - // this.editor.removeAnnotation(anno) - // } - this.editor.removeAnnotation(anno) }) Object.keys(cursors).forEach(key => { if (key !== this.socket.id && !keyMap[key]) { - this.editor.addAnnotation(cursors[key]) + this.editor.addAnnotation(toJS(cursors[key])) } }) }) @@ -121,11 +115,42 @@ class Connection { if (!doc) return + const selectionOps = operations.filter(op => op.type === 'set_selection') + + console.log('hasSelectionOps', selectionOps.size) + + const { value } = this.editor + + const { selection } = value + + const meta = { + id: this.socket.id, + selection, + annotationType: 'collaborative_selection' + } + + const cursor = doc.cursors[meta.id] + const cursorOffset = cursor && cursor.anchor && cursor.anchor.offset + + if (!selectionOps.size && selection.start.offset !== cursorOffset) { + const opData = { + type: 'set_selection', + properties: {}, + newProperties: { + anchor: selection.start, + focus: selection.end + } + } + + const op = Operation.fromJSON(opData) + + operations = operations.push(op) + } + + console.log('operations', operations.toJSON()) + const changed = Automerge.change(doc, message, (d: any) => - applySlateOps(d, operations, { - id: this.socket.id, - selection: this.editor.value.selection - }) + applySlateOps(d, operations, meta) ) this.docSet.setDoc(this.docId, changed) diff --git a/packages/client/src/renderAnnotation.tsx b/packages/client/src/renderAnnotation.tsx index 3929ee0..ce7cf97 100644 --- a/packages/client/src/renderAnnotation.tsx +++ b/packages/client/src/renderAnnotation.tsx @@ -15,23 +15,30 @@ const cursorStyleBase = { } as any const renderAnnotation = (props, editor, next) => { - const { children, annotation, attributes } = props + const { children, annotation, attributes, node } = props - const isLeft = annotation.focus.offset >= annotation.anchor.offset + const isBackward = annotation.data.get('isBackward') + const cursorPath = annotation.data.get('cursorPath') - console.log('isLeft', isLeft) + const cursorStyles = { ...cursorStyleBase, left: isBackward ? '0%' : '100%' } - const cursorStyles = { ...cursorStyleBase, left: isLeft ? '0%' : '100%' } + const { document } = editor.value - console.log('renderAnnotation', annotation.toJSON()) + const targetNode = document.getNode(cursorPath) + + const isTarget = targetNode && targetNode.key === node.key + + const showCursor = isTarget switch (annotation.type) { case 'collaborative_selection': return ( - - {annotation.key} - + {showCursor ? ( + + {annotation.key} + + ) : null} {children} )