diff options
author | Julius Härtl <jus@bitgrid.net> | 2021-01-23 19:32:53 +0300 |
---|---|---|
committer | Julius Härtl <jus@bitgrid.net> | 2021-01-26 10:34:18 +0300 |
commit | 7c7623222aeffdbf7ed2f513592d63b243f9dcbb (patch) | |
tree | cc100a49d65563ff83806f0e0fda28422590853e /src | |
parent | 886fd14249a02659b482b1aa6ba2b540bc9360e7 (diff) |
Track authors by step rather than transaction
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Diffstat (limited to 'src')
-rw-r--r-- | src/components/EditorWrapper.vue | 22 | ||||
-rw-r--r-- | src/components/SessionList.vue | 2 | ||||
-rw-r--r-- | src/extensions/UserColor.js | 14 | ||||
-rw-r--r-- | src/extensions/tracking/TrackState.js | 59 | ||||
-rw-r--r-- | src/extensions/tracking/models.js | 14 | ||||
-rw-r--r-- | src/mixins/store.js | 5 | ||||
-rw-r--r-- | src/services/PollingBackend.js | 2 | ||||
-rw-r--r-- | src/store.js | 7 |
8 files changed, 49 insertions, 76 deletions
diff --git a/src/components/EditorWrapper.vue b/src/components/EditorWrapper.vue index 9cea69aef..50c1ebf4c 100644 --- a/src/components/EditorWrapper.vue +++ b/src/components/EditorWrapper.vue @@ -74,7 +74,6 @@ import Vue from 'vue' import escapeHtml from 'escape-html' import moment from '@nextcloud/moment' -import { mapState } from 'vuex' import { SyncService, ERROR_TYPE, IDLE_TIMEOUT } from './../services/SyncService' import { endpointUrl, getRandomGuestName } from './../helpers' @@ -175,9 +174,9 @@ export default { } }, computed: { - ...mapState({ - showAuthorAnnotations: state => state.showAuthorAnnotations, - }), + showAuthorAnnotations() { + return this.$store.state.showAuthorAnnotations + }, lastSavedStatus() { let status = (this.dirtyStateIndicator ? '*' : '') if (!this.isMobile) { @@ -342,7 +341,7 @@ export default { steps.map(item => Step.fromJSON(schema, item.step)), steps.map(item => item.clientID), ) - tr.setMeta('clientID', steps[0].clientID) + tr.setMeta('clientID', steps.map(item => item.clientID)) view.dispatch(tr) }, }), @@ -379,14 +378,11 @@ export default { .on('sync', ({ steps, document }) => { this.hasConnectionIssue = false try { - for (let i = 0; i < steps.length; i++) { - // FIXME: seems pretty bad performance wise (maybe grouping the steps by user in the backend would be good) - this.tiptap.extensions.options.collaboration.update({ - version: document.currentVersion, - steps: [steps[i]], - editor: this.tiptap, - }) - } + this.tiptap.extensions.options.collaboration.update({ + version: document.currentVersion, + steps, + editor: this.tiptap, + }) this.syncService.state = this.tiptap.state this.updateLastSavedStatus() } catch (e) { diff --git a/src/components/SessionList.vue b/src/components/SessionList.vue index a05c25a6c..1415057dc 100644 --- a/src/components/SessionList.vue +++ b/src/components/SessionList.vue @@ -109,7 +109,7 @@ export default { return this.$store.state.showAuthorAnnotations }, set(value) { - this.$store.commit('setShowAuthorAnnotations', value) + this.$store.dispatch('setShowAuthorAnnotations', value) }, }, editorsTooltip() { diff --git a/src/extensions/UserColor.js b/src/extensions/UserColor.js index e3e4350c1..6ae68a0e6 100644 --- a/src/extensions/UserColor.js +++ b/src/extensions/UserColor.js @@ -60,20 +60,16 @@ export default class UserColor extends Extension { let { tracked, decos } = instance let tState = this.getState(oldState).tracked if (tr.docChanged) { + if (!tr.getMeta('clientID')) { + // we have an undefined client id for own transactions + tr.setMeta('clientID', tr.steps.map(i => this.spec.clientID)) + } tracked = tracked.applyTransform(tr) - const clientID = tr.getMeta('clientID') ? tr.getMeta('clientID') : this.spec.clientID - tracked = tracked.applyCommit(clientID, new Date(tr.time), { - clientID, - color: this.spec.color(clientID), - name: this.spec.name(clientID), - }) tState = tracked } decos = tState.blameMap - .filter(span => typeof tState.commits[span.commit]?.author?.color !== 'undefined') .map(span => { - const commit = tState.commits[span.commit] - const clientID = commit.author.clientID + const clientID = span.author return Decoration.inline(span.from, span.to, { class: 'author-annotation', style: 'background-color: ' + this.spec.color(clientID) + '66;', diff --git a/src/extensions/tracking/TrackState.js b/src/extensions/tracking/TrackState.js index 6a6d33d57..8193ecacb 100644 --- a/src/extensions/tracking/TrackState.js +++ b/src/extensions/tracking/TrackState.js @@ -20,32 +20,34 @@ * */ -import { Span, Commit } from './models' +import { Span } from './models' /* * This code is heavily inspired by the change tracking example of prosemirror * https://github.com/ProseMirror/website/blob/master/example/track/index.js */ -function updateBlameMap(map, transform, id) { - const result = []; const mapping = transform.mapping +function updateBlameMap(map, transform, clientIDs) { + const result = [] + const mapping = transform.mapping for (let i = 0; i < map.length; i++) { const span = map[i] - const from = mapping.map(span.from, 1); const to = mapping.map(span.to, -1) - if (from < to) result.push(new Span(from, to, span.commit)) + const from = mapping.map(span.from, 1) + const to = mapping.map(span.to, -1) + if (from < to) result.push(new Span(from, to, span.author)) } for (let i = 0; i < mapping.maps.length; i++) { const map = mapping.maps[i]; const after = mapping.slice(i + 1) map.forEach((_s, _e, start, end) => { - insertIntoBlameMap(result, after.map(start, 1), after.map(end, -1), id) + insertIntoBlameMap(result, after.map(start, 1), after.map(end, -1), clientIDs[i]) }) } return result } -function insertIntoBlameMap(map, from, to, commit) { +function insertIntoBlameMap(map, from, to, author) { if (from >= to) { return } @@ -53,11 +55,11 @@ function insertIntoBlameMap(map, from, to, commit) { let next for (; pos < map.length; pos++) { next = map[pos] - if (next.commit === commit) { + if (next.author === author) { if (next.to >= from) break - } else if (next.to > from) { // Different commit, not before + } else if (next.to > from) { // Different author, not before if (next.from < from) { // Sticks out to the left (loop below will handle right side) - const left = new Span(next.from, from, next.commit) + const left = new Span(next.from, from, next.author) if (next.to > to) map.splice(pos++, 0, left) else map[pos++] = left } @@ -67,7 +69,7 @@ function insertIntoBlameMap(map, from, to, commit) { // eslint-ignore while ((next = map[pos])) { - if (next.commit === commit) { + if (next.author === author) { if (next.from > to) break from = Math.min(from, next.from) to = Math.max(to, next.to) @@ -75,7 +77,7 @@ function insertIntoBlameMap(map, from, to, commit) { } else { if (next.from >= to) break if (next.to > to) { - map[pos] = new Span(to, next.to, next.commit) + map[pos] = new Span(to, next.to, next.author) break } else { map.splice(pos, 1) @@ -83,45 +85,26 @@ function insertIntoBlameMap(map, from, to, commit) { } } - map.splice(pos, 0, new Span(from, to, commit)) + map.splice(pos, 0, new Span(from, to, author)) } export default class TrackState { - constructor(blameMap, commits, uncommittedSteps, uncommittedMaps) { + constructor(blameMap) { // The blame map is a data structure that lists a sequence of - // document ranges, along with the commit that inserted them. This + // document ranges, along with the author that inserted them. This // can be used to, for example, highlight the part of the document - // that was inserted by a commit. + // that was inserted by a author. this.blameMap = blameMap - // The commit history, as an array of objects. - this.commits = commits - // Inverted steps and their maps corresponding to the changes that - // have been made since the last commit. - this.uncommittedSteps = uncommittedSteps - this.uncommittedMaps = uncommittedMaps } // Apply a transform to this state applyTransform(transform) { - // Invert the steps in the transaction, to be able to save them in - // the next commit - const inverted - = transform.steps.map((step, i) => step.invert(transform.docs[i])) - const newBlame = updateBlameMap(this.blameMap, transform, this.commits.length) + const clientID = transform.getMeta('clientID') ?? transform.steps.map(item => 'self') + const newBlame = updateBlameMap(this.blameMap, transform, clientID) // Create a new state—since these are part of the editor state, a // persistent data structure, they must not be mutated. - return new TrackState(newBlame, this.commits, - this.uncommittedSteps.concat(inverted), - this.uncommittedMaps.concat(transform.mapping.maps)) - } - - // When a transaction is marked as a commit, this is used to put any - // uncommitted steps into a new commit. - applyCommit(message, time, author) { - if (this.uncommittedSteps.length === 0) return this - const commit = new Commit(message, time, this.uncommittedSteps, this.uncommittedMaps, author) - return new TrackState(this.blameMap, this.commits.concat(commit), [], []) + return new TrackState(newBlame) } } diff --git a/src/extensions/tracking/models.js b/src/extensions/tracking/models.js index fbcacdb65..1c628f780 100644 --- a/src/extensions/tracking/models.js +++ b/src/extensions/tracking/models.js @@ -22,21 +22,9 @@ export class Span { - constructor(from, to, commit) { + constructor(from, to, author) { this.from = from this.to = to - this.commit = commit - } - -} - -export class Commit { - - constructor(message, time, steps, maps, author) { - this.message = message - this.time = time - this.steps = steps - this.maps = maps this.author = author } diff --git a/src/mixins/store.js b/src/mixins/store.js index fb73617b6..1cf36314b 100644 --- a/src/mixins/store.js +++ b/src/mixins/store.js @@ -28,6 +28,11 @@ import store from '../store' * are mounted in other apps e.g. viewer */ export default { + data() { + return { + $store: store, + } + }, beforeMount() { if (typeof this.$store === 'undefined') { this.$store = store diff --git a/src/services/PollingBackend.js b/src/services/PollingBackend.js index 42349451d..e93ad20ab 100644 --- a/src/services/PollingBackend.js +++ b/src/services/PollingBackend.js @@ -76,7 +76,7 @@ class PollingBackend { } connect() { - this.fetcher = setInterval(this._fetchSteps.bind(this), 0) + this.fetcher = setInterval(this._fetchSteps.bind(this), 50) document.addEventListener('visibilitychange', this.visibilitychange.bind(this)) } diff --git a/src/store.js b/src/store.js index a2d84806d..53a8a22ac 100644 --- a/src/store.js +++ b/src/store.js @@ -33,11 +33,16 @@ const store = new Vuex.Store({ showAuthorAnnotations: persistentStorage.getItem('showAuthorAnnotations') === 'true', }, mutations: { - setShowAuthorAnnotations(state, value) { + SET_SHOW_AUTHOR_ANNOTATIONS(state, value) { state.showAuthorAnnotations = value persistentStorage.setItem('showAuthorAnnotations', '' + value) }, }, + actions: { + setShowAuthorAnnotations({ commit }, value) { + store.commit('SET_SHOW_AUTHOR_ANNOTATIONS', value) + }, + }, }) export default store |