@textbus/collaborate 4.0.0-alpha.8 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bundles/index.js CHANGED
@@ -1,10 +1,73 @@
1
1
  'use strict';
2
2
 
3
- var core$1 = require('@viewfly/core');
4
3
  var stream = require('@tanbo/stream');
4
+ var provider = require('@hocuspocus/provider');
5
+ var yWebsocket = require('y-websocket');
6
+ var core$1 = require('@viewfly/core');
5
7
  var core = require('@textbus/core');
6
8
  var yjs = require('yjs');
7
9
 
10
+ /**
11
+ * 协作通信通用接口
12
+ */
13
+ class SyncConnector {
14
+ }
15
+
16
+ class HocuspocusConnector extends SyncConnector {
17
+ constructor(config) {
18
+ super();
19
+ this.loadEvent = new stream.Subject();
20
+ this.stateChangeEvent = new stream.Subject();
21
+ this.onLoad = this.loadEvent.asObservable();
22
+ this.onStateChange = this.stateChangeEvent.asObservable();
23
+ this.provide = new provider.HocuspocusProvider(Object.assign(Object.assign({}, config), { onSynced: (data) => {
24
+ var _a;
25
+ (_a = config.onSynced) === null || _a === void 0 ? void 0 : _a.call(config, data);
26
+ this.loadEvent.next();
27
+ }, onAwarenessUpdate: (data) => {
28
+ var _a;
29
+ (_a = config.onAwarenessUpdate) === null || _a === void 0 ? void 0 : _a.call(config, data);
30
+ data.states.forEach(state => {
31
+ this.stateChangeEvent.next(state);
32
+ });
33
+ } }));
34
+ }
35
+ setLocalStateField(key, data) {
36
+ this.provide.setAwarenessField(key, data);
37
+ }
38
+ onDestroy() {
39
+ this.provide.disconnect();
40
+ this.provide.destroy();
41
+ }
42
+ }
43
+
44
+ class YWebsocketConnector extends SyncConnector {
45
+ constructor(url, roomName, yDoc) {
46
+ super();
47
+ this.loadEvent = new stream.Subject();
48
+ this.stateChangeEvent = new stream.Subject();
49
+ this.onLoad = this.loadEvent.asObservable();
50
+ this.onStateChange = this.stateChangeEvent.asObservable();
51
+ this.provide = new yWebsocket.WebsocketProvider(url, roomName, yDoc);
52
+ this.provide.on('sync', (is) => {
53
+ if (is) {
54
+ this.loadEvent.next();
55
+ }
56
+ });
57
+ this.provide.awareness.on('update', () => {
58
+ this.provide.awareness.getStates().forEach(state => {
59
+ this.stateChangeEvent.next(state);
60
+ });
61
+ });
62
+ }
63
+ setLocalStateField(key, data) {
64
+ this.provide.awareness.setLocalStateField(key, data);
65
+ }
66
+ onDestroy() {
67
+ this.provide.disconnect();
68
+ }
69
+ }
70
+
8
71
  /******************************************************************************
9
72
  Copyright (c) Microsoft Corporation.
10
73
 
@@ -43,38 +106,38 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
43
106
  };
44
107
 
45
108
  const collaborateErrorFn = core.makeError('Collaborate');
46
- class ContentMap {
109
+ class SlotMap {
47
110
  constructor() {
48
111
  this.slotAndYTextMap = new WeakMap();
49
- this.yTextAndSLotMap = new WeakMap();
112
+ this.yTextAndSlotMap = new WeakMap();
50
113
  }
51
114
  set(key, value) {
52
115
  if (key instanceof core.Slot) {
53
116
  this.slotAndYTextMap.set(key, value);
54
- this.yTextAndSLotMap.set(value, key);
117
+ this.yTextAndSlotMap.set(value, key);
55
118
  }
56
119
  else {
57
120
  this.slotAndYTextMap.set(value, key);
58
- this.yTextAndSLotMap.set(key, value);
121
+ this.yTextAndSlotMap.set(key, value);
59
122
  }
60
123
  }
61
124
  get(key) {
62
125
  if (key instanceof core.Slot) {
63
126
  return this.slotAndYTextMap.get(key) || null;
64
127
  }
65
- return this.yTextAndSLotMap.get(key) || null;
128
+ return this.yTextAndSlotMap.get(key) || null;
66
129
  }
67
130
  delete(key) {
68
131
  if (key instanceof core.Slot) {
69
132
  const v = this.slotAndYTextMap.get(key);
70
133
  this.slotAndYTextMap.delete(key);
71
134
  if (v) {
72
- this.yTextAndSLotMap.delete(v);
135
+ this.yTextAndSlotMap.delete(v);
73
136
  }
74
137
  }
75
138
  else {
76
- const v = this.yTextAndSLotMap.get(key);
77
- this.yTextAndSLotMap.delete(key);
139
+ const v = this.yTextAndSlotMap.get(key);
140
+ this.yTextAndSlotMap.delete(key);
78
141
  if (v) {
79
142
  this.slotAndYTextMap.delete(v);
80
143
  }
@@ -107,13 +170,9 @@ exports.Collaborate = class Collaborate {
107
170
  this.manager = null;
108
171
  this.subscriptions = [];
109
172
  this.updateFromRemote = false;
110
- this.contentSyncCaches = new WeakMap();
111
- this.slotStateSyncCaches = new WeakMap();
112
- this.slotsSyncCaches = new WeakMap();
113
- this.componentStateSyncCaches = new WeakMap();
114
173
  this.localChangesAppliedEvent = new stream.Subject();
115
174
  this.selectionChangeEvent = new stream.Subject();
116
- this.contentMap = new ContentMap();
175
+ this.slotMap = new SlotMap();
117
176
  this.updateRemoteActions = [];
118
177
  this.noRecord = {};
119
178
  this.historyItems = [];
@@ -148,7 +207,7 @@ exports.Collaborate = class Collaborate {
148
207
  this.subscriptions.push(this.scheduler.onLocalChangeBefore.subscribe(() => {
149
208
  beforePosition = this.getRelativeCursorLocation();
150
209
  }));
151
- manager.on('stack-item-added', event => {
210
+ manager.on('stack-item-added', (event) => {
152
211
  if (event.type === 'undo') {
153
212
  if (event.origin === manager) {
154
213
  this.index++;
@@ -260,50 +319,145 @@ exports.Collaborate = class Collaborate {
260
319
  (_a = this.manager) === null || _a === void 0 ? void 0 : _a.destroy();
261
320
  }
262
321
  syncRootComponent(root, rootComponent) {
263
- let slots = root.get('slots');
264
- if (!slots) {
265
- slots = new yjs.Array();
266
- rootComponent.slots.toArray().forEach(i => {
267
- const sharedSlot = this.createSharedSlotBySlot(i);
268
- slots.push([sharedSlot]);
269
- });
322
+ let state = root.get('state');
323
+ if (!state) {
324
+ state = new yjs.Map();
325
+ this.syncLocalMapToSharedMap(rootComponent.state, state);
270
326
  this.yDoc.transact(() => {
271
- root.set('state', rootComponent.state);
272
- root.set('slots', slots);
327
+ root.set('state', state);
273
328
  });
274
329
  }
275
- else if (slots.length === 0) {
276
- rootComponent.updateState(() => {
277
- return root.get('state');
330
+ else {
331
+ Object.keys(rootComponent.state).forEach(key => {
332
+ Reflect.deleteProperty(rootComponent.state, key);
278
333
  });
279
- this.yDoc.transact(() => {
280
- rootComponent.slots.toArray().forEach(i => {
281
- const sharedSlot = this.createSharedSlotBySlot(i);
282
- slots.push([sharedSlot]);
334
+ this.syncSharedMapToLocalMap(state, rootComponent.state);
335
+ }
336
+ }
337
+ syncSharedMapToLocalMap(sharedMap, localMap) {
338
+ sharedMap.forEach((value, key) => {
339
+ localMap[key] = this.createLocalModelBySharedByModel(value);
340
+ });
341
+ this.syncObject(sharedMap, localMap);
342
+ }
343
+ createLocalMapBySharedMap(sharedMap) {
344
+ const localMap = core.createObjectProxy({});
345
+ this.syncSharedMapToLocalMap(sharedMap, localMap);
346
+ return localMap;
347
+ }
348
+ createLocalArrayBySharedArray(sharedArray) {
349
+ const localArray = core.createArrayProxy([]);
350
+ localArray.push(...sharedArray.map(item => this.createLocalModelBySharedByModel(item)));
351
+ this.syncArray(sharedArray, localArray);
352
+ return localArray;
353
+ }
354
+ syncLocalMapToSharedMap(localMap, sharedMap) {
355
+ Object.entries(localMap).forEach(([key, value]) => {
356
+ sharedMap.set(key, this.createSharedModelByLocalModel(value));
357
+ });
358
+ this.syncObject(sharedMap, localMap);
359
+ }
360
+ createSharedMapByLocalMap(localMap) {
361
+ const sharedMap = new yjs.Map();
362
+ this.syncLocalMapToSharedMap(localMap, sharedMap);
363
+ return sharedMap;
364
+ }
365
+ createSharedArrayByLocalArray(localArray) {
366
+ const sharedArray = new yjs.Array();
367
+ localArray.forEach(value => {
368
+ sharedArray.push([this.createSharedModelByLocalModel(value)]);
369
+ });
370
+ this.syncArray(sharedArray, localArray);
371
+ return sharedArray;
372
+ }
373
+ createSharedSlotByLocalSlot(localSlot) {
374
+ const sharedSlot = new yjs.Text();
375
+ sharedSlot.setAttribute('__schema__', [...localSlot.schema]);
376
+ let offset = 0;
377
+ localSlot.toDelta().forEach(i => {
378
+ let formats = {};
379
+ if (i.formats) {
380
+ i.formats.forEach(item => {
381
+ formats[item[0].name] = item[1];
283
382
  });
284
- });
383
+ }
384
+ else {
385
+ formats = null;
386
+ }
387
+ if (typeof i.insert === 'string') {
388
+ sharedSlot.insert(offset, i.insert, formats);
389
+ }
390
+ else {
391
+ const sharedComponent = this.createSharedComponentByLocalComponent(i.insert);
392
+ sharedSlot.insertEmbed(offset, sharedComponent, formats);
393
+ }
394
+ offset += i.insert.length;
395
+ });
396
+ localSlot.getAttributes().forEach(item => {
397
+ sharedSlot.setAttribute(item[0].name, item[1]);
398
+ });
399
+ this.syncSlot(sharedSlot, localSlot);
400
+ return sharedSlot;
401
+ }
402
+ createLocalSlotBySharedSlot(sharedSlot) {
403
+ const delta = sharedSlot.toDelta();
404
+ const localSlot = new core.Slot(sharedSlot.getAttribute('__schema__') || []); // TODO 这里有潜在的问题
405
+ const attrs = sharedSlot.getAttributes();
406
+ Object.keys(attrs).forEach(key => {
407
+ const attribute = this.registry.getAttribute(key);
408
+ if (attribute) {
409
+ localSlot.setAttribute(attribute, attrs[key]);
410
+ }
411
+ });
412
+ for (const action of delta) {
413
+ if (action.insert) {
414
+ if (typeof action.insert === 'string') {
415
+ const formats = remoteFormatsToLocal(this.registry, action.attributes);
416
+ localSlot.insert(action.insert, formats);
417
+ }
418
+ else {
419
+ const sharedComponent = action.insert;
420
+ const component = this.createLocalComponentBySharedComponent(sharedComponent);
421
+ localSlot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
422
+ }
423
+ }
424
+ else {
425
+ throw collaborateErrorFn('unexpected delta action.');
426
+ }
285
427
  }
286
- else {
287
- rootComponent.updateState(() => {
288
- return root.get('state');
289
- });
290
- rootComponent.slots.clean();
291
- slots.forEach(sharedSlot => {
292
- const slot = this.createSlotBySharedSlot(sharedSlot);
293
- this.syncSlotContent(sharedSlot.get('content'), slot);
294
- this.syncSlotState(sharedSlot, slot);
295
- rootComponent.slots.insert(slot);
296
- });
428
+ this.syncSlot(sharedSlot, localSlot);
429
+ return localSlot;
430
+ }
431
+ createSharedModelByLocalModel(localModel) {
432
+ if (localModel instanceof core.Slot) {
433
+ return this.createSharedSlotByLocalSlot(localModel);
434
+ }
435
+ if (Array.isArray(localModel)) {
436
+ return this.createSharedArrayByLocalArray(localModel);
297
437
  }
298
- this.syncComponentState(root, rootComponent);
299
- this.syncComponentSlots(slots, rootComponent);
438
+ if (typeof localModel === 'object' && localModel !== null) {
439
+ return this.createSharedMapByLocalMap(localModel);
440
+ }
441
+ return localModel;
442
+ }
443
+ createLocalModelBySharedByModel(sharedModel) {
444
+ if (sharedModel instanceof yjs.Map) {
445
+ return this.createLocalMapBySharedMap(sharedModel);
446
+ }
447
+ if (sharedModel instanceof yjs.Array) {
448
+ return this.createLocalArrayBySharedArray(sharedModel);
449
+ }
450
+ if (sharedModel instanceof yjs.Text) {
451
+ return this.createLocalSlotBySharedSlot(sharedModel);
452
+ }
453
+ return sharedModel;
300
454
  }
301
455
  getAbstractSelection(position) {
302
456
  const anchorPosition = yjs.createAbsolutePositionFromRelativePosition(position.anchor, this.yDoc);
303
457
  const focusPosition = yjs.createAbsolutePositionFromRelativePosition(position.focus, this.yDoc);
304
458
  if (anchorPosition && focusPosition) {
305
- const focusSlot = this.contentMap.get(focusPosition.type);
306
- const anchorSlot = this.contentMap.get(anchorPosition.type);
459
+ const focusSlot = this.slotMap.get(focusPosition.type);
460
+ const anchorSlot = this.slotMap.get(anchorPosition.type);
307
461
  if (focusSlot && anchorSlot) {
308
462
  return {
309
463
  anchorSlot,
@@ -318,11 +472,11 @@ exports.Collaborate = class Collaborate {
318
472
  getRelativeCursorLocation() {
319
473
  const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection;
320
474
  if (anchorSlot) {
321
- const anchorYText = this.contentMap.get(anchorSlot);
475
+ const anchorYText = this.slotMap.get(anchorSlot);
322
476
  if (anchorYText) {
323
477
  const anchorPosition = yjs.createRelativePositionFromTypeIndex(anchorYText, anchorOffset);
324
478
  if (focusSlot) {
325
- const focusYText = this.contentMap.get(focusSlot);
479
+ const focusYText = this.slotMap.get(focusSlot);
326
480
  if (focusYText) {
327
481
  const focusPosition = yjs.createRelativePositionFromTypeIndex(focusYText, focusOffset);
328
482
  return {
@@ -335,11 +489,10 @@ exports.Collaborate = class Collaborate {
335
489
  }
336
490
  return null;
337
491
  }
338
- syncSlotContent(content, slot) {
339
- this.contentMap.set(slot, content);
492
+ syncSlot(sharedSlot, localSlot) {
340
493
  const syncRemote = (ev, tr) => {
341
494
  this.runRemoteUpdate(tr, () => {
342
- slot.retain(0);
495
+ localSlot.retain(0);
343
496
  ev.keysChanged.forEach(key => {
344
497
  const change = ev.keys.get(key);
345
498
  if (!change) {
@@ -349,13 +502,13 @@ exports.Collaborate = class Collaborate {
349
502
  if (updateType === 'update' || updateType === 'add') {
350
503
  const attribute = this.registry.getAttribute(key);
351
504
  if (attribute) {
352
- slot.setAttribute(attribute, content.getAttribute(key));
505
+ localSlot.setAttribute(attribute, sharedSlot.getAttribute(key));
353
506
  }
354
507
  }
355
508
  else if (updateType === 'delete') {
356
509
  const attribute = this.registry.getAttribute(key);
357
510
  if (attribute) {
358
- slot.removeAttribute(attribute);
511
+ localSlot.removeAttribute(attribute);
359
512
  }
360
513
  }
361
514
  });
@@ -364,54 +517,52 @@ exports.Collaborate = class Collaborate {
364
517
  if (action.attributes) {
365
518
  const formats = remoteFormatsToLocal(this.registry, action.attributes);
366
519
  if (formats.length) {
367
- slot.retain(action.retain, formats);
520
+ localSlot.retain(action.retain, formats);
368
521
  }
369
- slot.retain(slot.index + action.retain);
522
+ localSlot.retain(localSlot.index + action.retain);
370
523
  }
371
524
  else {
372
- slot.retain(action.retain);
525
+ localSlot.retain(action.retain);
373
526
  }
374
527
  }
375
528
  else if (action.insert) {
376
- const index = slot.index;
529
+ const index = localSlot.index;
377
530
  let length = 1;
378
531
  if (typeof action.insert === 'string') {
379
532
  length = action.insert.length;
380
- slot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes));
533
+ localSlot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes));
381
534
  }
382
535
  else {
383
536
  const sharedComponent = action.insert;
384
- const component = this.createComponentBySharedComponent(sharedComponent);
385
- this.syncComponentSlots(sharedComponent.get('slots'), component);
386
- this.syncComponentState(sharedComponent, component);
387
- slot.insert(component);
537
+ const component = this.createLocalComponentBySharedComponent(sharedComponent);
538
+ localSlot.insert(component);
388
539
  }
389
540
  if (this.selection.isSelected && tr.origin !== this.manager) {
390
- if (slot === this.selection.anchorSlot && this.selection.anchorOffset > index) {
391
- this.selection.setAnchor(slot, this.selection.anchorOffset + length);
541
+ if (localSlot === this.selection.anchorSlot && this.selection.anchorOffset > index) {
542
+ this.selection.setAnchor(localSlot, this.selection.anchorOffset + length);
392
543
  }
393
- if (slot === this.selection.focusSlot && this.selection.focusOffset > index) {
394
- this.selection.setFocus(slot, this.selection.focusOffset + length);
544
+ if (localSlot === this.selection.focusSlot && this.selection.focusOffset > index) {
545
+ this.selection.setFocus(localSlot, this.selection.focusOffset + length);
395
546
  }
396
547
  }
397
548
  }
398
549
  else if (action.delete) {
399
- const index = slot.index;
400
- slot.delete(action.delete);
550
+ const index = localSlot.index;
551
+ localSlot.delete(action.delete);
401
552
  if (this.selection.isSelected && tr.origin !== this.manager) {
402
- if (slot === this.selection.anchorSlot && this.selection.anchorOffset >= index) {
403
- this.selection.setAnchor(slot, this.selection.startOffset - action.delete);
553
+ if (localSlot === this.selection.anchorSlot && this.selection.anchorOffset >= index) {
554
+ this.selection.setAnchor(localSlot, this.selection.startOffset - action.delete);
404
555
  }
405
- if (slot === this.selection.focusSlot && this.selection.focusOffset >= index) {
406
- this.selection.setFocus(slot, this.selection.focusOffset - action.delete);
556
+ if (localSlot === this.selection.focusSlot && this.selection.focusOffset >= index) {
557
+ this.selection.setFocus(localSlot, this.selection.focusOffset - action.delete);
407
558
  }
408
559
  }
409
560
  }
410
561
  });
411
562
  });
412
563
  };
413
- content.observe(syncRemote);
414
- const sub = slot.onContentChange.subscribe(actions => {
564
+ sharedSlot.observe(syncRemote);
565
+ const sub = localSlot.onContentChange.subscribe(actions => {
415
566
  this.runLocalUpdate(() => {
416
567
  var _a;
417
568
  let offset = 0;
@@ -430,170 +581,176 @@ exports.Collaborate = class Collaborate {
430
581
  }
431
582
  });
432
583
  if (length) {
433
- content.format(offset, action.offset, formats);
584
+ sharedSlot.format(offset, action.offset, formats);
434
585
  }
435
586
  }
436
587
  else {
437
588
  offset = action.offset;
438
589
  }
439
590
  }
440
- else if (action.type === 'insert') {
441
- const delta = content.toDelta();
591
+ else if (action.type === 'contentInsert') {
592
+ const delta = sharedSlot.toDelta();
442
593
  const isEmpty = delta.length === 1 && delta[0].insert === core.Slot.emptyPlaceholder;
443
594
  if (typeof action.content === 'string') {
444
595
  length = action.content.length;
445
- content.insert(offset, action.content, action.formats || {});
596
+ sharedSlot.insert(offset, action.content, action.formats || {});
446
597
  }
447
598
  else {
448
599
  length = 1;
449
- const sharedComponent = this.createSharedComponentByComponent(action.ref);
450
- content.insertEmbed(offset, sharedComponent, action.formats || {});
600
+ const sharedComponent = this.createSharedComponentByLocalComponent(action.ref);
601
+ sharedSlot.insertEmbed(offset, sharedComponent, action.formats || {});
451
602
  }
452
603
  if (isEmpty && offset === 0) {
453
- content.delete(content.length - 1, 1);
604
+ sharedSlot.delete(sharedSlot.length - 1, 1);
454
605
  }
455
606
  offset += length;
456
607
  }
457
608
  else if (action.type === 'delete') {
458
- const delta = content.toDelta();
459
- if (content.length) {
460
- content.delete(offset, action.count);
609
+ const delta = sharedSlot.toDelta();
610
+ if (sharedSlot.length) {
611
+ sharedSlot.delete(offset, action.count);
461
612
  }
462
- if (content.length === 0) {
463
- content.insert(0, '\n', (_a = delta[0]) === null || _a === void 0 ? void 0 : _a.attributes);
613
+ if (sharedSlot.length === 0) {
614
+ sharedSlot.insert(0, '\n', (_a = delta[0]) === null || _a === void 0 ? void 0 : _a.attributes);
464
615
  }
465
616
  }
466
617
  else if (action.type === 'attrSet') {
467
- content.setAttribute(action.name, action.value);
618
+ sharedSlot.setAttribute(action.name, action.value);
468
619
  }
469
- else if (action.type === 'attrRemove') {
470
- content.removeAttribute(action.name);
620
+ else if (action.type === 'attrDelete') {
621
+ sharedSlot.removeAttribute(action.name);
471
622
  }
472
623
  }
473
624
  });
474
625
  });
475
- sub.add(slot.onChildComponentRemove.subscribe(components => {
476
- components.forEach(c => {
477
- this.cleanSubscriptionsByComponent(c);
478
- });
479
- }));
480
- this.contentSyncCaches.set(slot, () => {
481
- content.unobserve(syncRemote);
626
+ this.slotMap.set(localSlot, sharedSlot);
627
+ localSlot.__changeMarker__.destroyCallbacks.push(() => {
628
+ this.slotMap.delete(localSlot);
629
+ sharedSlot.unobserve(syncRemote);
482
630
  sub.unsubscribe();
483
631
  });
484
632
  }
485
- syncSlotState(remoteSlot, slot) {
486
- const syncRemote = (ev, tr) => {
487
- this.runRemoteUpdate(tr, () => {
488
- ev.keysChanged.forEach(key => {
489
- if (key === 'state') {
490
- const state = ev.target.get('state');
491
- slot.updateState(draft => {
492
- if (typeof draft === 'object' && draft !== null) {
493
- Object.assign(draft, state);
633
+ createSharedComponentByLocalComponent(component) {
634
+ const sharedComponent = new yjs.Map();
635
+ const sharedState = this.createSharedMapByLocalMap(component.state);
636
+ sharedComponent.set('name', component.name);
637
+ sharedComponent.set('state', sharedState);
638
+ return sharedComponent;
639
+ }
640
+ createLocalComponentBySharedComponent(yMap) {
641
+ const componentName = yMap.get('name');
642
+ const sharedState = yMap.get('state');
643
+ const state = this.createLocalMapBySharedMap(sharedState);
644
+ const instance = this.registry.createComponentByData(componentName, state);
645
+ if (instance) {
646
+ return instance;
647
+ }
648
+ throw collaborateErrorFn(`cannot find component factory \`${componentName}\`.`);
649
+ }
650
+ /**
651
+ * 双向同步数组
652
+ * @param sharedArray
653
+ * @param localArray
654
+ * @private
655
+ */
656
+ syncArray(sharedArray, localArray) {
657
+ const sub = localArray.__changeMarker__.onSelfChange.subscribe((actions) => {
658
+ this.runLocalUpdate(() => {
659
+ let index = 0;
660
+ for (const action of actions) {
661
+ switch (action.type) {
662
+ case 'retain':
663
+ index = action.offset;
664
+ break;
665
+ case 'insert':
666
+ {
667
+ const ref = action.ref;
668
+ if (!Array.isArray(ref)) {
669
+ throw collaborateErrorFn('The insertion action must have a reference value.');
670
+ }
671
+ const data = ref.map(item => {
672
+ return this.createSharedModelByLocalModel(item);
673
+ });
674
+ sharedArray.insert(index, data);
494
675
  }
495
- else {
496
- return state;
676
+ break;
677
+ case 'delete':
678
+ if (action.count <= 0) {
679
+ break;
497
680
  }
498
- });
681
+ sharedArray.delete(index, action.count);
682
+ break;
683
+ case 'setIndex':
684
+ sharedArray.delete(action.index, 1);
685
+ sharedArray.insert(action.index, [this.createSharedModelByLocalModel(action.ref)]);
686
+ break;
499
687
  }
500
- });
688
+ }
501
689
  });
