From 220f9819b01ff900d192b89fb714b5bc237b51eb Mon Sep 17 00:00:00 2001 From: korelstar Date: Sat, 27 Jun 2020 15:40:34 +0200 Subject: [PATCH] auto refresh current note --- lib/Controller/NotesController.php | 6 ++++- src/NotesService.js | 29 ++++++++++++++++++++++ src/components/EditorEasyMDE.vue | 2 ++ src/components/Note.vue | 39 ++++++++++++++++++++++++++++-- src/store/notes.js | 8 +++--- 5 files changed, 77 insertions(+), 7 deletions(-) diff --git a/lib/Controller/NotesController.php b/lib/Controller/NotesController.php index dece7f6a0..f99123a4b 100644 --- a/lib/Controller/NotesController.php +++ b/lib/Controller/NotesController.php @@ -128,7 +128,11 @@ public function get(int $id) : JSONResponse { strval($id) ); - return $note->getData(); + $result = $note->getData(); + $etag = md5(json_encode($result)); + return (new JSONResponse($result)) + ->setETag($etag) + ; }); } diff --git a/src/NotesService.js b/src/NotesService.js index 4de598b12..051ae76e0 100644 --- a/src/NotesService.js +++ b/src/NotesService.js @@ -96,6 +96,35 @@ export const fetchNote = noteId => { }) } +export const refreshNote = (noteId, lastETag) => { + const headers = {} + if (lastETag) { + headers['If-None-Match'] = lastETag + } + const oldContent = store.getters.getNote(noteId).content + return axios + .get( + url('/notes/' + noteId), + { headers } + ) + .then(response => { + const currentContent = store.getters.getNote(noteId).content + // only update if local content has not changed + if (oldContent === currentContent) { + store.commit('updateNote', response.data) + return response.headers['etag'] + } + return null + }) + .catch(err => { + if (err.response.status !== 304) { + console.error(err) + handleSyncError(t('notes', 'Refreshing note {id} has failed.', { id: noteId })) + } + return null + }) +} + export const setTitle = (noteId, title) => { return axios .put(url('/notes/' + noteId + '/title'), { title: title }) diff --git a/src/components/EditorEasyMDE.vue b/src/components/EditorEasyMDE.vue index b2ef898c1..d161d57f4 100644 --- a/src/components/EditorEasyMDE.vue +++ b/src/components/EditorEasyMDE.vue @@ -33,7 +33,9 @@ export default { watch: { value(val) { if (val !== this.mde.value()) { + const position = this.mde.codemirror.getCursor() this.mde.value(val) + this.mde.codemirror.setCursor(position) } }, }, diff --git a/src/components/Note.vue b/src/components/Note.vue index 8da3478a3..a5d729e4e 100644 --- a/src/components/Note.vue +++ b/src/components/Note.vue @@ -61,7 +61,7 @@ import { import { showError } from '@nextcloud/dialogs' import { emit } from '@nextcloud/event-bus' -import { fetchNote, saveNote, saveNoteManually, routeIsNewNote } from '../NotesService' +import { fetchNote, refreshNote, saveNote, saveNoteManually, routeIsNewNote } from '../NotesService' import TheEditor from './EditorEasyMDE' import ThePreview from './EditorMarkdownIt' import store from '../store' @@ -97,6 +97,8 @@ export default { preview: false, actionsOpen: false, autosaveTimer: null, + refreshTimer: null, + etag: null, } }, @@ -133,6 +135,7 @@ export default { }, destroyed() { + this.stopRefreshTimer() document.removeEventListener('webkitfullscreenchange', this.onDetectFullscreen) document.removeEventListener('mozfullscreenchange', this.onDetectFullscreen) document.removeEventListener('fullscreenchange', this.onDetectFullscreen) @@ -144,6 +147,8 @@ export default { methods: { fetchData() { store.commit('setSidebarOpen', false) + this.etag = null + this.stopRefreshTimer() if (this.isMobile) { emit('toggle-navigation', { open: false }) @@ -152,11 +157,12 @@ export default { this.onUpdateTitle(this.title) this.loading = true this.preview = false - fetchNote(this.noteId) + fetchNote(parseInt(this.noteId)) .then((note) => { if (note.errorMessage) { showError(note.errorMessage) } + this.startRefreshTimer() }) .catch(() => { // note not found @@ -220,8 +226,34 @@ export default { this.actionsOpen = false }, + stopRefreshTimer() { + if (this.refreshTimer !== null) { + clearTimeout(this.refreshTimer) + this.refreshTimer = null + } + }, + + startRefreshTimer() { + this.stopRefreshTimer() + this.refreshTimer = setTimeout(() => { + this.refreshTimer = null + this.refreshNote() + }, 10000) + }, + + refreshNote() { + refreshNote(parseInt(this.noteId), this.etag).then(etag => { + if (etag) { + this.etag = etag + this.$forceUpdate() + } + this.startRefreshTimer() + }) + }, + onEdit(newContent) { if (this.note.content !== newContent) { + this.stopRefreshTimer() const note = { ...this.note, content: newContent, @@ -229,12 +261,15 @@ export default { autotitle: routeIsNewNote(this.$route), } store.commit('updateNote', note) + this.$forceUpdate() if (this.autosaveTimer === null) { this.autosaveTimer = setTimeout(() => { this.autosaveTimer = null saveNote(note.id) }, 2000) } + // TODO should be after save is finished + this.startRefreshTimer() } }, diff --git a/src/store/notes.js b/src/store/notes.js index 61e8b176a..88393aa8b 100644 --- a/src/store/notes.js +++ b/src/store/notes.js @@ -76,13 +76,13 @@ const mutations = { updateNote(state, updated) { const note = state.notesIds[updated.id] if (note) { + note.title = updated.title + note.modified = updated.modified + note.favorite = updated.favorite + note.category = updated.category // don't update meta-data over full data if (updated.content !== undefined || note.content === undefined) { - note.title = updated.title - note.modified = updated.modified note.content = updated.content - note.favorite = updated.favorite - note.category = updated.category Vue.set(note, 'autotitle', updated.autotitle) Vue.set(note, 'unsaved', updated.unsaved) Vue.set(note, 'error', updated.error)