@textbus/collaborate 4.0.0-alpha.24 → 4.0.0-alpha.26

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  import { Injectable, Inject, Optional } from '@viewfly/core';
2
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';
3
+ import { makeError, HISTORY_STACK_SIZE, ChangeOrigin, MapModel, ArrayModel, Slot, RootComponentRef, Scheduler, Registry, Selection, History } from '@textbus/core';
4
+ import { Doc, UndoManager, Map, Array, Text, createAbsolutePositionFromRelativePosition, createRelativePositionFromTypeIndex } from 'yjs';
5
5
 
6
6
  /******************************************************************************
7
7
  Copyright (c) Microsoft Corporation.
@@ -41,7 +41,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
41
41
  };
42
42
 
43
43
  const collaborateErrorFn = makeError('Collaborate');
44
- class ContentMap {
44
+ class SlotMap {
45
45
  constructor() {
46
46
  this.slotAndYTextMap = new WeakMap();
47
47
  this.yTextAndSLotMap = new WeakMap();
@@ -105,13 +105,9 @@ let Collaborate = class Collaborate {
105
105
  this.manager = null;
106
106
  this.subscriptions = [];
107
107
  this.updateFromRemote = false;
108
- this.contentSyncCaches = new WeakMap();
109
- this.slotStateSyncCaches = new WeakMap();
110
- this.slotsSyncCaches = new WeakMap();
111
- this.componentStateSyncCaches = new WeakMap();
112
108
  this.localChangesAppliedEvent = new Subject();
113
109
  this.selectionChangeEvent = new Subject();
114
- this.contentMap = new ContentMap();
110
+ this.slotMap = new SlotMap();
115
111
  this.updateRemoteActions = [];
116
112
  this.noRecord = {};
117
113
  this.historyItems = [];
@@ -146,7 +142,7 @@ let Collaborate = class Collaborate {
146
142
  this.subscriptions.push(this.scheduler.onLocalChangeBefore.subscribe(() => {
147
143
  beforePosition = this.getRelativeCursorLocation();
148
144
  }));
149
- manager.on('stack-item-added', event => {
145
+ manager.on('stack-item-added', (event) => {
150
146
  if (event.type === 'undo') {
151
147
  if (event.origin === manager) {
152
148
  this.index++;
@@ -258,50 +254,153 @@ let Collaborate = class Collaborate {
258
254
  (_a = this.manager) === null || _a === void 0 ? void 0 : _a.destroy();
259
255
  }
260
256
  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
- });
257
+ let state = root.get('state');
258
+ if (!state) {
259
+ state = new Map();
260
+ this.syncLocalMapToSharedMap(rootComponent.state, state, rootComponent);
268
261
  this.yDoc.transact(() => {
269
- root.set('state', rootComponent.state);
270
- root.set('slots', slots);
262
+ root.set('state', state);
271
263
  });
272
264
  }
273
- else if (slots.length === 0) {
274
- rootComponent.updateState(() => {
275
- return root.get('state');
276
- });
277
- this.yDoc.transact(() => {
278
- rootComponent.slots.toArray().forEach(i => {
279
- const sharedSlot = this.createSharedSlotBySlot(i);
280
- slots.push([sharedSlot]);
265
+ else {
266
+ rootComponent.state.clean();
267
+ this.syncSharedMapToLocalMap(state, rootComponent.state, rootComponent);
268
+ }
269
+ }
270
+ syncSharedMapToLocalMap(sharedMap, localMap, parent) {
271
+ sharedMap.forEach((value, key) => {
272
+ localMap.set(key, this.createLocalModelBySharedByModel(value, parent));
273
+ });
274
+ this.syncObject(sharedMap, localMap, parent);
275
+ }
276
+ createLocalMapBySharedMap(sharedMap, parent) {
277
+ const localMap = new MapModel(parent);
278
+ this.syncSharedMapToLocalMap(sharedMap, localMap, parent);
279
+ return localMap;
280
+ }
281
+ createLocalArrayBySharedArray(sharedArray, parent) {
282
+ const localArray = new ArrayModel(parent);
283
+ sharedArray.forEach(item => {
284
+ localArray.insert(this.createLocalModelBySharedByModel(item, parent));
285
+ });
286
+ this.syncArray(sharedArray, localArray, parent);
287
+ return localArray;
288
+ }
289
+ syncLocalMapToSharedMap(localMap, sharedMap, parent) {
290
+ localMap.forEach((value, key) => {
291
+ sharedMap.set(key, this.createSharedModelByLocalModel(value, parent));
292
+ });
293
+ this.syncObject(sharedMap, localMap, parent);
294
+ }
295
+ createSharedMapByLocalMap(localMap, parent) {
296
+ const sharedMap = new Map();
297
+ this.syncLocalMapToSharedMap(localMap, sharedMap, parent);
298
+ return sharedMap;
299
+ }
300
+ createSharedArrayByLocalArray(localArray, parent) {
301
+ const sharedArray = new Array();
302
+ localArray.forEach(value => {
303
+ sharedArray.push([this.createSharedModelByLocalModel(value, parent)]);
304
+ });
305
+ this.syncArray(sharedArray, localArray, parent);
306
+ return sharedArray;
307
+ }
308
+ createSharedSlotByLocalSlot(localSlot) {
309
+ const sharedSlot = new Text();
310
+ sharedSlot.setAttribute('__schema__', [...localSlot.schema]);
311
+ let offset = 0;
312
+ localSlot.toDelta().forEach(i => {
313
+ let formats = {};
314
+ if (i.formats) {
315
+ i.formats.forEach(item => {
316
+ formats[item[0].name] = item[1];
281
317
  });
282
- });
318
+ }
319
+ else {
320
+ formats = null;
321
+ }
322
+ if (typeof i.insert === 'string') {
323
+ sharedSlot.insert(offset, i.insert, formats);
324
+ }
325
+ else {
326
+ const sharedComponent = this.createSharedComponentByLocalComponent(i.insert);
327
+ sharedSlot.insertEmbed(offset, sharedComponent, formats);
328
+ }
329
+ offset += i.insert.length;
330
+ });
331
+ localSlot.getAttributes().forEach(item => {
332
+ sharedSlot.setAttribute(item[0].name, item[1]);
333
+ });
334
+ this.syncSlot(sharedSlot, localSlot);
335
+ return sharedSlot;
336
+ }
337
+ createLocalSlotBySharedSlot(sharedSlot) {
338
+ const delta = sharedSlot.toDelta();
339
+ const localSlot = new Slot(sharedSlot.getAttribute('__schema__') || []); // TODO 这里有潜在的问题
340
+ const attrs = sharedSlot.getAttributes();
341
+ Object.keys(attrs).forEach(key => {
342
+ const attribute = this.registry.getAttribute(key);
343
+ if (attribute) {
344
+ localSlot.setAttribute(attribute, attrs[key]);
345
+ }
346
+ });
347
+ for (const action of delta) {
348
+ if (action.insert) {
349
+ if (typeof action.insert === 'string') {
350
+ const formats = remoteFormatsToLocal(this.registry, action.attributes);
351
+ localSlot.insert(action.insert, formats);
352
+ }
353
+ else {
354
+ const sharedComponent = action.insert;
355
+ const component = this.createLocalComponentBySharedComponent(sharedComponent);
356
+ localSlot.insert(component, remoteFormatsToLocal(this.registry, action.attributes));
357
+ }
358
+ }
359
+ else {
360
+ throw collaborateErrorFn('unexpected delta action.');
361
+ }
283
362
  }
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
- });
363
+ this.syncSlot(sharedSlot, localSlot);
364
+ return localSlot;
365
+ }
366
+ // private createSharedModelByLocalModel(sharedModel: YMap<any>, parent: Component<any>): MapModel<any>
367
+ // private createSharedModelByLocalModel(sharedModel: YArray<any>, parent: Component<any>): ArrayModel<any>
368
+ // private createSharedModelByLocalModel(sharedModel: YText, parent: Component<any>): Slot
369
+ // private createSharedModelByLocalModel(sharedModel: any, parent: Component<any>): any
370
+ createSharedModelByLocalModel(sharedModel, parent) {
371
+ if (sharedModel instanceof MapModel) {
372
+ return this.createSharedMapByLocalMap(sharedModel, parent);
373
+ }
374
+ if (sharedModel instanceof ArrayModel) {
375
+ return this.createSharedArrayByLocalArray(sharedModel, parent);
376
+ }
377
+ if (sharedModel instanceof Slot) {
378
+ return this.createSharedSlotByLocalSlot(sharedModel);
295
379
  }
296
- this.syncComponentState(root, rootComponent);
297
- this.syncComponentSlots(slots, rootComponent);
380
+ return sharedModel;
381
+ }
382
+ // private createLocalModelBySharedByModel(localModel: MapModel<any>, parent: Component<any>): MapModel<any>
383
+ // private createLocalModelBySharedByModel(localModel: ArrayModel<any>, parent: Component<any>): YArray<any>
384
+ // private createLocalModelBySharedByModel(localModel: Slot, parent: Component<any>): YText
385
+ // private createLocalModelBySharedByModel(localModel: any, parent: Component<any>): any
386
+ createLocalModelBySharedByModel(localModel, parent) {
387
+ if (localModel instanceof Map) {
388
+ return this.createLocalMapBySharedMap(localModel, parent);
389
+ }
390
+ if (localModel instanceof Array) {
391
+ return this.createLocalArrayBySharedArray(localModel, parent);
392
+ }
393
+ if (localModel instanceof Text) {
394
+ return this.createLocalSlotBySharedSlot(localModel);
395
+ }
396
+ return localModel;
298
397
  }
299
398
  getAbstractSelection(position) {
300
399
  const anchorPosition = createAbsolutePositionFromRelativePosition(position.anchor, this.yDoc);
301
400
  const focusPosition = createAbsolutePositionFromRelativePosition(position.focus, this.yDoc);
302
401
  if (anchorPosition && focusPosition) {
303
- const focusSlot = this.contentMap.get(focusPosition.type);
304
- const anchorSlot = this.contentMap.get(anchorPosition.type);
402
+ const focusSlot = this.slotMap.get(focusPosition.type);
403
+ const anchorSlot = this.slotMap.get(anchorPosition.type);
305
404
  if (focusSlot && anchorSlot) {
306
405
  return {
307
406
  anchorSlot,
@@ -316,11 +415,11 @@ let Collaborate = class Collaborate {
316
415
  getRelativeCursorLocation() {
317
416
  const { anchorSlot, anchorOffset, focusSlot, focusOffset } = this.selection;
318
417
  if (anchorSlot) {
319
- const anchorYText = this.contentMap.get(anchorSlot);
418
+ const anchorYText = this.slotMap.get(anchorSlot);
320
419
  if (anchorYText) {
321
420
  const anchorPosition = createRelativePositionFromTypeIndex(anchorYText, anchorOffset);
322
421
  if (focusSlot) {
323
- const focusYText = this.contentMap.get(focusSlot);
422
+ const focusYText = this.slotMap.get(focusSlot);
324
423
  if (focusYText) {
325
424
  const focusPosition = createRelativePositionFromTypeIndex(focusYText, focusOffset);
326
425
  return {
@@ -333,11 +432,10 @@ let Collaborate = class Collaborate {
333
432
  }
334
433
  return null;
335
434
  }
336
- syncSlotContent(content, slot) {
337
- this.contentMap.set(slot, content);
435
+ syncSlot(sharedSlot, localSlot) {
338
436
  const syncRemote = (ev, tr) => {
339
437
  this.runRemoteUpdate(tr, () => {
340
- slot.retain(0);
438
+ localSlot.retain(0);
341
439
  ev.keysChanged.forEach(key => {
342
440
  const change = ev.keys.get(key);
343
441
  if (!change) {
@@ -347,13 +445,13 @@ let Collaborate = class Collaborate {
347
445
  if (updateType === 'update' || updateType === 'add') {
348
446
  const attribute = this.registry.getAttribute(key);
349
447
  if (attribute) {
350
- slot.setAttribute(attribute, content.getAttribute(key));
448
+ localSlot.setAttribute(attribute, sharedSlot.getAttribute(key));
351
449
  }
352
450
  }
353
451
  else if (updateType === 'delete') {
354
452
  const attribute = this.registry.getAttribute(key);
355
453
  if (attribute) {
356
- slot.removeAttribute(attribute);
454
+ localSlot.removeAttribute(attribute);
357
455
  }
358
456
  }
359
457
  });
@@ -362,54 +460,52 @@ let Collaborate = class Collaborate {
362
460
  if (action.attributes) {
363
461
  const formats = remoteFormatsToLocal(this.registry, action.attributes);
364
462
  if (formats.length) {
365
- slot.retain(action.retain, formats);
463
+ localSlot.retain(action.retain, formats);
366
464
  }
367
- slot.retain(slot.index + action.retain);
465
+ localSlot.retain(localSlot.index + action.retain);
368
466
  }
369
467
  else {
370
- slot.retain(action.retain);
468
+ localSlot.retain(action.retain);
371
469
  }
372
470
  }
373
471
  else if (action.insert) {
374
- const index = slot.index;
472
+ const index = localSlot.index;
375
473
  let length = 1;
376
474
  if (typeof action.insert === 'string') {
377
475
  length = action.insert.length;
378
- slot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes));
476
+ localSlot.insert(action.insert, remoteFormatsToLocal(this.registry, action.attributes));
379
477
  }
380
478
  else {
381
479
  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);
480
+ const component = this.createLocalComponentBySharedComponent(sharedComponent);
481
+ localSlot.insert(component);
386
482
  }
387
483
  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);
484
+ if (localSlot === this.selection.anchorSlot && this.selection.anchorOffset > index) {
485
+ this.selection.setAnchor(localSlot, this.selection.anchorOffset + length);
390
486
  }
391
- if (slot === this.selection.focusSlot && this.selection.focusOffset > index) {
392
- this.selection.setFocus(slot, this.selection.focusOffset + length);
487
+ if (localSlot === this.selection.focusSlot && this.selection.focusOffset > index) {
488
+ this.selection.setFocus(localSlot, this.selection.focusOffset + length);
393
489
  }
394
490
  }
395
491
  }
396
492
  else if (action.delete) {
397
- const index = slot.index;
398
- slot.delete(action.delete);
493
+ const index = localSlot.index;
494
+ localSlot.delete(action.delete);
399
495
  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);
496
+ if (localSlot === this.selection.anchorSlot && this.selection.anchorOffset >= index) {
497
+ this.selection.setAnchor(localSlot, this.selection.startOffset - action.delete);
402
498
  }
403
- if (slot === this.selection.focusSlot && this.selection.focusOffset >= index) {
404
- this.selection.setFocus(slot, this.selection.focusOffset - action.delete);
499
+ if (localSlot === this.selection.focusSlot && this.selection.focusOffset >= index) {
500
+ this.selection.setFocus(localSlot, this.selection.focusOffset - action.delete);
405
501
  }
406
502
  }
407
503
  }
408
504
  });
409
505
  });
410
506
  };
411
- content.observe(syncRemote);
412
- const sub = slot.onContentChange.subscribe(actions => {
507
+ sharedSlot.observe(syncRemote);
508
+ const sub = localSlot.onContentChange.subscribe(actions => {
413
509
  this.runLocalUpdate(() => {
414
510
  var _a;
415
511
  let offset = 0;
@@ -428,170 +524,169 @@ let Collaborate = class Collaborate {
428
524
  }
429
525
  });
430
526
  if (length) {
431
- content.format(offset, action.offset, formats);
527
+ sharedSlot.format(offset, action.offset, formats);
432
528
  }
433
529
  }
434
530
  else {
435
531
  offset = action.offset;
436
532
  }
437
533
  }
438
- else if (action.type === 'insert') {
439
- const delta = content.toDelta();
534
+ else if (action.type === 'contentInsert') {
535
+ const delta = sharedSlot.toDelta();
440
536
  const isEmpty = delta.length === 1 && delta[0].insert === Slot.emptyPlaceholder;
441
537
  if (typeof action.content === 'string') {
442
538
  length = action.content.length;
443
- content.insert(offset, action.content, action.formats || {});
539
+ sharedSlot.insert(offset, action.content, action.formats || {});
444
540
  }
445
541
  else {
446
542
  length = 1;
447
- const sharedComponent = this.createSharedComponentByComponent(action.ref);
448
- content.insertEmbed(offset, sharedComponent, action.formats || {});
543
+ const sharedComponent = this.createSharedComponentByLocalComponent(action.ref);
544
+ sharedSlot.insertEmbed(offset, sharedComponent, action.formats || {});
449
545
  }
450
546
  if (isEmpty && offset === 0) {
451
- content.delete(content.length - 1, 1);
547
+ sharedSlot.delete(sharedSlot.length - 1, 1);
452
548
  }
453
549
  offset += length;
454
550
  }
455
551
  else if (action.type === 'delete') {
456
- const delta = content.toDelta();
457
- if (content.length) {
458
- content.delete(offset, action.count);
552
+ const delta = sharedSlot.toDelta();
553
+ if (sharedSlot.length) {
554
+ sharedSlot.delete(offset, action.count);
459
555
  }
460
- if (content.length === 0) {
461
- content.insert(0, '\n', (_a = delta[0]) === null || _a === void 0 ? void 0 : _a.attributes);
556
+ if (sharedSlot.length === 0) {
557
+ sharedSlot.insert(0, '\n', (_a = delta[0]) === null || _a === void 0 ? void 0 : _a.attributes);
462
558
  }
463
559
  }
464
560
  else if (action.type === 'attrSet') {
465
- content.setAttribute(action.name, action.value);
561
+ sharedSlot.setAttribute(action.name, action.value);
466
562
  }
467
- else if (action.type === 'attrRemove') {
468
- content.removeAttribute(action.name);
563
+ else if (action.type === 'attrDelete') {
564
+ sharedSlot.removeAttribute(action.name);
469
565
  }
470
566
  }
471
567
  });
472
568
  });
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);
569
+ this.slotMap.set(localSlot, sharedSlot);
570
+ localSlot.destroyCallbacks.push(() => {
571
+ this.slotMap.delete(localSlot);
572
+ sharedSlot.unobserve(syncRemote);
480
573
  sub.unsubscribe();
481
574
  });
482
575
  }
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);
492
- }
493
- else {
494
- return state;
576
+ createSharedComponentByLocalComponent(component) {
577
+ const sharedComponent = new Map();
578
+ const sharedState = this.createSharedMapByLocalMap(component.state, component);
579
+ sharedComponent.set('name', component.name);
580
+ sharedComponent.set('state', sharedState);
581
+ return sharedComponent;
582
+ }
583
+ createLocalComponentBySharedComponent(yMap) {
584
+ const componentName = yMap.get('name');
585
+ const sharedState = yMap.get('state');
586
+ const instance = this.registry.createComponentByData(componentName, (component) => {
587
+ return this.createLocalMapBySharedMap(sharedState, component);
588
+ });
589
+ if (instance) {
590
+ return instance;
591
+ }
592
+ throw collaborateErrorFn(`cannot find component factory \`${componentName}\`.`);
593
+ }
594
+ /**
595
+ * 双向同步数组
596
+ * @param sharedArray
597
+ * @param localArray
598
+ * @param parent
599
+ * @private
600
+ */
601
+ syncArray(sharedArray, localArray, parent) {
602
+ const sub = localArray.changeMarker.onChange.subscribe((op) => {
603
+ this.runLocalUpdate(() => {
604
+ let index = 0;
605
+ for (const action of op.apply) {
606
+ switch (action.type) {
607
+ case 'retain':
608
+ index = action.offset;
609
+ break;
610
+ case 'insert':
611
+ {
612
+ const data = this.createSharedModelByLocalModel(action.ref, parent);
613
+ sharedArray.insert(index, [data]);
495
614
  }
496
- });
615
+ break;
616
+ case 'delete':
617
+ sharedArray.delete(index, 1);
618
+ break;
497
619
  }
498
- });
620
+ }
499
621
  });
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
622
  });
