From 0007d0d2effaa70822d97f3956d1a15eb6e47795 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 31 May 2022 10:17:46 +0200 Subject: refactor: inject $imageResolver in EditorWrapper Signed-off-by: Max --- src/components/EditorWrapper.provider.js | 15 +++ src/components/EditorWrapper.vue | 13 +++ src/nodes/ImageResolver.js | 166 --------------------------- src/nodes/ImageView.vue | 15 +-- src/services/ImageResolver.js | 185 +++++++++++++++++++++++++++++++ src/tests/nodes/ImageResolver.spec.js | 109 ------------------ src/tests/services/ImageResolver.spec.js | 109 ++++++++++++++++++ 7 files changed, 326 insertions(+), 286 deletions(-) delete mode 100644 src/nodes/ImageResolver.js create mode 100644 src/services/ImageResolver.js delete mode 100644 src/tests/nodes/ImageResolver.spec.js create mode 100644 src/tests/services/ImageResolver.spec.js diff --git a/src/components/EditorWrapper.provider.js b/src/components/EditorWrapper.provider.js index c66cc9cb6..b791075c5 100644 --- a/src/components/EditorWrapper.provider.js +++ b/src/components/EditorWrapper.provider.js @@ -22,6 +22,7 @@ export const EDITOR = Symbol('tiptap:editor') export const FILE = Symbol('editor:file') +export const IMAGE_RESOLVER = Symbol('image:resolver') export const IS_MOBILE = Symbol('editor:is-mobile') export const IS_PUBLIC = Symbol('editor:is-public') export const IS_RICH_EDITOR = Symbol('editor:is-rich-editor') @@ -76,3 +77,17 @@ export const useFileMixin = { }, }, } + +export const useImageResolver = { + inject: { + $imageResolver: { + from: IMAGE_RESOLVER, + default: { + resolve(src) { + console.warn('No image resolver provided. Some image sources cannot be resolved.') + return [src] + }, + }, + }, + }, +} diff --git a/src/components/EditorWrapper.vue b/src/components/EditorWrapper.vue index 3b63558f9..c75d6897b 100644 --- a/src/components/EditorWrapper.vue +++ b/src/components/EditorWrapper.vue @@ -94,10 +94,12 @@ import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip' import { EditorContent } from '@tiptap/vue-2' import { getVersion, receiveTransaction } from 'prosemirror-collab' import { Step } from 'prosemirror-transform' +import { getCurrentUser } from '@nextcloud/auth' import { EDITOR, FILE, + IMAGE_RESOLVER, IS_MOBILE, IS_PUBLIC, IS_RICH_EDITOR, @@ -106,6 +108,7 @@ import { } from './EditorWrapper.provider.js' import { SyncService, ERROR_TYPE, IDLE_TIMEOUT } from './../services/SyncService.js' +import ImageResolver from './../services/ImageResolver.js' import { getRandomGuestName } from './../helpers/index.js' import { extensionHighlight } from '../helpers/mappings.js' import { createEditor, serializePlainText, loadSyntaxHighlight } from './../EditorFactory.js' @@ -158,6 +161,9 @@ export default { [FILE]: { get: () => this.fileData, }, + [IMAGE_RESOLVER]: { + get: () => this.$imageResolver, + }, [IS_PUBLIC]: { get: () => this.isPublic, }, @@ -335,6 +341,7 @@ export default { created() { this.$editor = null this.$syncService = null + this.$imageResolver = null this.saveStatusPolling = setInterval(() => { this.updateLastSavedStatus() }, 2000) @@ -483,6 +490,12 @@ export default { this.lock = this.$syncService.lock localStorage.setItem('nick', this.currentSession.guestName) this.$store.dispatch('setCurrentSession', this.currentSession) + this.$imageResolver = new ImageResolver({ + session: this.currentSession, + user: getCurrentUser(), + shareToken: this.shareToken, + currentDirectory: this.relativePath, + }) }, onLoaded({ documentSource }) { diff --git a/src/nodes/ImageResolver.js b/src/nodes/ImageResolver.js deleted file mode 100644 index 056a128a4..000000000 --- a/src/nodes/ImageResolver.js +++ /dev/null @@ -1,166 +0,0 @@ -/* - * @copyright Copyright (c) 2022 Max - * - * @author Max - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -import { generateUrl, generateRemoteUrl } from '@nextcloud/router' -import path from 'path' - -export default class ImageResolver { - constructor({session, user, shareToken, currentDirectory}) { - this.session = session - this.user = user - this.shareToken = shareToken - this.currentDirectory = currentDirectory - } - - /* - * Resolve a given src. - * @param { string } the original src in the node. - * @returns { Array } - resolved urls to try. - * - * Currently returns either one or two urls. - */ - resolve(src) { - if (src.startsWith('text://')) { - const imageFileName = getQueryVariable(src, 'imageFileName') - return [this.getAttachmentUrl(imageFileName)] - } - if (src.startsWith(`.attachments.${this.session?.documentId}/`)) { - const imageFileName = decodeURIComponent(src.replace(`.attachments.${this.session?.documentId}/`, '').split('?')[0]) - return [this.getAttachmentUrl(imageFileName)] - } - if (isDirectUrl(src)) { - return [src] - } - if (hasPreview(src)) { // && this.mime !== 'image/gif') { - return [this.previewUrl(src)] - } - - // if it starts with '.attachments.1234/' - if (src.match(/^\.attachments\.\d+\//)) { - const imageFileName = decodeURIComponent(src.replace(/\.attachments\.\d+\//, '').split('?')[0]) - const attachmentUrl = this.getAttachmentUrl(imageFileName) - // try the webdav url and attachment API if the fails - return [this.davUrl(src), attachmentUrl] - } - return [this.davUrl(src)] - } - - getAttachmentUrl(imageFileName) { - if (this.user || !this.shareToken) { - return generateUrl('/apps/text/image?documentId={documentId}&sessionId={sessionId}&sessionToken={sessionToken}&imageFileName={imageFileName}', { - ...this.textApiParams(), - imageFileName, - }) - } - return generateUrl('/apps/text/image?documentId={documentId}&sessionId={sessionId}&sessionToken={sessionToken}&imageFileName={imageFileName}&shareToken={shareToken}', { - ...this.textApiParams(), - imageFileName, - shareToken: this.shareToken, - }) - } - - textApiParams() { - if (this.session) { - return { - documentId: this.session.documentId, - sessionId: this.session.id, - sessionToken: this.session.token, - } - } - } - - previewUrl(src) { - const imageFileId = getQueryVariable(src, 'fileId') - const path = this.filePath(src) - const fileQuery = (imageFileId) - ? `?fileId=${imageFileId}&file=${encodeURIComponent(path)}` - : `?file=${encodeURIComponent(path)}` - const query = fileQuery + '&x=1024&y=1024&a=true' - if (this.user) { - return generateUrl('/core/preview') + query - } else { - return generateUrl(`/apps/files_sharing/publicpreview/${this.shareToken}${query}`) - } - } - - filePath(src) { - const f = [ - this.currentDirectory, - basename(src), - ].join('/') - return path.normalize(f) - } - - davUrl(src){ - if (this.user) { - const uid = this.user.uid - const encoded = encodeURI(this.filePath(src)) - return generateRemoteUrl(`dav/files/${uid}${encoded}`) - } else { - return generateUrl('/s/{token}/download?path={dirname}&files={basename}', - { - token: this.shareToken, - dirname: this.currentDirectory, - basename: basename(src), - }) - } - } -} - -/* Urls that can be loaded directy: - * * remote urls - * * data urls - * * preview urls - */ -function isDirectUrl(src) { - return src.startsWith('http://') - || src.startsWith('https://') - || src.startsWith('data:') - || src.match(/^(\/index.php)?\/core\/preview/) - || src.match(/^(\/index.php)?\/apps\/files_sharing\/publicpreview\//) -} - -function hasPreview(src) { - return getQueryVariable(src, 'hasPreview') === 'true' -} - -function basename(src) { - return decodeURI(src.split('?')[0]) -} - -function getQueryVariable(src, variable) { - const query = src.split('?')[1] - if (typeof query === 'undefined') { - return - } - const vars = query.split(/[&#]/) - if (typeof vars === 'undefined') { - return - } - for (let i = 0; i < vars.length; i++) { - const pair = vars[i].split('=') - if (decodeURIComponent(pair[0]) === variable) { - return decodeURIComponent(pair[1]) - } - } -} - diff --git a/src/nodes/ImageView.vue b/src/nodes/ImageView.vue index 86035bba0..8466d7760 100644 --- a/src/nodes/ImageView.vue +++ b/src/nodes/ImageView.vue @@ -75,14 +75,12 @@