|
1 | | -import { markRaw, ref, unref } from 'vue' |
| 1 | +import { computed, markRaw, ref, unref } from 'vue' |
2 | 2 | import type { Component } from 'vue' |
3 | 3 | import type { Editor, Range } from '@tiptap/core' |
4 | 4 | import type {} from '@tiptap/extension-text-align' |
5 | 5 | import { useGettext } from 'vue3-gettext' |
6 | 6 | import { storeToRefs } from 'pinia' |
| 7 | +import type { Resource } from '@opencloud-eu/web-client' |
7 | 8 | import { OcEmojiPicker } from '@opencloud-eu/design-system/components' |
8 | 9 | import { useModals, useThemeStore } from '../../composables' |
| 10 | +import { useClientService } from '../../composables/clientService' |
| 11 | +import { useGetMatchingSpace } from '../../composables/spaces' |
| 12 | +import { useFolderLink } from '../../composables/folderLink' |
| 13 | +import FilePickerModal from '../../components/Modals/FilePickerModal.vue' |
| 14 | +import { arrayBufferToDataUrl } from '../../helpers' |
9 | 15 | import { TextEditorState } from '../types' |
10 | 16 | import { requestLinkPanel } from '../helpers/link' |
11 | 17 | import TextEditorSearchAndReplacePanel from '../components/TextEditorSearchAndReplacePanel.vue' |
@@ -64,6 +70,12 @@ export function useEditorActions(state: TextEditorState) { |
64 | 70 | const { dispatchModal } = useModals() |
65 | 71 | const themeStore = useThemeStore() |
66 | 72 | const { currentTheme } = storeToRefs(themeStore) |
| 73 | + const clientService = useClientService() |
| 74 | + const { getMatchingSpace } = useGetMatchingSpace() |
| 75 | + const { getParentFolderLink } = useFolderLink() |
| 76 | + const currentResource = computed<Resource | null>(() => { |
| 77 | + return unref(state.currentResource) ?? null |
| 78 | + }) |
67 | 79 |
|
68 | 80 | //Search |
69 | 81 | const searchSearchTerm = ref('') |
@@ -544,6 +556,47 @@ export function useEditorActions(state: TextEditorState) { |
544 | 556 | }) |
545 | 557 | } |
546 | 558 |
|
| 559 | + const insertImageFromCloudResource = async (editor: Editor, resource: Resource) => { |
| 560 | + const space = getMatchingSpace(resource) |
| 561 | + const response = await clientService.webdav.getFileContents( |
| 562 | + space, |
| 563 | + { path: resource.path }, |
| 564 | + { responseType: 'arraybuffer' } |
| 565 | + ) |
| 566 | + const body = response?.body as ArrayBuffer | undefined |
| 567 | + if (!body) { |
| 568 | + return |
| 569 | + } |
| 570 | + const mimeType = resource.mimeType || 'application/octet-stream' |
| 571 | + const dataUrl = await arrayBufferToDataUrl(body, mimeType) |
| 572 | + editor.chain().focus().setImage({ src: dataUrl }).run() |
| 573 | + } |
| 574 | + |
| 575 | + const openCloudImagePicker = (editor: Editor) => { |
| 576 | + const resource = unref(currentResource) |
| 577 | + if (!resource) { |
| 578 | + return |
| 579 | + } |
| 580 | + |
| 581 | + dispatchModal({ |
| 582 | + elementClass: 'file-picker-modal', |
| 583 | + title: $gettext('Insert image from cloud'), |
| 584 | + customComponent: markRaw(FilePickerModal), |
| 585 | + hideActions: true, |
| 586 | + customComponentAttrs: () => ({ |
| 587 | + allowedFileTypes: ['image/png', 'image/gif', 'image/jpeg', 'image/svg'], |
| 588 | + parentFolderLink: getParentFolderLink(resource), |
| 589 | + callbackFn: async ({ resource }: { resource: Resource }) => { |
| 590 | + if (!resource.mimeType?.startsWith('image/')) { |
| 591 | + return |
| 592 | + } |
| 593 | + await insertImageFromCloudResource(editor, resource) |
| 594 | + } |
| 595 | + }), |
| 596 | + focusTrapInitial: false |
| 597 | + }) |
| 598 | + } |
| 599 | + |
547 | 600 | const imageUrl = (): EditorAction => ({ |
548 | 601 | id: 'image-url', |
549 | 602 | title: $gettext('Image from URL'), |
@@ -575,13 +628,28 @@ export function useEditorActions(state: TextEditorState) { |
575 | 628 | isActive: () => false |
576 | 629 | }) |
577 | 630 |
|
| 631 | + const imageCloud = (): EditorAction => ({ |
| 632 | + id: 'image-cloud', |
| 633 | + title: $gettext('Insert from cloud'), |
| 634 | + description: $gettext('Insert an image from your cloud files'), |
| 635 | + icon: 'cloud-line', |
| 636 | + keywords: ['image', 'picture', 'cloud'], |
| 637 | + showInSlashCommands: false, |
| 638 | + toolbarAction: (editor) => openCloudImagePicker(editor), |
| 639 | + slashCommandAction: ({ editor, range }) => { |
| 640 | + editor.chain().focus().deleteRange(range).run() |
| 641 | + openCloudImagePicker(editor) |
| 642 | + }, |
| 643 | + isActive: () => false |
| 644 | + }) |
| 645 | + |
578 | 646 | const image = (): EditorAction => ({ |
579 | 647 | id: 'image', |
580 | 648 | title: $gettext('Insert image'), |
581 | 649 | icon: 'image-line', |
582 | | - keywords: ['image', 'picture', 'upload', 'url'], |
| 650 | + keywords: ['image', 'picture', 'upload', 'url', 'cloud'], |
583 | 651 | showInSlashCommands: false, |
584 | | - childActions: [imageUpload(), imageUrl()], |
| 652 | + childActions: [imageUpload(), imageUrl(), imageCloud()], |
585 | 653 | isActive: () => false |
586 | 654 | }) |
587 | 655 |
|
|
0 commit comments