507
- this.slotStateSyncCaches.set(slot, () => {
508
- remoteSlot.unobserve(syncRemote);
509
- sub.unsubscribe();
510
- });
511
- }
512
- syncComponentSlots(remoteSlots, component) {
513
- const slots = component.slots;
514
623
  const syncRemote = (ev, tr) => {
515
624
  this.runRemoteUpdate(tr, () => {
516
625
  let index = 0;
517
- slots.retain(index);
518
- ev.delta.forEach(action => {
626
+ localArray.retain(index);
627
+ ev.delta.forEach((action) => {
519
628
  if (Reflect.has(action, 'retain')) {
520
629
  index += action.retain;
521
- slots.retain(index);
630
+ localArray.retain(index);
522
631
  }
523
632
  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);
633
+ action.insert.forEach((item) => {
634
+ const value = this.createLocalModelBySharedByModel(item, parent);
635
+ localArray.insert(value);
529
636
  index++;
530
637
  });
531
638
  }
532
639
  else if (action.delete) {
533
- slots.retain(index);
534
- slots.delete(action.delete);
640
+ localArray.retain(index);
641
+ localArray.delete(action.delete);
535
642
  }
536
643
  });
537
644
  });
538
645
  };
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);
646
+ sharedArray.observe(syncRemote);
647
+ localArray.destroyCallbacks.push(() => {
566
648
  sub.unsubscribe();
649
+ sharedArray.unobserve(syncRemote);
567
650
  });
