Skip to content

Commit d1d6920

Browse files
authored
Merge pull request #7371 from nextcloud/migrate/editor-to-setup-function
Migrate editor instantiation to setup function
2 parents fc96fc5 + 4a49f73 commit d1d6920

File tree

2 files changed

+83
-82
lines changed

2 files changed

+83
-82
lines changed

src/components/Editor.vue

Lines changed: 80 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
</template>
8282

8383
<script>
84-
import Vue, { ref, set, watch } from 'vue'
84+
import Vue, { ref, set, shallowRef, watch } from 'vue'
8585
import { getCurrentUser } from '@nextcloud/auth'
8686
import { loadState } from '@nextcloud/initial-state'
8787
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
@@ -135,6 +135,7 @@ import { useDelayedFlag } from './Editor/useDelayedFlag.ts'
135135
import { useEditorMethods } from '../composables/useEditorMethods.ts'
136136
import { useSyntaxHighlighting } from '../composables/useSyntaxHighlighting.ts'
137137
import { provideConnection } from '../composables/useConnection.ts'
138+
import { Awareness } from 'y-protocols/awareness.js'
138139
139140
export default {
140141
name: 'Editor',
@@ -163,7 +164,7 @@ export default {
163164
// actual values without being reactive
164165
Object.defineProperties(val, {
165166
[SYNC_SERVICE]: {
166-
get: () => this.$syncService,
167+
get: () => this.syncService,
167168
},
168169
[FILE]: {
169170
get: () => this.fileData,
@@ -237,6 +238,7 @@ export default {
237238
el.value.style.setProperty('--widget-full-width', `${maxWidth}px`)
238239
})
239240
const ydoc = new Doc()
241+
const awareness = new Awareness(ydoc)
240242
// Wrap the connection in an object so we can hand it to the Mention extension as a ref.
241243
const wrappedConnection = provideConnection()
242244
const hasConnectionIssue = ref(false)
@@ -248,19 +250,62 @@ export default {
248250
isRichEditor,
249251
props,
250252
)
253+
const baseVersionEtag = shallowRef(null)
254+
const syncService = shallowRef(null)
255+
const connectSyncService = () => {
256+
const guestName = localStorage.getItem('nick') ?? ''
257+
const api = new SessionApi({
258+
guestName,
259+
shareToken: props.shareToken,
260+
filePath: props.relativePath,
261+
})
262+
syncService.value = new SyncService({
263+
api,
264+
baseVersionEtag: baseVersionEtag.value,
265+
serialize: isRichEditor.value
266+
? (content) =>
267+
createMarkdownSerializer(editor.value?.schema).serialize(
268+
content ?? editor.value?.state.doc,
269+
)
270+
: (content) =>
271+
serializePlainText(content ?? editor.value?.state.doc),
272+
getDocumentState: () => getDocumentState(ydoc),
273+
})
274+
}
275+
const syncProvider = shallowRef(null)
276+
277+
const extensions = [
278+
Autofocus.configure({ fileId: props.fileId }),
279+
Collaboration.configure({ document: ydoc }),
280+
CollaborationCursor.configure({ provider: { awareness } }),
281+
]
282+
editor.value = isRichEditor
283+
? createRichEditor({
284+
...wrappedConnection,
285+
relativePath: props.relativePath,
286+
extensions,
287+
isEmbedded: props.isEmbedded,
288+
})
289+
: createPlainEditor({ language, extensions })
290+
251291
return {
252-
wrappedConnection,
292+
awareness,
293+
baseVersionEtag,
294+
connectSyncService,
295+
editor,
253296
el,
254-
width,
255297
hasConnectionIssue,
256-
requireReconnect,
257-
editor,
258-
setEditable,
259298
isPublic,
260299
isRichEditor,
261300
isRichWorkspace,
262301
language,
263302
lowlightLoaded,
303+
requireReconnect,
304+
setEditable,
305+
syncProvider,
306+
syncService,
307+
width,
308+
wrappedConnection,
264309
ydoc,
265310
}
266311
},
@@ -388,24 +433,9 @@ export default {
388433
// console.debug('ydoc update', update, origin, doc, tr)
389434
// Y.logUpdate(update)
390435
// });
391-
this.$providers = []
392-
this.$syncService = null
393436
this.$attachmentResolver = null
394437
if (this.active && this.hasDocumentParameters) {
395438
this.initSession()
396-
const extensions = [
397-
Autofocus.configure({ fileId: this.fileId }),
398-
Collaboration.configure({ document: this.ydoc }),
399-
CollaborationCursor.configure({ provider: this.$providers[0] }),
400-
]
401-
this.editor = this.isRichEditor
402-
? createRichEditor({
403-
...this.wrappedConnection,
404-
relativePath: this.relativePath,
405-
extensions,
406-
isEmbedded: this.isEmbedded,
407-
})
408-
: createPlainEditor({ language: this.language, extensions })
409439
this.listenEditorEvents()
410440
}
411441
},
@@ -420,50 +450,23 @@ export default {
420450
unsubscribe('text:translate-modal:show', this.showTranslateModal)
421451
if (this.dirty) {
422452
const timeout = new Promise((resolve) => setTimeout(resolve, 2000))
423-
await Promise.any([timeout, this.$syncService.save()])
453+
await Promise.any([timeout, this.syncService.save()])
424454
}
425455
await this.close()
426456
removeFromDebugging(this)
427457
},
428458
methods: {
429459
initSession() {
430-
if (!this.hasDocumentParameters) {
431-
this.emit('error', 'No valid file provided')
432-
return
433-
}
434-
const guestName = localStorage.getItem('nick')
435-
? localStorage.getItem('nick')
436-
: ''
437-
438-
const api = new SessionApi({
439-
guestName,
440-
shareToken: this.shareToken,
441-
filePath: this.relativePath,
442-
})
443-
444-
this.$syncService = new SyncService({
445-
api,
446-
baseVersionEtag: this.$baseVersionEtag,
447-
serialize: this.isRichEditor
448-
? (content) =>
449-
createMarkdownSerializer(this.editor?.schema).serialize(
450-
content ?? this.editor?.state.doc,
451-
)
452-
: (content) =>
453-
serializePlainText(content ?? this.editor?.state.doc),
454-
getDocumentState: () => getDocumentState(this.ydoc),
455-
})
456-
460+
this.connectSyncService()
457461
this.listenSyncServiceEvents()
458-
459-
const syncServiceProvider = createSyncServiceProvider({
462+
this.syncProvider = createSyncServiceProvider({
460463
ydoc: this.ydoc,
461-
syncService: this.$syncService,
464+
syncService: this.syncService,
462465
fileId: this.fileId,
463466
initialSession: this.initialSession,
464467
disableBC: true,
468+
awareness: this.awareness,
465469
})
466-
this.$providers.push(syncServiceProvider)
467470
},
468471
469472
listenEditorEvents() {
@@ -481,7 +484,7 @@ export default {
481484
},
482485
483486
listenSyncServiceEvents() {
484-
this.$syncService
487+
this.syncService
485488
.on('opened', this.onOpened)
486489
.on('change', this.onChange)
487490
.on('loaded', this.onLoaded)
@@ -493,7 +496,7 @@ export default {
493496
},
494497
495498
unlistenSyncServiceEvents() {
496-
this.$syncService
499+
this.syncService
497500
.off('opened', this.onOpened)
498501
.off('change', this.onChange)
499502
.off('loaded', this.onLoaded)
@@ -567,7 +570,7 @@ export default {
567570
this.editMode = !document.readOnly && !this.openReadOnlyEnabled
568571
569572
this.setEditable(this.editMode)
570-
this.lock = this.$syncService.lock
573+
this.lock = this.syncService.lock
571574
localStorage.setItem('nick', this.currentSession.guestName)
572575
this.$attachmentResolver = new AttachmentResolver({
573576
session: this.currentSession,
@@ -594,15 +597,15 @@ export default {
594597
onLoaded({ document, documentSource, documentState }) {
595598
// Fetch the document state after syntax highlights are loaded
596599
this.lowlightLoaded.then(() => {
597-
this.$syncService.startSync()
600+
this.syncService.startSync()
598601
if (!documentState) {
599602
setInitialYjsState(this.ydoc, documentSource, {
600603
isRichEditor: this.isRichEditor,
601604
})
602605
}
603606
})
604607
605-
this.$baseVersionEtag = document.baseVersionEtag
608+
this.baseVersionEtag = document.baseVersionEtag
606609
this.hasConnectionIssue = false
607610
608611
const session = this.currentSession
@@ -628,28 +631,28 @@ export default {
628631
},
629632
630633
onCreate({ editor }) {
631-
const proseMirrorMarkdown = this.$syncService.serialize(editor.state.doc)
634+
const proseMirrorMarkdown = this.syncService.serialize(editor.state.doc)
632635
this.emit('create:content', {
633636
markdown: proseMirrorMarkdown,
634637
})
635638
},
636639
637640
onUpdate({ editor }) {
638641
// this.debugContent(editor)
639-
const proseMirrorMarkdown = this.$syncService.serialize(editor.state.doc)
642+
const proseMirrorMarkdown = this.syncService.serialize(editor.state.doc)
640643
this.emit('update:content', {
641644
markdown: proseMirrorMarkdown,
642645
})
643646
},
644647
645648
onSync({ steps, document }) {
646649
this.hasConnectionIssue =
647-
this.$syncService.backend.fetcher === 0
648-
|| !this.$providers[0].wsconnected
649-
|| this.$syncService.pushError > 0
650-
if (this.$syncService.pushError > 0) {
650+
this.syncService.backend.fetcher === 0
651+
|| !this.syncProvider?.wsconnected
652+
|| this.syncService.pushError > 0
653+
if (this.syncService.pushError > 0) {
651654
// successfully received steps - so let's try and also push
652-
this.$syncService.sendStepsNow()
655+
this.syncService.sendStepsNow()
653656
}
654657
this.$nextTick(() => {
655658
this.emit('sync-service:sync')
@@ -712,14 +715,14 @@ export default {
712715
if (this.editor.can().undo() || this.editor.can().redo()) {
713716
this.dirty = state.dirty
714717
if (this.dirty) {
715-
this.$syncService.autosave()
718+
this.syncService.autosave()
716719
}
717720
}
718721
}
719722
},
720723
721724
onIdle() {
722-
this.$syncService.close()
725+
this.syncService.close()
723726
this.idle = true
724727
this.readOnly = true
725728
this.editMode = false
@@ -749,7 +752,7 @@ export default {
749752
},
750753
751754
onKeyboardSave() {
752-
this.$syncService.save()
755+
this.syncService.save()
753756
},
754757
755758
onAddImageNode() {
@@ -761,21 +764,19 @@ export default {
761764
},
762765
763766
async save() {
764-
await this.$syncService.save()
767+
await this.syncService.save()
765768
},
766769
767770
async disconnect() {
768-
await this.$syncService.close()
771+
await this.syncService.close()
769772
this.unlistenSyncServiceEvents()
770-
this.$providers.forEach((p) => p?.destroy())
771-
this.$providers = []
772-
this.$syncService = null
773+
this.syncProvider?.destroy()
773774
// disallow editing while still showing the content
774775
this.readOnly = true
775776
},
776777
777778
async close() {
778-
await this.$syncService
779+
await this.syncService
779780
.sendRemainingSteps()
780781
.catch((err) =>
781782
logger.warn('Failed to send remaining steps', { err }),
@@ -787,12 +788,10 @@ export default {
787788
try {
788789
this.unlistenEditorEvents()
789790
this.editor.destroy()
790-
this.editor = undefined
791791
} catch (error) {
792792
logger.warn('Failed to destroy editor', { error })
793793
}
794794
}
795-
return true
796795
},
797796
798797
/**
@@ -829,7 +828,7 @@ export default {
829828
* @param {object} editor The Tiptap editor
830829
*/
831830
debugContent(editor) {
832-
const proseMirrorMarkdown = this.$syncService.serialize(editor.state.doc)
831+
const proseMirrorMarkdown = this.syncService.serialize(editor.state.doc)
833832
const markdownItHtml = markdownit.render(proseMirrorMarkdown)
834833
835834
logger.debug(
@@ -852,7 +851,7 @@ export default {
852851
clientId: this.ydoc.clientID,
853852
pendingStructs: this.ydoc.store.pendingStructs,
854853
clientVectors: [],
855-
documentState: this.$syncService?.getDocumentState(),
854+
documentState: this.syncService?.getDocumentState(),
856855
}
857856
for (const client of this.ydoc.store.clients.values()) {
858857
yjsData.clientVectors.push(client.at(-1).id)
@@ -867,7 +866,7 @@ export default {
867866
868867
readOnlyToggled() {
869868
if (this.editMode) {
870-
this.$syncService.save()
869+
this.syncService.save()
871870
}
872871
this.editMode = !this.editMode
873872
this.setEditable(this.editMode)
@@ -916,7 +915,7 @@ export default {
916915
},
917916
918917
saveBeforeUnload() {
919-
this.$syncService?.saveViaSendBeacon()
918+
this.syncService?.saveViaSendBeacon()
920919
},
921920
},
922921
}

src/services/SyncServiceProvider.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ import { logger } from '../helpers/logger.js'
1616
* @param {number} options.queue - queue for outgoing steps
1717
* @param {object} options.initialSession - initialSession to start from
1818
* @param {boolean} options.disableBc - disable broadcast channel synchronization (default: disabled in debug mode, enabled otherwise)
19+
* @param {object} options.awareness - awareness instance for the provider
1920
*/
2021
export default function createSyncServiceProvider({
2122
ydoc,
2223
syncService,
2324
fileId,
2425
initialSession,
2526
queue,
27+
awareness,
2628
disableBc,
2729
}) {
2830
if (!fileId) {
@@ -40,7 +42,7 @@ export default function createSyncServiceProvider({
4042
'ws://localhost:1234',
4143
'file:' + fileId,
4244
ydoc,
43-
{ WebSocketPolyfill, disableBc },
45+
{ WebSocketPolyfill, awareness, disableBc },
4446
)
4547
websocketProvider.on('status', (event) => logger.debug('status', event))
4648
return websocketProvider

0 commit comments

Comments
 (0)