You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cudr_slate-collaborative/packages/client/src/Connection.ts

209 lines
4.5 KiB

5 years ago
import Automerge from 'automerge'
import Immutable from 'immutable'
import io from 'socket.io-client'
import { Value, Operation } from 'slate'
import { ConnectionModel, ExtendedEditor } from './model'
5 years ago
import {
setCursor,
removeCursor,
cursorOpFilter,
applySlateOps,
toSlateOp,
toJS
} from '@slate-collaborative/bridge'
5 years ago
class Connection {
url: string
docId: string
docSet: Automerge.DocSet<any>
connection: Automerge.Connection<any>
socket: SocketIOClient.Socket
editor: ExtendedEditor
5 years ago
connectOpts: any
selection: any
5 years ago
onConnect?: () => void
onDisconnect?: () => void
constructor({
editor,
url,
connectOpts,
onConnect,
onDisconnect
}: ConnectionModel) {
this.url = url
this.editor = editor
this.connectOpts = connectOpts
this.onConnect = onConnect
this.onDisconnect = onDisconnect
this.docId = connectOpts.path || new URL(url).pathname
this.docSet = new Automerge.DocSet()
this.connect()
}
sendData = (data: any) => {
this.socket.emit('operation', data)
}
recieveData = async (data: any) => {
if (this.docId !== data.docId || !this.connection) {
return
}
const currentDoc = this.docSet.getDoc(this.docId)
const docNew = this.connection.receiveMsg(data)
console.log('current doc before updates', toJS(currentDoc))
console.log('new doc with remote updates!!', toJS(docNew))
5 years ago
if (!docNew) {
return
}
try {
const operations = Automerge.diff(currentDoc, docNew)
if (operations.length !== 0) {
const slateOps = toSlateOp(operations, currentDoc)
5 years ago
this.editor.remote = true
this.editor.withoutSaving(() => {
slateOps.forEach(o => {
this.editor.applyOperation(o)
})
5 years ago
})
await Promise.resolve()
this.editor.remote = false
this.setCursors(docNew.cursors)
5 years ago
}
} catch (e) {
console.error(e)
}
}
setCursors = cursors => {
if (!cursors) return
// const {
// value: { annotations }
// } = this.editor
// const keyMap = {}
// console.log('cursors', cursors)
// this.editor.withoutSaving(() => {
// annotations.forEach(anno => {
// this.editor.removeAnnotation(anno)
// })
// Object.keys(cursors).forEach(key => {
// if (key !== this.socket.id && !keyMap[key]) {
// this.editor.addAnnotation(toJS(cursors[key]))
// }
// })
// })
console.log(
'!!!!VAL',
this.connectOpts.query.name,
this.editor.value.toJSON({ preserveAnnotations: true })
)
}
5 years ago
receiveSlateOps = (operations: Immutable.List<Operation>) => {
const doc = this.docSet.getDoc(this.docId)
const message = `change from ${this.socket.id}`
if (!doc) return
const {
value: { selection }
} = this.editor
const annotationType = 'collaborative_selection'
const cursorData = {
id: this.socket.id,
selection,
// selectionOps: operations.filter(op => op.type === 'set_selection'),
annotationType
}
const withCursor = selection.isFocused ? setCursor : removeCursor
5 years ago
const changed = Automerge.change(doc, message, (d: any) =>
withCursor(
applySlateOps(d, cursorOpFilter(operations, annotationType)),
cursorData
)
5 years ago
)
console.log('doc with annotations!!', toJS(changed))
5 years ago
this.docSet.setDoc(this.docId, changed)
}
recieveDocument = data => {
const currentDoc = this.docSet.getDoc(this.docId)
if (!currentDoc) {
const doc = Automerge.load(data)
this.docSet.removeDoc(this.docId)
this.docSet.setDoc(this.docId, doc)
this.editor.controller.setValue(Value.fromJSON(toJS(doc)))
}
this.editor.setFocus()
this.connection.open()
this.onConnect && setTimeout(this.onConnect, 0)
}
connect = () => {
this.socket = io(this.url, this.connectOpts)
this.socket.on('connect', () => {
this.connection = new Automerge.Connection(this.docSet, this.sendData)
this.socket.on('document', this.recieveDocument)
this.socket.on('operation', this.recieveData)
this.socket.on('disconnect', this.disconnect)
})
}
disconnect = () => {
this.onDisconnect()
this.connection && this.connection.close()
delete this.connection
this.socket.removeListener('document')
this.socket.removeListener('operation')
}
close = () => {
this.onDisconnect()
this.socket.close()
}
}
export default Connection