Skip to content

Commit

Permalink
Merge pull request #6 from humandx/rev/slate-0.34-update
Browse files Browse the repository at this point in the history
Update bridge for Slate 0.34
  • Loading branch information
vshia authored Jun 15, 2018
2 parents 18b8e0d + 994ba86 commit dbb5c18
Show file tree
Hide file tree
Showing 11 changed files with 396 additions and 386 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ The "API" for the clients to call to send Automerge operations.
...
}
```
and
and
```
componentDidMount = () => {
this.connection.open()
Expand Down Expand Up @@ -181,7 +181,7 @@ This receives a message from the server regarding new remote changes and applies
this.setState({ value: Value.fromJSON(newJson) })
}
```
This is the failure handler when the Automerge -> Slate conversion fails.
This is the failure handler when the Automerge -> Slate conversion fails.

- When sending a change:
```
Expand All @@ -205,6 +205,7 @@ This converts the Slate operation to Automerge operations, applies it to the cli
3) If a new client joins, do they have to initialize the entire Automerge document (with the history)? Or can they just start from the latest snapshot?
4) What's a good way to batch changes from a client? To reduce network traffic, it would be nice to batch keystrokes within a second of each other together.
5) How should we send over information (such as cursor location) which we don't want to persist?
6) Currently does not include support for marks (especially with the Slate 0.34 update)

## Original README below

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"react": "16.0.0",
"react-dom": "16.0.0",
"react-scripts": "1.0.14",
"slate": "0.33.6",
"slate": "0.34.0",
"slate-react": "0.12.4",
"slate-edit-list": "0.11.3"
},
Expand Down
10 changes: 5 additions & 5 deletions src/example/App.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Client } from "./client"
import { initialValue } from "../utils/initialSlateValue"
import { slateCustomToJson } from "../libs/slateAutomergeBridge"
import { Value } from 'slate'
import Automerge from 'automerge'
import React from 'react'
import './App.css';
import { Value } from "slate"
import Automerge from "automerge"
import React from "react"
import "./App.css";