502
- };
503
- remoteSlot.observe(syncRemote);
504
- const sub = slot.onStateChange.subscribe(change => {
505
- this.runLocalUpdate(() => {
506
- remoteSlot.set('state', change.newState);
507
- }, change.record);
508
- });
509
- this.slotStateSyncCaches.set(slot, () => {
510
- remoteSlot.unobserve(syncRemote);
511
- sub.unsubscribe();
512
690
  });
513
- }
514
- syncComponentSlots(remoteSlots, component) {
515
- const slots = component.slots;
516
691
  const syncRemote = (ev, tr) => {
517
692
  this.runRemoteUpdate(tr, () => {
518
693
  let index = 0;
519
- slots.retain(index);
520
- ev.delta.forEach(action => {
694
+ ev.delta.forEach((action) => {
521
695
  if (Reflect.has(action, 'retain')) {
522
696
  index += action.retain;
523
- slots.retain(index);
524
697
  }
525
698
  else if (action.insert) {
526
- action.insert.forEach(item => {
527
- const slot = this.createSlotBySharedSlot(item);
528
- slots.insert(slot);
529
- this.syncSlotContent(item.get('content'), slot);
530
- this.syncSlotState(item, slot);
531
- index++;
699
+ const data = action.insert.map((item) => {
700
+ return this.createLocalModelBySharedByModel(item);
532
701
  });
702
+ localArray.splice(index, 0, ...data);
703
+ index += data.length;
533
704
  }
534
705
  else if (action.delete) {
535
- slots.retain(index);
536
- slots.delete(action.delete);
706
+ localArray.splice(index, action.delete);
537
707
  }
538
708
  });
539
709
  });
540
710
  };
541
- remoteSlots.observe(syncRemote);
542
- const sub = slots.onChange.subscribe(operations => {
543
- this.runLocalUpdate(() => {
544
- const applyActions = operations.apply;
545
- let index;
546
- applyActions.forEach(action => {
547
- if (action.type === 'retain') {
548
- index = action.offset;
549
- }
550
- else if (action.type === 'insertSlot') {
551
- const sharedSlot = this.createSharedSlotBySlot(action.ref);
552
- remoteSlots.insert(index, [sharedSlot]);
553
- index++;
554
- }
555
- else if (action.type === 'delete') {
556
- remoteSlots.delete(index, action.count);
557
- }
558
- });
559
- });
560
- });
561
- sub.add(slots.onChildSlotRemove.subscribe(slots => {
562
- slots.forEach(slot => {
563
- this.cleanSubscriptionsBySlot(slot);
564
- });
565
- }));
566
- this.slotsSyncCaches.set(component, () => {
567
- remoteSlots.unobserve(syncRemote);
711
+ sharedArray.observe(syncRemote);
712
+ localArray.__changeMarker__.destroyCallbacks.push(() => {
568
713
  sub.unsubscribe();
714
+ sharedArray.unobserve(syncRemote);
569
715
  });
570
716
  }
571
- syncComponentState(remoteComponent, component) {
717
+ /**
718
+ * 双向同步对象
719
+ * @param sharedObject
720
+ * @param localObject
721
+ * @private
722
+ */
723
+ syncObject(sharedObject, localObject) {
572
724
  const syncRemote = (ev, tr) => {
573
725
  this.runRemoteUpdate(tr, () => {
574
- ev.keysChanged.forEach(key => {
575
- if (key === 'state') {
576
- const state = ev.target.get('state');
577
- component.updateState(draft => {
578
- if (typeof draft === 'object' && draft !== null) {
579
- Object.assign(draft, state);
580
- }
581
- else {
582
- return state;
583
- }
584
- });
726
+ ev.changes.keys.forEach((item, key) => {
727
+ if (item.action === 'add' || item.action === 'update') {
728
+ const value = sharedObject.get(key);
729
+ localObject[key] = this.createLocalModelBySharedByModel(value);
730
+ }
731
+ else {
732
+ Reflect.deleteProperty(localObject, key);
585
733
  }
586
734
  });
587
735
  });
588
736
  };
589
- remoteComponent.observe(syncRemote);
590
- const sub = component.onStateChange.subscribe(change => {
737
+ sharedObject.observe(syncRemote);
738
+ const sub = localObject.__changeMarker__.onSelfChange.subscribe((actions) => {
591
739
  this.runLocalUpdate(() => {
592
- remoteComponent.set('state', change.newState);
593
- }, change.record);
740
+ for (const action of actions) {
741
+ switch (action.type) {
742
+ case 'propSet':
743
+ sharedObject.set(action.key, this.createSharedModelByLocalModel(action.ref));
744
+ break;
745
+ case 'propDelete':
746
+ sharedObject.delete(action.key);
747
+ break;
748
+ }
749
+ }
750
+ });
594
751
  });
595
- this.componentStateSyncCaches.set(component, () => {
596
- remoteComponent.unobserve(syncRemote);
752
+ localObject.__changeMarker__.destroyCallbacks.push(function () {
753
+ sharedObject.unobserve(syncRemote);
597
754
  sub.unsubscribe();
598
755
  });
599
756
  }
@@ -619,140 +776,6 @@ exports.Collaborate = class Collaborate {
619
776
  }
620
777
  this.updateFromRemote = false;
621
778
  }
622
- createSharedComponentByComponent(component) {
623
- const sharedComponent = new yjs.Map();
624
- sharedComponent.set('state', component.state);
625
- sharedComponent.set('name', component.name);
626
- const sharedSlots = new yjs.Array();
627
- sharedComponent.set('slots', sharedSlots);
628
- component.slots.toArray().forEach(slot => {
629
- const sharedSlot = this.createSharedSlotBySlot(slot);
630
- sharedSlots.push([sharedSlot]);
631
- });
632
- this.syncComponentSlots(sharedSlots, component);
633
- this.syncComponentState(sharedComponent, component);
634
- return sharedComponent;
635
- }
636
- createSharedSlotBySlot(slot) {
637
- const sharedSlot = new yjs.Map();
638
- sharedSlot.set('schema', slot.schema);
639
- sharedSlot.set('state', slot.state);
640
- const sharedContent = new yjs.Text();
641
- sharedSlot.set('content', sharedContent);
642
- let offset = 0;
643
- slot.toDelta().forEach(i => {
644
- let formats = {};
645
- if (i.formats) {
646
- i.formats.forEach(item => {
647
- formats[item[0].name] = item[1];
648
- });
649
- }
650
- else {
651
- formats = null;
652
- }
653
- if (typeof i.insert === 'string') {
654
- sharedContent.insert(offset, i.insert, formats);
655
- }
656
- else {
657
- const sharedComponent = this.createSharedComponentByComponent(i.insert);
658
- sharedContent.insertEmbed(offset, sharedComponent, formats);
659
- }
660
- offset += i.insert.length;
661
- });
662
- slot.getAttributes().forEach(item => {
663
- sharedContent.setAttribute(item[0].name, item[1]);
664
- });
665
- this.syncSlotContent(sharedContent, slot);
666
- this.syncSlotState(sharedSlot, slot);
667
- return sharedSlot;
668
- }
669
- createComponentBySharedComponent(yMap) {
670
- const sharedSlots = yMap.get('slots');
671
- const slots = [];
672
- sharedSlots.forEach(sharedSlot => {
673
- const slot = this.createSlotBySharedSlot(sharedSlot);
674
- slots.push(slot);
675
- });
676
- const name = yMap.get('name');
677
- const state = yMap.get('state');
678
- const instance = this.registry.createComponentByData(name, {
679
- state,
680
- slots
681
- });
682
- if (instance) {
683
- instance.slots.toArray().forEach((slot, index) => {
684
- let sharedSlot = sharedSlots.get(index);
685
- if (!sharedSlot) {
686
- sharedSlot = this.createSharedSlotBySlot(slot);
687
- sharedSlots.push([sharedSlot]);
688
- }
689
- this.syncSlotState(sharedSlot, slot);
690
- this.syncSlotContent(sharedSlot.get('content'), slot);
691
- });
692
- return instance;
693
- }
694
- throw collaborateErrorFn(`cannot find component factory \`${name}\`.`);
695
- }
696
- createSlotBySharedSlot(sharedSlot) {
697
- const content = sharedSlot.get('content');
698
- const delta = content.toDelta();
699
- const slot = this.registry.createSlot({
700
- schema: sharedSlot.get('schema'),
701
- state: sharedSlot.get('state'),
702
- attributes: {},
703
- formats: {},
704
- content: []
705
- });
706
- const attrs = content.getAttributes();
707
- Object.keys(attrs).forEach(key => {
708
- const attribute = this.registry.getAttribute(key);
709
- if (attribute) {
710
- slot.setAttribute(attribute, attrs[key]);
711
- }
712
- });
713
- for (const action of delta) {
714
- if (action.insert) {
715
- if (typeof action.insert === 'string') {
716
- const formats = remoteFormatsToLocal(this.registry, action.attributes);
717
- slot.insert(action.insert, formats);
718
- }
719
- else {
720
- const sharedComponent = action.insert;
721
- const component = this.createComponentBySharedComponent(sharedComponent);
722
- slot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
723
- this.syncComponentSlots(sharedComponent.get('slots'), component);
724
- this.syncComponentState(sharedComponent, component);
725
- }
726
- }
727
- else {
728
- throw collaborateErrorFn('unexpected delta action.');
729
- }
730
- }
731
- return slot;
732
- }
733
- cleanSubscriptionsBySlot(slot) {
734
- this.contentMap.delete(slot);
735
- [this.contentSyncCaches.get(slot), this.slotStateSyncCaches.get(slot)].forEach(fn => {
736
- if (fn) {
737
- fn();
738
- }
739
- });
740
- slot.sliceContent().forEach(i => {
741
- if (typeof i !== 'string') {
742
- this.cleanSubscriptionsByComponent(i);
743
- }
744
- });
745
- }
746
- cleanSubscriptionsByComponent(component) {
747
- [this.slotsSyncCaches.get(component), this.componentStateSyncCaches.get(component)].forEach(fn => {
748
- if (fn) {
749
- fn();
750
- }
751
- });
752
- component.slots.toArray().forEach(slot => {
753
- this.cleanSubscriptionsBySlot(slot);
754
- });
755
- }
756
779
  };
757
780
  exports.Collaborate = __decorate([
758
781
  core$1.Injectable(),
@@ -777,17 +800,77 @@ function remoteFormatsToLocal(registry, attrs) {
777
800
  return formats;
778
801
  }
779
802
 
803
+ exports.UserActivity = class UserActivity {
804
+ constructor(syncConnector, selection) {
805
+ this.syncConnector = syncConnector;
806
+ this.selection = selection;
807
+ this.stateChangeEvent = new stream.Subject();
808
+ this.userChangeEvent = new stream.Subject();
809
+ this.subscription = new stream.Subscription();
810
+ this.onStateChange = this.stateChangeEvent.asObservable();
811
+ this.onUserChange = this.userChangeEvent.asObservable();
812
+ }
813
+ init(userinfo) {
814
+ this.syncConnector.setLocalStateField('user', userinfo);
815
+ this.subscription.add(this.selection.onChange.subscribe(() => {
816
+ const selection = this.selection.getPaths();
817
+ this.syncConnector.setLocalStateField('selection', Object.assign(Object.assign({}, userinfo), { selection }));
818
+ }), this.syncConnector.onStateChange.subscribe((state) => {
819
+ const users = [];
820
+ const remoteSelections = [];
821
+ if (state.user) {
822
+ users.push(state.user);
823
+ }
824
+ if (state.selection) {
825
+ remoteSelections.push(state.selection);
826
+ }
827
+ const selections = remoteSelections.filter(i => i.id !== userinfo.id);
828
+ this.userChangeEvent.next(users);
829
+ this.stateChangeEvent.next(selections);
830
+ }));
831
+ }
832
+ destroy() {
833
+ this.subscription.unsubscribe();
834
+ }
835
+ };
836
+ exports.UserActivity = __decorate([
837
+ core$1.Injectable(),
838
+ __metadata("design:paramtypes", [SyncConnector,
839
+ core.Selection])
840
+ ], exports.UserActivity);
841
+
780
842
  class CollaborateModule {
781
- constructor() {
843
+ constructor(config) {
844
+ this.config = config;
782
845
  this.providers = [
783
846
  exports.Collaborate,
847
+ exports.UserActivity,
784
848
  {
785
849
  provide: core.History,
786
850
  useExisting: exports.Collaborate
851
+ }, {
852
+ provide: SyncConnector,
853
+ useFactory: (collab) => {
854
+ return this.config.createConnector(collab.yDoc);
855
+ },
856
+ deps: [exports.Collaborate]
787
857
  }
788
858
  ];
789
859
  }
860
+ setup(textbus) {
861
+ const connector = textbus.get(SyncConnector);
862
+ const userActivity = textbus.get(exports.UserActivity);
863
+ userActivity.init(this.config.userinfo);
864
+ return connector.onLoad.toPromise();
865
+ }
866
+ onDestroy(textbus) {
867
+ textbus.get(exports.UserActivity).destroy();
868
+ textbus.get(SyncConnector).onDestroy();
869
+ }
790
870
  }
791
871
 
792
872
  exports.CollaborateModule = CollaborateModule;
793
873
  exports.CustomUndoManagerConfig = CustomUndoManagerConfig;
874
+ exports.HocuspocusConnector = HocuspocusConnector;
875
+ exports.SyncConnector = SyncConnector;
876
+ exports.YWebsocketConnector = YWebsocketConnector;