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