Skip to content

Commit

Permalink
Merge branch 'collab-performance' into 'main'
Browse files Browse the repository at this point in the history
Throttle MDE update events

See merge request reportcreator/reportcreator!562
  • Loading branch information
MWedl committed May 27, 2024
2 parents b749317 + 98c1272 commit dfeb7d6
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Project history diff views: add revert changes button to markdown editor
* Send update_text events with text diff when updating text fields via API instead of overwriting the whole text
* Fix MDE preview layout break on zoom out
* Throttle MDE update events to prevent browser from hanging
* Fix elastic APM tracing middleware always enabled


Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/EditToolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ const savingInProgress = ref(false);
const deletingInProgress = ref(false);
const actionInProgress = computed(() => savingInProgress.value || deletingInProgress.value);
const previousData = ref<T | null>(null);
const previousData = shallowRef<T | null>(null);
const isDestroying = ref(false);
const lockingInProgress = ref(false);
const lockInfo = ref<LockInfo | null>(null);
Expand Down
87 changes: 58 additions & 29 deletions frontend/src/composables/markdown.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { v4 as uuid4 } from 'uuid';
import type { PropType } from "vue";
import throttle from "lodash/throttle";
import {
createEditorExtensionToggler,
EditorState, EditorView, ViewUpdate,
Expand Down Expand Up @@ -181,6 +182,57 @@ export function useMarkdownEditorBase(options: {
const editorActions = ref<{[key: string]: (enabled: boolean) => void}>({});
const eventBusBeforeApplyRemoteTextChanges = useEventBus('collab:beforeApplyRemoteTextChanges');

const pendingViewUpdateEvents = shallowRef<ViewUpdate[]>([]);
const sendUpdateEventsThrottled = throttle(() => {
// Throttle and batch update events to prevent the UI from hanging.
// Updating reactive data in stores is costly and slow.

const pendingUpdates = [...pendingViewUpdateEvents.value];
pendingViewUpdateEvents.value = [];
if (pendingUpdates.length === 0) {
return;
}

let newModelValue = valueNotNull.value;
let newSelection = null;
const newTextUpdates = [];
for (const viewUpdate of pendingUpdates) {
for (const tr of viewUpdate.transactions) {
if (tr.docChanged && !tr.annotation(Transaction.remote)) {
newTextUpdates.push({ changes: tr.changes, selection: tr.selection });
}
}
if (viewUpdate.docChanged) {
newModelValue = viewUpdate.state.doc.toString();
}

if (viewUpdate.selectionSet || viewUpdate.focusChanged) {
newSelection = { changed: true, selection: viewUpdate.state.selection };
}
}

if (options.props.value.collab && newTextUpdates.length > 0) {
options.emit('collab', {
type: CollabEventType.UPDATE_TEXT,
path: options.props.value.collab.path,
updates: newTextUpdates,
});
}
if (newModelValue !== valueNotNull.value) {
options.emit('update:modelValue', newModelValue);
}
if (options.props.value.collab && newSelection?.changed) {
// Collab awareness updates
const hasFocus = options.editorView.value?.hasFocus || false;
options.emit('collab', {
type: CollabEventType.AWARENESS,
path: options.props.value.collab.path,
focus: hasFocus,
selection: newSelection.selection,
});
}
}, 500, { leading: true })

function createEditorStateConfig() {
return {
doc: valueNotNull.value,
Expand All @@ -194,36 +246,12 @@ export function useMarkdownEditorBase(options: {
focus: (event: FocusEvent) => options.emit('focus', event),
}),
EditorView.updateListener.of((viewUpdate: ViewUpdate) => {
editorState.value = viewUpdate.state;
// https://discuss.codemirror.net/t/codemirror-6-proper-way-to-listen-for-changes/2395/11
if (viewUpdate.docChanged && viewUpdate.state.doc.toString() !== valueNotNull.value) {
// Collab updates
if (options.props.value.collab) {
for (const tr of viewUpdate.transactions) {
if (tr.docChanged && !tr.annotation(Transaction.remote)) {
options.emit('collab', {
type: CollabEventType.UPDATE_TEXT,
path: options.props.value.collab.path,
updates: [{ changes: tr.changes, selection: tr.selection }],
});
}
}
}

// Model-value updates
options.emit('update:modelValue', viewUpdate.state.doc.toString());
}
editorState.value = viewUpdate.state;

if (options.props.value.collab && (viewUpdate.selectionSet || viewUpdate.focusChanged)) {
// Collab awareness updates
const hasFocus = options.editorView.value?.hasFocus || false;
options.emit('collab', {
type: CollabEventType.AWARENESS,
path: options.props.value.collab.path,
focus: hasFocus,
selection: viewUpdate.state.selection,
});
}
// Throttle update events
pendingViewUpdateEvents.value.push(viewUpdate);
sendUpdateEventsThrottled();
}),
remoteSelection(),
]
Expand Down Expand Up @@ -279,6 +307,7 @@ export function useMarkdownEditorBase(options: {
editorActions.value.darkTheme(theme.current.value.dark);
} else if (!newValue) {
eventBusBeforeApplyRemoteTextChanges.off(onBeforeApplyRemoteTextChange);
sendUpdateEventsThrottled.flush();
}
}, { immediate: true });
onBeforeUnmount(() => {
Expand Down Expand Up @@ -334,7 +363,7 @@ export function useMarkdownEditorBase(options: {
setRemoteClients.of(remoteClients)
]
})
}, { deep: true });
});

function focus() {
if (options.editorView.value) {
Expand Down
2 changes: 1 addition & 1 deletion packages/markdown/editor/spellcheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export function spellcheck({ performSpellcheckRequest, performSpellcheckAddWordR
}] : []),
};
});
}, {delay: 750});
}, {delay: 1000});
}


Expand Down

0 comments on commit dfeb7d6

Please sign in to comment.