568
651
  }
569
- syncComponentState(remoteComponent, component) {
652
+ /**
653
+ * 双向同步对象
654
+ * @param sharedObject
655
+ * @param localObject
656
+ * @param parent
657
+ * @private
658
+ */
659
+ syncObject(sharedObject, localObject, parent) {
570
660
  const syncRemote = (ev, tr) => {
571
661
  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
- });
662
+ ev.changes.keys.forEach((item, key) => {
663
+ if (item.action === 'add' || item.action === 'update') {
664
+ const value = sharedObject.get(key);
665
+ localObject.set(key, this.createLocalModelBySharedByModel(value, parent));
666
+ }
667
+ else {
668
+ localObject.remove(key);
583
669
  }
584
670
  });
585
671
  });
586
672
  };
587
- remoteComponent.observe(syncRemote);
588
- const sub = component.onStateChange.subscribe(change => {
673
+ sharedObject.observe(syncRemote);
674
+ const sub = localObject.changeMarker.onChange.subscribe((op) => {
589
675
  this.runLocalUpdate(() => {
590
- remoteComponent.set('state', change.newState);
591
- }, change.record);
676
+ for (const action of op.apply) {
677
+ switch (action.type) {
678
+ case 'propSet':
679
+ sharedObject.set(action.key, this.createSharedModelByLocalModel(action.value, parent));
680
+ break;
681
+ case 'propDelete':
682
+ sharedObject.delete(action.key);
683
+ break;
684
+ }
685
+ }
686
+ });
592
687
  });
593
- this.componentStateSyncCaches.set(component, () => {
594
- remoteComponent.unobserve(syncRemote);
688
+ localObject.destroyCallbacks.push(function () {
689
+ sharedObject.unobserve(syncRemote);
595
690
  sub.unsubscribe();
596
691
  });
597
692
  }
@@ -617,140 +712,6 @@ let Collaborate = class Collaborate {
617
712
  }
618
713
  this.updateFromRemote = false;
619
714
  }
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
715
  };
755
716
  Collaborate = __decorate([
756
717
  Injectable(),