Skip to content

Commit 658dcc0

Browse files
aem2doc parsing now done in da-collab (#163)
* Remove the aem2prose handling Also cleans up old websockets on navigation --------- Co-authored-by: Karl Pauls <[email protected]>
1 parent 849e18b commit 658dcc0

File tree

5 files changed

+50
-152
lines changed

5 files changed

+50
-152
lines changed

blocks/edit/da-editor/da-editor.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ export default class DaEditor extends LitElement {
2222
initIms().then(() => { this._imsLoaded = true; });
2323
}
2424

25+
disconnectWebsocket() {
26+
if (this.wsProvider) {
27+
this.wsProvider.disconnect({ data: 'Client navigation' });
28+
this.wsProvider = undefined;
29+
}
30+
}
31+
32+
disconnectedCallback() {
33+
super.disconnectedCallback();
34+
this.disconnectWebsocket();
35+
}
36+
2537
async fetchVersion() {
2638
const resp = await daFetch(this.version);
2739
if (!resp.ok) return;
@@ -73,9 +85,10 @@ export default class DaEditor extends LitElement {
7385
updated(props) {
7486
if (!this._imsLoaded) return;
7587
if (!(props.has('version') || props.has('_versionDom'))) {
88+
this.disconnectWebsocket();
7689
const prose = this.shadowRoot.querySelector('.da-prose-mirror');
7790
prose.innerHTML = '';
78-
initProse({ editor: prose, path: this.path });
91+
this.wsProvider = initProse({ editor: prose, path: this.path });
7992
}
8093
}
8194
}

blocks/edit/prose/index.js

+13-58
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import {
33
EditorState,
44
EditorView,
5-
DOMParser,
65
Schema,
76
baseSchema,
87
history,
@@ -25,15 +24,13 @@ import {
2524
yUndoPlugin,
2625
yUndo,
2726
yRedo,
28-
prosemirrorToYXmlFragment,
2927
} from 'da-y-wrapper';
3028

3129
// DA
3230
import prose2aem from '../../shared/prose2aem.js';
3331
import menu from './plugins/menu.js';
3432
import imageDrop from './plugins/imageDrop.js';
3533
import linkConverter from './plugins/linkConverter.js';
36-
import { aem2prose, parse } from '../utils/helpers.js';
3734
import { COLLAB_ORIGIN, getDaAdmin } from '../../shared/constants.js';
3835
import { addLocNodes, getLocClass } from './loc-utils.js';
3936

@@ -58,6 +55,8 @@ function addCustomMarks(marks) {
5855
.addToEnd('contextHighlightingMark', contextHighlight);
5956
}
6057

58+
// Note: until getSchema() is separated in its own module, this function needs to be kept in-sync
59+
// with the getSchema() function in da-collab src/collab.js
6160
export function getSchema() {
6261
const { marks, nodes: baseNodes } = baseSchema.spec;
6362
const withLocNodes = addLocNodes(baseNodes);
@@ -101,18 +100,6 @@ function pollForUpdates() {
101100
}, 500);
102101
}
103102

104-
// Apply the document in AEM doc format to the editor.
105-
// For this it's converted to Prose and then applied to the current ydoc as an XML fragment
106-
function setAEMDocInEditor(aemDoc, yXmlFragment, schema) {
107-
const doc = parse(aemDoc);
108-
const pdoc = aem2prose(doc);
109-
const docc = document.createElement('div');
110-
docc.append(...pdoc);
111-
const parser = DOMParser.fromSchema(schema);
112-
const fin = parser.parse(docc);
113-
prosemirrorToYXmlFragment(fin, yXmlFragment);
114-
}
115-
116103
function handleAwarenessUpdates(wsProvider, daTitle, win) {
117104
const users = new Set();
118105

@@ -148,45 +135,13 @@ export function createAwarenessStatusWidget(wsProvider, win) {
148135
return daTitle;
149136
}
150137

151-
export function handleYDocUpdates({
152-
daTitle, editor, ydoc, path, schema, wsProvider, yXmlFragment, fnInitProse,
153-
}, win = window, fnSetAEMDocInEditor = setAEMDocInEditor) {
154-
let firstUpdate = true;
155-
ydoc.on('update', (_, originWS) => {
156-
if (firstUpdate) {
157-
firstUpdate = false;
158-
159-
// Do the following async to allow the ydoc to init itself with any
160-
// changes coming from other editors
161-
setTimeout(() => {
162-
const aemMap = ydoc.getMap('aem');
163-
const current = aemMap.get('content');
164-
const inital = aemMap.get('initial');
165-
if (!current && inital) {
166-
fnSetAEMDocInEditor(inital, yXmlFragment, schema);
167-
}
168-
}, 1);
169-
}
170-
171-
const serverInvKey = 'svrinv';
172-
const svrUpdate = ydoc.getMap('aem').get(serverInvKey);
173-
if (svrUpdate) {
174-
// push update from the server: re-init document
175-
delete daTitle.collabStatus;
176-
delete daTitle.collabUsers;
177-
ydoc.destroy();
178-
wsProvider.destroy();
179-
editor.innerHTML = '';
180-
fnInitProse({ editor, path });
181-
return;
182-
}
183-
184-
if (originWS && originWS !== wsProvider) {
185-
const proseEl = win.view.root.querySelector('.ProseMirror');
186-
const clone = proseEl.cloneNode(true);
187-
const aem = prose2aem(clone);
188-
const aemMap = ydoc.getMap('aem');
189-
aemMap.set('content', aem);
138+
function registerErrorHandler(ydoc) {
139+
ydoc.on('update', () => {
140+
const errorMap = ydoc.getMap('error');
141+
if (errorMap && errorMap.size > 0) {
142+
// eslint-disable-next-line no-console
143+
console.log('Error from server', JSON.stringify(errorMap));
144+
errorMap.clear();
190145
}
191146
});
192147
}
@@ -226,12 +181,10 @@ export default function initProse({ editor, path }) {
226181
}
227182

228183
const wsProvider = new WebsocketProvider(server, roomName, ydoc, opts);
229-
const daTitle = createAwarenessStatusWidget(wsProvider, window);
184+
createAwarenessStatusWidget(wsProvider, window);
185+
registerErrorHandler(ydoc);
230186

231187
const yXmlFragment = ydoc.getXmlFragment('prosemirror');
232-
handleYDocUpdates({
233-
daTitle, editor, ydoc, path, schema, wsProvider, yXmlFragment, fnInitProse: initProse,
234-
});
235188

236189
if (window.adobeIMS?.isSignedInUser()) {
237190
window.adobeIMS.getProfile().then(
@@ -310,4 +263,6 @@ export default function initProse({ editor, path }) {
310263

311264
document.execCommand('enableObjectResizing', false, 'false');
312265
document.execCommand('enableInlineTableEditing', false, 'false');
266+
267+
return wsProvider;
313268
}

blocks/shared/constants.js

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const DA_ADMIN_ENVS = {
2121

2222
const DA_COLLAB_ENVS = {
2323
local: 'ws://localhost:4711',
24+
stage: 'wss://stage-collab.da.live',
2425
prod: 'wss://collab.da.live',
2526
};
2627

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { expect } from '@esm-bundle/chai';
2+
3+
// This is needed to make a dynamic import work that is indirectly referenced
4+
// from da-editor.js
5+
const { setNx } = await import('../../../../../scripts/utils.js');
6+
setNx('/bheuaark/', { hostname: 'localhost' });
7+
8+
const { default: DaEditor } = await import('../../../../../blocks/edit/da-editor/da-editor.js');
9+
10+
describe('da-editor', () => {
11+
it('Test wsprovider disconnectedcallback', async () => {
12+
const ed = new DaEditor();
13+
14+
const called = [];
15+
const mockWSProvider = { disconnect: () => called.push('disconnect') };
16+
17+
ed.wsProvider = mockWSProvider;
18+
ed.disconnectWebsocket();
19+
expect(ed.wsProvider).to.be.undefined;
20+
expect(called).to.deep.equal(['disconnect']);
21+
});
22+
});

test/unit/blocks/edit/proseCollab.test.js

-93
Original file line numberDiff line numberDiff line change
@@ -73,97 +73,4 @@ describe('Prose collab', () => {
7373
awarenessOnCalled[0].f(delta2); // Call the callback function
7474
expect(daTitle.collabUsers).to.deep.equal(['Anonymous', 'Anonymous', 'Joe Bloggs']);
7575
});
76-
77-
it('Test YDoc firstUpdate callback', (done) => {
78-
const ydocMap = new Map();
79-
ydocMap.set('initial', 'Some intial text');
80-
81-
const ydocOnCalls = [];
82-
const ydoc = {
83-
getMap: (n) => (n === 'aem' ? ydocMap : null),
84-
on: (n, f) => ydocOnCalls.push({ n, f }),
85-
};
86-
87-
const setAEMDocCalls = [];
88-
const fnSetAEMDoc = () => setAEMDocCalls.push('called');
89-
90-
pi.handleYDocUpdates({
91-
daTitle: {},
92-
editor: {},
93-
ydoc,
94-
path: {},
95-
schema: {},
96-
wsProvider: {},
97-
yXmlFragment: {},
98-
fnInitProse: () => {},
99-
}, {}, fnSetAEMDoc);
100-
expect(ydocOnCalls.length).to.equal(1);
101-
expect(ydocOnCalls[0].n).to.equal('update');
102-
103-
ydocOnCalls[0].f();
104-
setTimeout(() => {
105-
expect(setAEMDocCalls).to.deep.equal(['called']);
106-
107-
// the function call again, it should not perform any action this time
108-
ydocOnCalls[0].f();
109-
setTimeout(() => {
110-
expect(setAEMDocCalls).to.deep.equal(
111-
['called'],
112-
'First update code should only be called once',
113-
);
114-
done();
115-
}, 200);
116-
}, 200);
117-
});
118-
119-
it('Test YDoc server update callback', () => {
120-
const daTitle = {
121-
collabStatus: 'yeah',
122-
collabUsers: 'some',
123-
};
124-
const editor = {};
125-
126-
const ydocMap = new Map();
127-
ydocMap.set('svrinv', 'Some svrinv text');
128-
129-
const ydocCalls = [];
130-
const ydocOnCalls = [];
131-
const ydoc = {
132-
getMap: (n) => (n === 'aem' ? ydocMap : null),
133-
destroy: () => ydocCalls.push('destroy'),
134-
on: (n, f) => ydocOnCalls.push({ n, f }),
135-
};
136-
137-
const wspCalls = [];
138-
const wsp = { destroy: () => wspCalls.push('destroy') };
139-
140-
const initProseCalls = [];
141-
const mockInitProse = () => initProseCalls.push('init');
142-
143-
pi.handleYDocUpdates({
144-
daTitle,
145-
editor,
146-
ydoc,
147-
path: {},
148-
schema: {},
149-
wsProvider: wsp,
150-
yXmlFragment: {},
151-
fnInitProse: mockInitProse,
152-
}, {}, () => {});
153-
expect(ydocOnCalls.length).to.equal(1);
154-
expect(ydocOnCalls[0].n).to.equal('update');
155-
156-
expect(daTitle.collabStatus).to.equal('yeah', 'Precondition');
157-
expect(daTitle.collabUsers).to.equal('some', 'Precondition');
158-
159-
// Calls server invalidation
160-
ydocOnCalls[0].f();
161-
162-
expect(daTitle.collabStatus).to.be.undefined;
163-
expect(daTitle.collabUsers).to.be.undefined;
164-
expect(ydocCalls).to.deep.equal(['destroy']);
165-
expect(wspCalls).to.deep.equal(['destroy']);
166-
expect(initProseCalls).to.deep.equal(['init']);
167-
expect(editor.innerHTML).to.equal('');
168-
});
16976
});

0 commit comments

Comments
 (0)