const docId = 1;
let doc = Automerge.init();
const initialSlateValue = Value.fromJSON(initialValue);
doc = Automerge.change(doc, 'Initialize Slate state', doc => {
doc = Automerge.change(doc, "Initialize Slate state", doc => {
doc.note = slateCustomToJson(initialSlateValue.document);
})
// const savedAutomergeDoc = Automerge.save(doc);
Expand Down
30 changes: 15 additions & 15 deletions src/example/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
*/

import { applyAutomergeOperations, applySlateOperations, automergeJsonToSlate } from "../libs/slateAutomergeBridge"
import { Editor } from 'slate-react'
import { Value } from 'slate'
import Automerge from 'automerge'
import EditList from 'slate-edit-list'
import { Editor } from "slate-react"
import { Value } from "slate"
import Automerge from "automerge"
import EditList from "slate-edit-list"
import Immutable from "immutable";
import React from 'react'
import './client.css';
import React from "react"
import "./client.css";


const plugin = EditList();
Expand All @@ -22,25 +22,25 @@ function renderNode(props) {
.contains(node);

switch (node.type) {
case 'ul_list':
case "ul_list":
return <ul {...attributes}>{children}</ul>;
case 'ol_list':
case "ol_list":
return <ol {...attributes}>{children}</ol>;

case 'list_item':
case "list_item":
return (
<li
className={isCurrentItem ? 'current-item' : ''}
title={isCurrentItem ? 'Current Item' : ''}
className={isCurrentItem ? "current-item" : ""}
title={isCurrentItem ? "Current Item" : ""}
{...props.attributes}
>
{props.children}
</li>
);

case 'paragraph':
case "paragraph":
return <div {...attributes}>{children}</div>;
case 'heading':
case "heading":
return <h1 {...attributes}>{children}</h1>;
default:
return <div {...attributes}>{children}</div>;
Expand Down Expand Up @@ -164,7 +164,7 @@ export class Client extends React.Component {
if (isOnline) {
this.props.connectionHandler(this.props.clientId, true)
this.connection.open()
let clock = this.docSet.getDoc(this.props.docId)._state.getIn(['opSet', 'clock']);
let clock = this.docSet.getDoc(this.props.docId)._state.getIn(["opSet", "clock"]);
this.props.sendMessage(this.props.clientId, {
clock: clock,
docId: this.props.docId,
Expand Down Expand Up @@ -217,7 +217,7 @@ export class Client extends React.Component {
*/
renderInternalClock = () => {
try {
let clockList = this.docSet.getDoc(this.props.docId)._state.getIn(['opSet', 'clock']);
let clockList = this.docSet.getDoc(this.props.docId)._state.getIn(["opSet", "clock"]);
let clockComponents = [];
clockList.forEach((value, actorId) => {
actorId = actorId.substr(0, actorId.indexOf("-"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export const applyAutomergeOperations = (opSetDiff, change, failureCallback) =>
} catch (e) {
// If an error occurs, release the Slate Value based on the Automerge
// document, which is the ground truth.
console.debug("The following warning is fine:")
console.debug(e)
console.info("The following warning is fine:")
console.info(e)
if (failureCallback) {
failureCallback();
}
Expand All @@ -35,7 +35,7 @@ export const applyAutomergeOperations = (opSetDiff, change, failureCallback) =>
* @function convertAutomergeToSlateOps
* @desc Converts Automerge operations to Slate operations.
* @param {Array} automergeOps - a list of Automerge operations created from Automerge.diff
* @return {Array} List of Slate operations
* @return {Array} Array of Slate operations
*/
export const convertAutomergeToSlateOps = (automergeOps) => {
// To build objects from Automerge operations
Expand All @@ -53,9 +53,7 @@ export const convertAutomergeToSlateOps = (automergeOps) => {
case "remove":
temp = automergeOpRemove(op, objIdMap);
objIdMap = temp.objIdMap;
if (temp.slateOp) {
slateOps[idx] = [temp.slateOp]
}
slateOps[idx] = temp.slateOps
break;
case "set":
objIdMap = automergeOpSet(op, objIdMap);
Expand All @@ -64,6 +62,7 @@ export const convertAutomergeToSlateOps = (automergeOps) => {
temp = automergeOpInsert(op, objIdMap);
objIdMap = temp.objIdMap;
deferredOps[idx] = temp.deferredOps;
slateOps[idx] = temp.slateOps;
if (temp.deferredOps && !containsDeferredOps) {
containsDeferredOps = true;
}
Expand All @@ -90,14 +89,14 @@ export const convertAutomergeToSlateOps = (automergeOps) => {
*/
const automergeOpCreate = (op, objIdMap) => {
switch (op.type) {
case 'map':
case "map":
objIdMap[op.obj] = {}
break;
case 'list':
case "list":
objIdMap[op.obj] = []
break;
default:
console.error('`create`, unsupported type: ', op.type)
console.error("`create`, unsupported type: ", op.type)
}
return objIdMap;
}
Expand All @@ -107,10 +106,11 @@ const automergeOpCreate = (op, objIdMap) => {
* @desc Handles the `remove` Automerge operation
* @param {Object} op - Automerge operation
* @param {Object} objIdMap - Map from the objectId to created object
* @return {Object} The objIdMap and corresponding Slate Operations for this operation
* @return {Object} The objIdMap and array of corresponding Slate Operations for
* this operation
*/
const automergeOpRemove = (op, objIdMap) => {
let slatePath, slateOp
let nodePath, slateOps
let pathString = op.path.slice(1).join("/")
const lastObjectPath = op.path[op.path.length - 1];
pathString = pathString.match(/\d+/g)
Expand All @@ -119,41 +119,38 @@ const automergeOpRemove = (op, objIdMap) => {
objIdMap[op.obj].splice(op.index, 1)
} else {
switch (lastObjectPath) {
case 'characters':
case "text":
// Remove a character
if (pathString) {
slatePath = pathString.map(x => { return parseInt(x, 10); });
} else {
slatePath = [op.index]
}
nodePath = pathString.map(x => { return parseInt(x, 10); })
nodePath = nodePath.splice(0, nodePath.length - 1)

slateOp = {
type: 'remove_text',
path: slatePath,
slateOps = [{
type: "remove_text",
path: nodePath,
offset: op.index,
text: '*',
text: "*",
marks: []
}
}]
break;
case 'nodes':
case "nodes":
// Remove a node
if (pathString) {
slatePath = pathString.map(x => { return parseInt(x, 10); });
slatePath = [...slatePath, op.index];
nodePath = pathString.map(x => { return parseInt(x, 10); });
nodePath = [...nodePath, op.index];
} else {
slatePath = [op.index]
nodePath = [op.index]
}

slateOp = {
type: 'remove_node',
path: slatePath,
}
slateOps = [{
type: "remove_node",
path: nodePath,
}]
break;
default:
console.error('`remove`, unsupported node type:', lastObjectPath)
console.error("`remove`, unsupported node type:", lastObjectPath)
}
}
return { objIdMap: objIdMap, slateOp: slateOp };
return { objIdMap: objIdMap, slateOps: slateOps };
}


Expand All @@ -165,7 +162,7 @@ const automergeOpRemove = (op, objIdMap) => {
* @return {Object} Map from Object Id to Object
*/
const automergeOpSet = (op, objIdMap) => {
if (op.hasOwnProperty('link')) {
if (op.hasOwnProperty("link")) {
// What's the point of the `link` field? All my experiments
// have `link` = true
if (op.link) {
Expand All @@ -175,7 +172,7 @@ const automergeOpSet = (op, objIdMap) => {
objIdMap[op.obj][op.key] = objIdMap[op.value]
} else {
// TODO: Does this ever happen?
console.error('`set`, unable to find objectId: ', op.value)
console.error("`set`, unable to find objectId: ", op.value)
}
}
} else {
Expand All @@ -189,23 +186,44 @@ const automergeOpSet = (op, objIdMap) => {
* @desc Handles the `insert` Automerge operation
* @param {Object} op - Automerge operation
* @param {Object} objIdMap - Map from the objectId to created object
* @return {Object} Containing the map from Object Id to Object and deferred operation
* @return {Object} Containing the map from Object Id to Object,
* deferred operation, and array of Slate operations.
*/
const automergeOpInsert = (op, objIdMap) => {
if (op.link) {
// Check if inserting into a newly created object or one that
// already exists in our Automerge document
// already exists in our Automerge document.
if (objIdMap.hasOwnProperty(op.obj)) {
objIdMap[op.obj].splice(op.index, 0, objIdMap[op.value])
} else {
return { objIdMap: objIdMap, deferredOps: op }
}
}
else {
// TODO: Does this ever happen?
console.log('op.action is `insert`, but link is false')
// If adding in a primitive to a list, then op.link is False.
// This is used when adding in a character to the text field of a Leaf
// node.
if (objIdMap.hasOwnProperty(op.obj)) {
objIdMap[op.obj].splice(op.index, 0, op.value)
} else {
let pathString = op.path.slice(1).join("/")
pathString = pathString.match(/\d+/g)
let nodePath = pathString.map(x => {
return parseInt(x, 10);
});
nodePath = nodePath.splice(0, nodePath.length - 1)

const slateOp = {
type: "insert_text",
path: nodePath,
offset: op.index,
text: op.value,
marks: []
}
return { objIdMap: objIdMap, deferredOps: null, slateOps: [slateOp] }
}
}
return { objIdMap: objIdMap, deferredOps: null };
return { objIdMap: objIdMap }
}

/**
Expand All @@ -220,21 +238,21 @@ const automergeOpInsertText = (deferredOps, objIdMap, slateOps) => {
// We know all ops in this list have the following conditions true:
// - op.action === `insert`
// - pathMap.hasOwnProperty(op.obj)
// - typeof pathMap[op.obj] === 'string' ||
// - typeof pathMap[op.obj] === "string" ||
// pathMap[op.obj] instanceof String
deferredOps.forEach((op, idx) => {
if (op === undefined || op === null) return;

const insertInto = op.path.slice(1).join("/")
let pathString, slatePath
let pathString, nodePath
let slateOp = []

if (insertInto === "nodes") {
// If inserting into the "root" of the tree, the slatePath is []
slatePath = []
// If inserting into the "root" of the tree, the nodePath is []
nodePath = []
} else {
pathString = insertInto.match(/\d+/g)
slatePath = pathString.map(x => {
nodePath = pathString.map(x => {
return parseInt(x, 10);
});
}
Expand All @@ -244,19 +262,19 @@ const automergeOpInsertText = (deferredOps, objIdMap, slateOps) => {
switch (nodeToAdd.object) {
case "character":
slateOp.push({
type: 'insert_text',
path: slatePath,
type: "insert_text",
path: nodePath,
offset: op.index,
text: objIdMap[op.value].text,
marks: objIdMap[op.value].marks
})
break;
case "block":
const newNode = automergeJsonToSlate(nodeToAdd);
slatePath.push(op.index)
nodePath.push(op.index)
slateOp.push({
type: "insert_node",
path: slatePath,
path: nodePath,
node: newNode,
})
break;
Expand All @@ -278,7 +296,7 @@ const automergeOpInsertText = (deferredOps, objIdMap, slateOps) => {
const flattenArray = (array_of_lists) => {
let newList = []
array_of_lists.forEach((items) => {
if (items !== null) {
if (items !== null && items !== undefined) {
items.forEach((item) => { newList.push(item) })
}
});
Expand Down
Loading

0 comments on commit dbb5c18

Please sign in to comment.