integrate Nextcloud Text as editor for notes
This commit is contained in:
parent
b1a988e1da
commit
21e6dafddc
|
@ -6,10 +6,13 @@ namespace OCA\Notes\Controller;
|
|||
|
||||
use OCA\Notes\Service\NotesService;
|
||||
|
||||
use OCA\Viewer\Event\LoadViewer;
|
||||
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
||||
use OCP\AppFramework\Http\RedirectResponse;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IRequest;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUserSession;
|
||||
|
@ -19,6 +22,8 @@ class PageController extends Controller {
|
|||
private $notesService;
|
||||
/** @var IUserSession */
|
||||
private $userSession;
|
||||
/** @var IEventDispatcher */
|
||||
private $eventDispatcher;
|
||||
/** @IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
|
@ -27,11 +32,13 @@ class PageController extends Controller {
|
|||
IRequest $request,
|
||||
NotesService $notesService,
|
||||
IUserSession $userSession,
|
||||
IEventDispatcher $eventDispatcher,
|
||||
IURLGenerator $urlGenerator
|
||||
) {
|
||||
parent::__construct($AppName, $request);
|
||||
$this->notesService = $notesService;
|
||||
$this->userSession = $userSession;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
|
@ -41,6 +48,7 @@ class PageController extends Controller {
|
|||
* @NoCSRFRequired
|
||||
*/
|
||||
public function index() : TemplateResponse {
|
||||
$this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer());
|
||||
$devMode = !is_file(dirname(__FILE__).'/../../js/notes-main.js');
|
||||
$response = new TemplateResponse(
|
||||
$this->appName,
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
<template>
|
||||
<AppContent :class="{ loading: loading || isManualSave, 'icon-error': !loading && (!note || note.error), 'sidebar-open': sidebarOpen }">
|
||||
<AppContent :class="{ loading: loading, 'icon-error': !loading && (!note || note.error), 'sidebar-open': sidebarOpen }">
|
||||
<div v-if="!loading && note && !note.error && !note.deleting"
|
||||
id="note-container"
|
||||
class="note-container"
|
||||
:class="{ fullscreen: fullscreen }"
|
||||
>
|
||||
<div class="note-editor">
|
||||
<div v-show="!note.content" class="placeholder">
|
||||
{{ preview ? t('notes', 'Empty note') : t('notes', 'Write …') }}
|
||||
</div>
|
||||
<ThePreview v-if="preview" :value="note.content" />
|
||||
<TheEditor v-else :value="note.content" @input="onEdit" />
|
||||
<component :is="viewer.component"
|
||||
ref="texteditor"
|
||||
:fileid="fileId"
|
||||
:basename="title"
|
||||
:active="true"
|
||||
:has-preview="true"
|
||||
mime="text/markdown"
|
||||
class="text-editor"
|
||||
@ready="onEditorReady"
|
||||
/>
|
||||
</div>
|
||||
<span class="action-buttons">
|
||||
<Actions :open.sync="actionsOpen" menu-align="right">
|
||||
|
@ -20,13 +25,6 @@
|
|||
>
|
||||
{{ t('notes', 'Details') }}
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
v-tooltip.left="t('notes', 'CTRL + /')"
|
||||
:icon="preview ? 'icon-rename' : 'icon-toggle'"
|
||||
@click="onTogglePreview"
|
||||
>
|
||||
{{ preview ? t('notes', 'Edit') : t('notes', 'Preview') }}
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
icon="icon-fullscreen"
|
||||
:class="{ active: fullscreen }"
|
||||
|
@ -35,11 +33,6 @@
|
|||
{{ fullscreen ? t('notes', 'Exit full screen') : t('notes', 'Full screen') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
<button v-show="note.saveError"
|
||||
v-tooltip.right="t('notes', 'Save failed. Click to retry.')"
|
||||
class="action-error icon-error-color"
|
||||
@click="onManualSave"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</AppContent>
|
||||
|
@ -53,13 +46,10 @@ import {
|
|||
Tooltip,
|
||||
isMobile,
|
||||
} from '@nextcloud/vue'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
|
||||
import { config } from '../config'
|
||||
import { fetchNote, refreshNote, saveNote, saveNoteManually, autotitleNote, routeIsNewNote } from '../NotesService'
|
||||
import TheEditor from './EditorEasyMDE'
|
||||
import ThePreview from './EditorMarkdownIt'
|
||||
import { autotitleNote, routeIsNewNote } from '../NotesService'
|
||||
import store from '../store'
|
||||
|
||||
export default {
|
||||
|
@ -69,8 +59,6 @@ export default {
|
|||
Actions,
|
||||
ActionButton,
|
||||
AppContent,
|
||||
TheEditor,
|
||||
ThePreview,
|
||||
},
|
||||
|
||||
directives: {
|
||||
|
@ -90,11 +78,8 @@ export default {
|
|||
return {
|
||||
loading: false,
|
||||
fullscreen: false,
|
||||
preview: false,
|
||||
actionsOpen: false,
|
||||
autosaveTimer: null,
|
||||
autotitleTimer: null,
|
||||
refreshTimer: null,
|
||||
etag: null,
|
||||
}
|
||||
},
|
||||
|
@ -109,25 +94,30 @@ export default {
|
|||
isNewNote() {
|
||||
return routeIsNewNote(this.$route)
|
||||
},
|
||||
isManualSave() {
|
||||
return store.state.app.isManualSave
|
||||
},
|
||||
sidebarOpen() {
|
||||
return store.state.app.sidebarOpen
|
||||
},
|
||||
fileId() {
|
||||
return parseInt(this.noteId)
|
||||
},
|
||||
viewer() {
|
||||
return OCA.Viewer.availableHandlers.filter(h => h.id === 'text')[0]
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
if (to.name !== from.name || to.params.noteId !== from.params.noteId) {
|
||||
this.fetchData()
|
||||
// this.loading = true
|
||||
this.initNote()
|
||||
this.$refs.texteditor.$children[0].reconnect()
|
||||
}
|
||||
},
|
||||
title: 'onUpdateTitle',
|
||||
},
|
||||
|
||||
created() {
|
||||
this.fetchData()
|
||||
this.initNote()
|
||||
document.addEventListener('webkitfullscreenchange', this.onDetectFullscreen)
|
||||
document.addEventListener('mozfullscreenchange', this.onDetectFullscreen)
|
||||
document.addEventListener('fullscreenchange', this.onDetectFullscreen)
|
||||
|
@ -135,7 +125,6 @@ export default {
|
|||
},
|
||||
|
||||
destroyed() {
|
||||
this.stopRefreshTimer()
|
||||
document.removeEventListener('webkitfullscreenchange', this.onDetectFullscreen)
|
||||
document.removeEventListener('mozfullscreenchange', this.onDetectFullscreen)
|
||||
document.removeEventListener('fullscreenchange', this.onDetectFullscreen)
|
||||
|
@ -145,31 +134,14 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
fetchData() {
|
||||
initNote() {
|
||||
store.commit('setSidebarOpen', false)
|
||||
this.etag = null
|
||||
this.stopRefreshTimer()
|
||||
|
||||
if (this.isMobile) {
|
||||
emit('toggle-navigation', { open: false })
|
||||
}
|
||||
|
||||
this.onUpdateTitle(this.title)
|
||||
this.loading = true
|
||||
this.preview = false
|
||||
fetchNote(parseInt(this.noteId))
|
||||
.then((note) => {
|
||||
if (note.errorMessage) {
|
||||
showError(note.errorMessage)
|
||||
}
|
||||
this.startRefreshTimer()
|
||||
})
|
||||
.catch(() => {
|
||||
// note not found
|
||||
})
|
||||
.then(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
onUpdateTitle(title) {
|
||||
|
@ -181,11 +153,6 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
onTogglePreview() {
|
||||
this.preview = !this.preview
|
||||
this.actionsOpen = false
|
||||
},
|
||||
|
||||
onDetectFullscreen() {
|
||||
this.fullscreen = document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen
|
||||
},
|
||||
|
@ -226,38 +193,13 @@ 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()
|
||||
}, config.interval.note.refresh * 1000)
|
||||
},
|
||||
|
||||
refreshNote() {
|
||||
if (this.note.unsaved) {
|
||||
this.startRefreshTimer()
|
||||
return
|
||||
}
|
||||
refreshNote(parseInt(this.noteId), this.etag).then(etag => {
|
||||
if (etag) {
|
||||
this.etag = etag
|
||||
this.$forceUpdate()
|
||||
}
|
||||
this.startRefreshTimer()
|
||||
})
|
||||
onEditorReady() {
|
||||
console.debug('onEditorReady')
|
||||
this.loading = false
|
||||
},
|
||||
|
||||
onEdit(newContent) {
|
||||
if (this.note.content !== newContent) {
|
||||
this.stopRefreshTimer()
|
||||
const note = {
|
||||
...this.note,
|
||||
content: newContent,
|
||||
|
@ -266,18 +208,6 @@ export default {
|
|||
store.commit('updateNote', note)
|
||||
this.$forceUpdate()
|
||||
|
||||
// queue auto saving note content
|
||||
if (this.autosaveTimer === null) {
|
||||
this.autosaveTimer = setTimeout(() => {
|
||||
this.autosaveTimer = null
|
||||
saveNote(note.id)
|
||||
}, config.interval.note.autosave * 1000)
|
||||
}
|
||||
|
||||
// (re-) start auto refresh timer
|
||||
// TODO should be after save is finished
|
||||
this.startRefreshTimer()
|
||||
|
||||
// stop old autotitle timer
|
||||
if (this.autotitleTimer !== null) {
|
||||
clearTimeout(this.autotitleTimer)
|
||||
|
@ -296,26 +226,6 @@ export default {
|
|||
},
|
||||
|
||||
onKeyPress(event) {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
switch (event.key.toLowerCase()) {
|
||||
case 's':
|
||||
event.preventDefault()
|
||||
this.onManualSave()
|
||||
break
|
||||
case '/':
|
||||
event.preventDefault()
|
||||
this.onTogglePreview()
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onManualSave() {
|
||||
const note = {
|
||||
...this.note,
|
||||
}
|
||||
store.commit('updateNote', note)
|
||||
saveNoteManually(this.note.id)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -334,6 +244,11 @@ export default {
|
|||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.text-editor {
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
}
|
||||
|
||||
/* center editor on large screens */
|
||||
@media (min-width: 1600px) {
|
||||
.note-editor {
|
||||
|
|
Loading…
Reference in New Issue