2019-10-06 20:56:27 +00:00
|
|
|
import * as Automerge from 'automerge'
|
2021-01-29 14:02:01 +00:00
|
|
|
import { Element, Operation } from 'slate'
|
2020-05-10 13:50:12 +00:00
|
|
|
|
|
|
|
import { toSlatePath, toJS } from '../utils'
|
2019-10-05 21:24:52 +00:00
|
|
|
import { getTarget } from '../path'
|
2020-10-31 15:34:02 +00:00
|
|
|
import { rootKey } from './constants'
|
2021-01-29 14:02:01 +00:00
|
|
|
import { CollabMap, CollabOperation } from '../model'
|
2019-10-05 08:44:49 +00:00
|
|
|
|
2021-01-29 14:02:01 +00:00
|
|
|
const removeTextOp = (op: Automerge.Diff) => (map: CollabMap, doc: Element) => {
|
2020-07-01 21:56:48 +00:00
|
|
|
const { index, path, obj } = op
|
|
|
|
|
|
|
|
const slatePath = toSlatePath(path).slice(0, path?.length)
|
|
|
|
|
2021-01-12 16:26:12 +00:00
|
|
|
const node = getTarget(doc, slatePath) || map[obj]
|
2020-07-01 21:56:48 +00:00
|
|
|
|
2021-01-12 16:26:12 +00:00
|
|
|
// if we are removing text for a node that has already been removed
|
|
|
|
// treat this as a noop
|
|
|
|
if (!node) {
|
|
|
|
return
|
2020-07-01 21:56:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof index !== 'number') return
|
|
|
|
|
2020-10-27 18:29:56 +00:00
|
|
|
const text = node?.text?.[index] || '*'
|
2020-07-01 21:56:48 +00:00
|
|
|
|
2020-10-27 23:41:18 +00:00
|
|
|
if (node && node.text) {
|
|
|
|
node.text = node.text.slice(0, index) + node.text.slice(index + 1)
|
2020-07-01 21:56:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
type: 'remove_text',
|
|
|
|
path: slatePath,
|
|
|
|
offset: index,
|
2020-10-28 20:29:49 +00:00
|
|
|
text
|
2020-07-01 21:56:48 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-05 08:44:49 +00:00
|
|
|
|
2020-05-10 13:50:12 +00:00
|
|
|
const removeNodeOp = ({ index, obj, path }: Automerge.Diff) => (
|
|
|
|
map: any,
|
2020-07-01 21:56:48 +00:00
|
|
|
doc: Element
|
2020-05-10 13:50:12 +00:00
|
|
|
) => {
|
2019-10-06 10:30:35 +00:00
|
|
|
const slatePath = toSlatePath(path)
|
|
|
|
|
2020-07-01 21:56:48 +00:00
|
|
|
const parent = getTarget(doc, slatePath)
|
2021-01-12 16:26:12 +00:00
|
|
|
|
|
|
|
// if we are removing a node that has already been removed
|
|
|
|
// treat this as a noop
|
|
|
|
if (!parent) return
|
|
|
|
|
2020-10-27 22:29:50 +00:00
|
|
|
const target = parent?.children?.[index as number] || { children: [] }
|
2020-07-01 21:56:48 +00:00
|
|
|
|
|
|
|
if (!map.hasOwnProperty(obj)) {
|
2019-10-06 10:30:35 +00:00
|
|
|
map[obj] = target
|
|
|
|
}
|
|
|
|
|
2019-10-05 08:44:49 +00:00
|
|
|
return {
|
|
|
|
type: 'remove_node',
|
2019-10-06 10:30:35 +00:00
|
|
|
path: slatePath.length ? slatePath.concat(index) : [index],
|
2020-07-01 21:56:48 +00:00
|
|
|
node: target
|
2019-10-05 08:44:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-29 14:02:01 +00:00
|
|
|
const opRemove = (
|
|
|
|
op: Automerge.Diff,
|
|
|
|
[map, ops]: [CollabMap, CollabOperation[]]
|
|
|
|
) => {
|
2019-10-05 08:44:49 +00:00
|
|
|
try {
|
2020-05-10 13:50:12 +00:00
|
|
|
const { index, path, obj, type } = op
|
2019-10-05 08:44:49 +00:00
|
|
|
|
2020-05-10 13:50:12 +00:00
|
|
|
if (
|
|
|
|
map.hasOwnProperty(obj) &&
|
|
|
|
typeof map[obj] !== 'string' &&
|
2020-10-31 15:00:58 +00:00
|
|
|
map[obj].splice &&
|
2020-05-10 13:50:12 +00:00
|
|
|
type !== 'text'
|
|
|
|
) {
|
2019-10-05 08:44:49 +00:00
|
|
|
map[obj].splice(index, 1)
|
|
|
|
|
|
|
|
return [map, ops]
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!path) return [map, ops]
|
|
|
|
|
2020-05-10 13:50:12 +00:00
|
|
|
const key = path[path.length - 1]
|
|
|
|
|
|
|
|
if (key === 'cursors') return [map, ops]
|
2020-10-31 15:34:02 +00:00
|
|
|
// if we don't have a valid key and this is the root obj no slate op is needed
|
|
|
|
if (key === undefined && obj === rootKey) return [map, ops]
|
2020-05-10 13:50:12 +00:00
|
|
|
|
|
|
|
const fn = key === 'text' ? removeTextOp : removeNodeOp
|
2019-10-05 08:44:49 +00:00
|
|
|
|
|
|
|
return [map, [...ops, fn(op)]]
|
|
|
|
} catch (e) {
|
|
|
|
console.error(e, op, toJS(map))
|
|
|
|
|
|
|
|
return [map, ops]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default opRemove
|