diff --git a/packages/backend/src/Connection.ts b/packages/backend/src/Connection.ts index 6a81e63..0940434 100644 --- a/packages/backend/src/Connection.ts +++ b/packages/backend/src/Connection.ts @@ -2,6 +2,7 @@ import io from 'socket.io' import { ValueJSON } from 'slate' import * as Automerge from 'automerge' import throttle from 'lodash/throttle' +import merge from 'lodash/merge' import { toSync, toJS } from '@slate-collaborative/bridge' @@ -18,7 +19,7 @@ class Connection { this.io = io(options.port, options.connectOpts) this.docSet = new Automerge.DocSet() this.connections = {} - this.options = options + this.options = merge(defaultOptions, options) this.configure() } @@ -32,16 +33,28 @@ class Connection { private appendDoc = (path: string, value: ValueJSON) => { const sync = toSync(value) + sync.annotations = {} + const doc = Automerge.from(sync) this.docSet.setDoc(path, doc) } 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)) + if (doc) { + const data = toJS(doc) + + delete data.annotations + + this.options.onDocumentSave(pathname, data) + } + } + } catch (e) { + console.log(e) } }, (this.options && this.options.saveTreshold) || 2000) @@ -96,6 +109,8 @@ class Connection { socket.on('operation', this.onOperation(id, name)) socket.on('disconnect', this.onDisconnect(id, socket)) + + this.garbageCursors(name) } private onOperation = (id, name) => data => { @@ -103,6 +118,8 @@ class Connection { this.connections[id].receiveMsg(data) this.saveDoc(name) + + this.garbageCursors(name) } catch (e) { console.log(e) } @@ -114,6 +131,9 @@ class Connection { socket.leave(id) + this.garbageCursor(socket.nsp.name, id) + this.garbageCursors(socket.nsp.name) + this.garbageNsp() } @@ -127,6 +147,35 @@ class Connection { }) } + garbageCursor = (nsp, id) => { + const doc = this.docSet.getDoc(nsp) + + if (!doc.annotations) return + + const change = Automerge.change(doc, `remove cursor ${id}`, (d: any) => { + delete d.annotations[id] + }) + + this.docSet.setDoc(nsp, change) + } + + garbageCursors = nsp => { + const doc = this.docSet.getDoc(nsp) + + if (!doc.annotations) return + + const namespace = this.io.of(nsp) + + Object.keys(doc.annotations).forEach(key => { + if ( + !namespace.sockets[key] && + doc.annotations[key].type === this.options.cursorAnnotationType + ) { + this.garbageCursor(nsp, key) + } + }) + } + removeDoc = async nsp => { const doc = this.docSet.getDoc(nsp) diff --git a/packages/backend/src/model.ts b/packages/backend/src/model.ts index 9f3ede9..4adcf09 100644 --- a/packages/backend/src/model.ts +++ b/packages/backend/src/model.ts @@ -5,6 +5,7 @@ export interface ConnectionOptions { connectOpts?: SocketIO.ServerOptions defaultValue?: ValueJSON saveTreshold?: number + cursorAnnotationType?: string onAuthRequest?: ( query: Object, socket?: SocketIO.Socket diff --git a/packages/backend/src/utils/index.ts b/packages/backend/src/utils/index.ts index d8567a8..5bcce0e 100644 --- a/packages/backend/src/utils/index.ts +++ b/packages/backend/src/utils/index.ts @@ -7,7 +7,8 @@ export const getClients = (io, nsp) => export const defaultOptions = { port: 9000, - saveTreshold: 2000 + saveTreshold: 2000, + cursorAnnotationType: 'collaborative_selection' } export { defaultValue } diff --git a/packages/bridge/src/apply/annotation.ts b/packages/bridge/src/apply/annotation.ts index 083b660..dd7326a 100644 --- a/packages/bridge/src/apply/annotation.ts +++ b/packages/bridge/src/apply/annotation.ts @@ -1,14 +1,17 @@ -import { Operation, SyncDoc } from '../model' +import { Operation, SyncDoc } from '../model/index' export const addAnnotation = (doc: SyncDoc, op: Operation): SyncDoc => { + console.log('addAnnotation!!!', op.toJS()) return doc } export const removeAnnotation = (doc: SyncDoc, op: Operation): SyncDoc => { + console.log('removeAnnotation!!!', op.toJS()) return doc } export const setAnnotation = (doc: SyncDoc, op: Operation): SyncDoc => { + console.log('setAnnotation!!!', op.toJS()) return doc } diff --git a/packages/bridge/src/apply/index.ts b/packages/bridge/src/apply/index.ts index 7af5423..2679830 100644 --- a/packages/bridge/src/apply/index.ts +++ b/packages/bridge/src/apply/index.ts @@ -6,23 +6,25 @@ import text from './text' import annotation from './annotation' const setSelection = doc => doc -const setValue = (doc, op) => doc +const setValue = doc => doc const opType: any = { ...text, ...annotation, ...node, ...mark, - - set_selection: setSelection, - set_value: setValue + set_selection: setSelection + // set_value: setValue } export const applyOperation = (doc: SyncDoc, op: Operation): SyncDoc => { try { const applyOp = opType[op.type] - if (!applyOp) throw new TypeError('Invalid operation type!') + if (!applyOp) { + console.log('operation', op.toJS()) + throw new TypeError(`Unsupported operation type: ${op.type}!`) + } return applyOp(doc, op) } catch (e) { diff --git a/packages/bridge/src/convert/index.ts b/packages/bridge/src/convert/index.ts index 7dc0321..782e14c 100644 --- a/packages/bridge/src/convert/index.ts +++ b/packages/bridge/src/convert/index.ts @@ -14,7 +14,7 @@ const byAction = { const rootKey = '00000000-0000-0000-0000-000000000000' -const toSlateOp = (ops: Automerge.Diff[], currentTree) => { +const toSlateOp = (ops: Automerge.Diff[], doc) => { const iterate = (acc, op) => { const action = byAction[op.action] @@ -30,7 +30,7 @@ const toSlateOp = (ops: Automerge.Diff[], currentTree) => { [] ]) - return defer.flatMap(op => op(tempTree, currentTree)) + return defer.flatMap(op => op(tempTree, doc)).filter(op => op) } export { toSlateOp } diff --git a/packages/bridge/src/convert/insert.ts b/packages/bridge/src/convert/insert.ts index fd2a508..2f923d8 100644 --- a/packages/bridge/src/convert/insert.ts +++ b/packages/bridge/src/convert/insert.ts @@ -15,18 +15,20 @@ const insertNodeOp = ({ value, obj, index, path }: Automerge.Diff) => map => { const inserate = ({ nodes, ...json }: any, path) => { const node = nodes ? { ...json, nodes: [] } : json - if (node.object === 'mark') { - ops.push({ - type: 'add_mark', - path: path.slice(0, -1), - mark: node - }) - } else { - ops.push({ - type: 'insert_node', - path, - node - }) + if (node.object) { + if (node.object === 'mark') { + ops.push({ + type: 'add_mark', + path: path.slice(0, -1), + mark: node + }) + } else { + ops.push({ + type: 'insert_node', + path, + node + }) + } } nodes && nodes.forEach((n, i) => inserate(n, [...path, i])) @@ -50,7 +52,7 @@ const opInsert = (op: Automerge.Diff, [map, ops]) => { if (link && map[obj]) { map[obj].splice(index, 0, map[value] || value) - } else if (type === 'text' && !path) { + } else if ((type === 'text' || type === 'list') && !path) { map[obj] = map[obj] ? map[obj] .slice(0, index) diff --git a/packages/bridge/src/convert/remove.ts b/packages/bridge/src/convert/remove.ts index a768cdf..5b89b27 100644 --- a/packages/bridge/src/convert/remove.ts +++ b/packages/bridge/src/convert/remove.ts @@ -40,10 +40,22 @@ const removeNodesOp = ({ index, obj, path }: Automerge.Diff) => (map, doc) => { } } +const removeAnnotationOp = ({ key }: Automerge.Diff) => (map, doc) => { + const annotation = toJS(doc.annotations[key]) + + if (annotation) { + return { + type: 'remove_annotation', + annotation + } + } +} + const removeByType = { text: removeTextOp, nodes: removeNodesOp, - marks: removeMarkOp + marks: removeMarkOp, + annotations: removeAnnotationOp } const opRemove = (op: Automerge.Diff, [map, ops]) => { diff --git a/packages/bridge/src/convert/set.ts b/packages/bridge/src/convert/set.ts index 8189597..8927b33 100644 --- a/packages/bridge/src/convert/set.ts +++ b/packages/bridge/src/convert/set.ts @@ -10,6 +10,32 @@ const setDataOp = ({ path, value }: Automerge.Diff) => map => ({ } }) +const AnnotationSetOp = ({ key, value }: Automerge.Diff) => (map, doc) => { + if (!doc.annotations) { + doc.annotations = {} + } + + let op + + /** + * Looks like set_annotation option is broken, temporary disabled + */ + // if (!doc.annotations[key]) { + op = { + type: 'add_annotation', + annotation: map[value] + } + // } else { + // op = { + // type: 'set_annotation', + // properties: toJS(doc.annotations[key]), + // newProperties: map[value] + // } + // } + + return op +} + const setByType = { data: setDataOp } @@ -21,10 +47,17 @@ const opSet = (op: Automerge.Diff, [map, ops]) => { if (set && path) { ops.push(set(op)) - } else { + } else if (map[obj]) { map[obj][key] = link ? map[value] : value } + /** + * Annotation + */ + if (path && path.length === 1 && path[0] === 'annotations') { + ops.push(AnnotationSetOp(op)) + } + return [map, ops] } catch (e) { console.error(e, op, toJS(map)) diff --git a/packages/bridge/src/cursor/index.ts b/packages/bridge/src/cursor/index.ts new file mode 100644 index 0000000..c8d7087 --- /dev/null +++ b/packages/bridge/src/cursor/index.ts @@ -0,0 +1,69 @@ +import { Operation, Selection } from 'slate' +import * as Immutable from 'immutable' +import merge from 'lodash/merge' + +import { toJS } from '../utils' +import { SyncDoc, CursorKey } from '../model' + +export const setCursor = ( + doc: SyncDoc, + key: CursorKey, + selection: Selection, + type, + data +) => { + if (!doc) return + + if (!doc.annotations) { + doc.annotations = {} + } + + if (!doc.annotations[key]) { + doc.annotations[key] = { + key, + type, + data: {} + } + } + + const annotation = toJS(doc.annotations[key]) + + annotation.focus = selection.end.toJSON() + annotation.anchor = selection.start.toJSON() + + annotation.data = merge(annotation.data, data, { + isBackward: selection.isBackward, + targetPath: selection.isBackward + ? annotation.anchor.path + : annotation.focus.path + }) + + doc.annotations[key] = annotation + + return doc +} + +export const removeCursor = (doc: SyncDoc, key: CursorKey) => { + if (doc.annotations && doc.annotations[key]) { + delete doc.annotations[key] + } + + return doc +} + +export const cursorOpFilter = (ops: Immutable.List, type: string) => + ops.filter(op => { + if (op.type === 'set_annotation') { + return !( + (op.properties && op.properties.type === type) || + (op.newProperties && op.newProperties.type === type) + ) + } else if ( + op.type === 'add_annotation' || + op.type === 'remove_annotation' + ) { + return op.annotation.type !== type + } + + return true + }) diff --git a/packages/bridge/src/index.ts b/packages/bridge/src/index.ts index 26d4a2e..8a6778a 100644 --- a/packages/bridge/src/index.ts +++ b/packages/bridge/src/index.ts @@ -1,3 +1,5 @@ export * from './apply' export * from './convert' export * from './utils' +export * from './cursor' +export * from './model' diff --git a/packages/bridge/src/model/automerge.ts b/packages/bridge/src/model/automerge.ts index dce0000..82f306a 100644 --- a/packages/bridge/src/model/automerge.ts +++ b/packages/bridge/src/model/automerge.ts @@ -1,3 +1,3 @@ -import { Doc } from 'automerge' +export type CursorKey = string -export type SyncDoc = Doc +export type SyncDoc = any diff --git a/packages/bridge/src/utils/index.ts b/packages/bridge/src/utils/index.ts index e33660e..3029458 100644 --- a/packages/bridge/src/utils/index.ts +++ b/packages/bridge/src/utils/index.ts @@ -1,7 +1,14 @@ import toSync from './toSync' import hexGen from './hexGen' -export const toJS = node => JSON.parse(JSON.stringify(node)) +export const toJS = node => { + try { + return JSON.parse(JSON.stringify(node)) + } catch (e) { + console.error('Convert to js failed!!! Return null') + return null + } +} export const cloneNode = node => toSync(toJS(node)) diff --git a/packages/client/src/Connection.ts b/packages/client/src/Connection.ts index 6971d33..503240a 100644 --- a/packages/client/src/Connection.ts +++ b/packages/client/src/Connection.ts @@ -3,9 +3,16 @@ import Immutable from 'immutable' import io from 'socket.io-client' import { Value, Operation } from 'slate' -import { ConnectionModel } from './model' +import { ConnectionModel, ExtendedEditor } from './model' -import { applySlateOps, toSlateOp, toJS } from '@slate-collaborative/bridge' +import { + setCursor, + removeCursor, + cursorOpFilter, + applySlateOps, + toSlateOp, + toJS +} from '@slate-collaborative/bridge' class Connection { url: string @@ -13,8 +20,10 @@ class Connection { docSet: Automerge.DocSet connection: Automerge.Connection socket: SocketIOClient.Socket - editor: any + editor: ExtendedEditor connectOpts: any + annotationDataMixin: any + cursorAnnotationType: string onConnect?: () => void onDisconnect?: () => void @@ -23,11 +32,16 @@ class Connection { url, connectOpts, onConnect, - onDisconnect + onDisconnect, + cursorAnnotationType, + annotationDataMixin }: ConnectionModel) { this.url = url this.editor = editor this.connectOpts = connectOpts + this.cursorAnnotationType = cursorAnnotationType + this.annotationDataMixin = annotationDataMixin + this.onConnect = onConnect this.onDisconnect = onDisconnect @@ -68,7 +82,9 @@ class Connection { }) }) - setTimeout(() => (this.editor.remote = false), 5) + await Promise.resolve() + + this.editor.remote = false } } catch (e) { console.error(e) @@ -81,8 +97,20 @@ class Connection { if (!doc) return + const { + value: { selection } + } = this.editor + + const withCursor = selection.isFocused ? setCursor : removeCursor + const changed = Automerge.change(doc, message, (d: any) => - applySlateOps(d, operations) + withCursor( + applySlateOps(d, cursorOpFilter(operations, this.cursorAnnotationType)), + this.socket.id, + selection, + this.cursorAnnotationType, + this.annotationDataMixin + ) ) this.docSet.setDoc(this.docId, changed) @@ -109,7 +137,7 @@ class Connection { } connect = () => { - this.socket = io(this.url, this.connectOpts) + this.socket = io(this.url, { ...this.connectOpts }) this.socket.on('connect', () => { this.connection = new Automerge.Connection(this.docSet, this.sendData) @@ -125,6 +153,8 @@ class Connection { disconnect = () => { this.onDisconnect() + console.log('disconnect', this.socket) + this.connection && this.connection.close() delete this.connection @@ -137,6 +167,7 @@ class Connection { this.onDisconnect() this.socket.close() + // this.socket.destroy() } } diff --git a/packages/client/src/Controller.tsx b/packages/client/src/Controller.tsx index da41219..17c2e40 100644 --- a/packages/client/src/Controller.tsx +++ b/packages/client/src/Controller.tsx @@ -4,7 +4,6 @@ import { KeyUtils } from 'slate' import { hexGen } from '@slate-collaborative/bridge' import Connection from './Connection' - import { ControllerProps } from './model' class Controller extends Component { @@ -15,7 +14,13 @@ class Controller extends Component { } componentDidMount() { - const { editor, url, connectOpts } = this.props + const { + editor, + url, + cursorAnnotationType, + annotationDataMixin, + connectOpts + } = this.props KeyUtils.setGenerator(() => hexGen()) @@ -23,6 +28,8 @@ class Controller extends Component { editor, url, connectOpts, + cursorAnnotationType, + annotationDataMixin, onConnect: this.onConnect, onDisconnect: this.onDisconnect }) @@ -37,10 +44,10 @@ class Controller extends Component { } render() { - const { children, preloader } = this.props + const { children, renderPreloader } = this.props const { preloading } = this.state - if (preloader && preloading) return preloader() + if (renderPreloader && preloading) return renderPreloader() return children } diff --git a/packages/client/src/Cursor.tsx b/packages/client/src/Cursor.tsx new file mode 100644 index 0000000..3af7c45 --- /dev/null +++ b/packages/client/src/Cursor.tsx @@ -0,0 +1,47 @@ +import React, { Fragment } from 'react' + +const cursorStyleBase = { + position: 'absolute', + top: -2, + pointerEvents: 'none', + userSelect: 'none', + transform: 'translateY(-100%)', + fontSize: 10, + color: 'white', + background: 'palevioletred', + whiteSpace: 'nowrap' +} as any + +const caretStyleBase = { + position: 'absolute', + top: 0, + pointerEvents: 'none', + userSelect: 'none', + height: '100%', + width: 2, + background: 'palevioletred' +} as any + +const Cursor = ({ color, isBackward, name }) => { + const cursorStyles = { + ...cursorStyleBase, + background: color, + left: isBackward ? '0%' : '100%' + } + const caretStyles = { + ...caretStyleBase, + background: color, + left: isBackward ? '0%' : '100%' + } + + return ( + + + {name} + + + + ) +} + +export default Cursor diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts deleted file mode 100644 index cd0dc30..0000000 --- a/packages/client/src/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ReactNode } from 'react' - -import onChange from './onChange' -import renderEditor from './renderEditor' - -import Connection from './Connection' - -export interface PluginOptions { - url?: string - connectOpts?: SocketIOClient.ConnectOpts - preloader?: () => ReactNode - onConnect?: (connection: Connection) => void - onDisconnect?: (connection: Connection) => void -} - -const defaultOpts = { - url: 'http://localhost:9000' -} - -const plugin = (opts: PluginOptions = {}) => { - const options = { ...defaultOpts, ...opts } - - return { - onChange: onChange(options), - renderEditor: renderEditor(options) - } -} - -export default plugin diff --git a/packages/client/src/index.tsx b/packages/client/src/index.tsx new file mode 100644 index 0000000..0623a68 --- /dev/null +++ b/packages/client/src/index.tsx @@ -0,0 +1,30 @@ +import onChange from './onChange' +import renderEditor from './renderEditor' +import renderAnnotation from './renderAnnotation' + +import renderCursor from './renderCursor' + +import { PluginOptions } from './model' + +export const defaultOpts = { + url: 'http://localhost:9000', + cursorAnnotationType: 'collaborative_selection', + renderCursor, + annotationDataMixin: { + name: 'an collaborator name', + color: 'palevioletred', + alphaColor: 'rgba(233, 30, 99, 0.2)' + } +} + +const plugin = (opts: PluginOptions = defaultOpts) => { + const options = { ...defaultOpts, ...opts } + + return { + onChange: onChange(options), + renderEditor: renderEditor(options), + renderAnnotation: renderAnnotation(options) + } +} + +export default plugin diff --git a/packages/client/src/model.ts b/packages/client/src/model.ts index cc284c1..ba98209 100644 --- a/packages/client/src/model.ts +++ b/packages/client/src/model.ts @@ -1,16 +1,28 @@ -import { Editor } from 'slate' -import { PluginOptions } from './index' +import { ReactNode } from 'react' +import { Editor, Controller, Value } from 'slate' + import Connection from './Connection' -export interface ConnectionModel extends PluginOptions { - editor: Editor - onConnect: () => void - onDisconnect: () => void +type Data = { + [key: string]: any +} + +interface FixedController extends Controller { + setValue: (value: Value) => void } export interface ExtendedEditor extends Editor { - remote: boolean - connection: Connection + remote?: boolean + connection?: Connection + controller: FixedController + setFocus: () => void +} + +export interface ConnectionModel extends PluginOptions { + editor: ExtendedEditor + cursorAnnotationType: string + onConnect: () => void + onDisconnect: () => void } export interface ControllerProps extends PluginOptions { @@ -18,3 +30,14 @@ export interface ControllerProps extends PluginOptions { url?: string connectOpts?: SocketIOClient.ConnectOpts } + +export interface PluginOptions { + url?: string + connectOpts?: SocketIOClient.ConnectOpts + cursorAnnotationType?: string + annotationDataMixin?: Data + renderPreloader?: () => ReactNode + renderCursor?: (data: Data) => ReactNode | any + onConnect?: (connection: Connection) => void + onDisconnect?: (connection: Connection) => void +} diff --git a/packages/client/src/onChange.ts b/packages/client/src/onChange.ts index a71a371..3632c16 100644 --- a/packages/client/src/onChange.ts +++ b/packages/client/src/onChange.ts @@ -1,7 +1,7 @@ import { ExtendedEditor } from './model' const onChange = opts => (editor: ExtendedEditor, next: () => void) => { - if (!editor.remote) { + if (editor.connection && !editor.remote) { const operations: any = editor.operations editor.connection.receiveSlateOps(operations) diff --git a/packages/client/src/renderAnnotation.tsx b/packages/client/src/renderAnnotation.tsx new file mode 100644 index 0000000..4114f0d --- /dev/null +++ b/packages/client/src/renderAnnotation.tsx @@ -0,0 +1,31 @@ +import React from 'react' + +const renderAnnotation = ({ cursorAnnotationType, renderCursor }) => ( + props, + editor, + next +) => { + const { children, annotation, attributes, node } = props + + if (annotation.type !== cursorAnnotationType) return next() + + const data = annotation.data.toJS() + + const { targetPath, alphaColor } = data + const { document } = editor.value + + const targetNode = document.getNode(targetPath) + const showCursor = targetNode && targetNode.key === node.key + + return ( + + {showCursor ? renderCursor(data) : null} + {children} + + ) +} + +export default renderAnnotation diff --git a/packages/client/src/renderCursor.tsx b/packages/client/src/renderCursor.tsx new file mode 100644 index 0000000..d70c4f4 --- /dev/null +++ b/packages/client/src/renderCursor.tsx @@ -0,0 +1,7 @@ +import React from 'react' + +import Cursor from './Cursor' + +const renderCursor = data => + +export default renderCursor diff --git a/packages/client/src/renderEditor.tsx b/packages/client/src/renderEditor.tsx index 6a51db3..7355c6c 100644 --- a/packages/client/src/renderEditor.tsx +++ b/packages/client/src/renderEditor.tsx @@ -1,7 +1,6 @@ import React from 'react' -import { PluginOptions } from './index' - +import { PluginOptions } from './model' import Controller from './Controller' const renderEditor = (opts: PluginOptions) => ( diff --git a/packages/example/package.json b/packages/example/package.json index bacc6d7..5a05cd2 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -16,6 +16,8 @@ "concurrently": "^4.1.2", "faker": "^4.1.0", "lodash": "^4.17.15", + "nodemon": "^1.19.2", + "randomcolor": "^0.5.4", "react": "^16.9.0", "react-dom": "^16.9.0", "react-scripts": "3.1.1", @@ -25,10 +27,9 @@ }, "scripts": { "start": "react-scripts start", + "build": "react-scripts build", "dev": "concurrently \"yarn start\" \"yarn serve\"", - "serve": "nodemon --watch ../backend/lib --inspect server.js", - "test": "react-scripts test", - "eject": "react-scripts eject" + "serve": "nodemon --watch ../backend/lib --inspect server.js" }, "eslintConfig": { "extends": "react-app" @@ -44,8 +45,5 @@ "last 1 firefox version", "last 1 safari version" ] - }, - "devDependencies": { - "nodemon": "^1.19.2" } } diff --git a/packages/example/src/App.tsx b/packages/example/src/App.tsx index c1fbd9f..64d00e1 100644 --- a/packages/example/src/App.tsx +++ b/packages/example/src/App.tsx @@ -19,9 +19,11 @@ class App extends Component<{}, { rooms: string[] }> { return ( - - Add Room - + + + Add Room + + {rooms.map(room => ( ))} @@ -46,6 +48,10 @@ export default App const Container = styled.div`` +const Panel = styled.div` + display: flex; +` + const Button = styled.button` padding: 6px 14px; display: block; diff --git a/packages/example/src/Client.tsx b/packages/example/src/Client.tsx index 359ced2..21eaea2 100644 --- a/packages/example/src/Client.tsx +++ b/packages/example/src/Client.tsx @@ -2,6 +2,7 @@ import React, { Component } from 'react' import { Value, ValueJSON } from 'slate' import { Editor } from 'slate-react' +import randomColor from 'randomcolor' import styled from '@emotion/styled' @@ -23,12 +24,18 @@ class Client extends Component { state = { value: Value.fromJSON(defaultValue as ValueJSON), - isOnline: true, + isOnline: false, plugins: [] } componentDidMount() { - const plugin = ClientPlugin({ + const color = randomColor({ + luminosity: 'dark', + format: 'rgba', + alpha: 1 + }) + + const options = { url: `http://localhost:9000/${this.props.slug}`, connectOpts: { query: { @@ -37,10 +44,17 @@ class Client extends Component { slug: this.props.slug } }, - // preloader: () =>
PRELOADER!!!!!!
, + annotationDataMixin: { + name: this.props.name, + color, + alphaColor: color.slice(0, -2) + '0.2)' + }, + // renderPreloader: () =>
PRELOADER!!!!!!
, onConnect: this.onConnect, onDisconnect: this.onDisconnect - }) + } + + const plugin = ClientPlugin(options) this.setState({ plugins: [plugin]