mirror of
https://github.com/go-vikunja/vikunja.git
synced 2026-04-24 22:25:15 +00:00
fix(editor): make pasting a file work again
It seems like only one paste handler is possible - with the change inf52a321acf19b8925a5285abf09ae3ed51ea4ca8 the paste handler for the image paste did not work anymore. Resolves https://community.vikunja.io/t/feature-suggestion-paste-images-directly-into-description-comment-from-clipboard/3656
This commit is contained in:
@@ -630,6 +630,29 @@ describe('Task', () => {
|
||||
.should('contain', 'Success')
|
||||
})
|
||||
|
||||
it('Can paste an image into the description editor which uploads it as an attachment', () => {
|
||||
TaskAttachmentFactory.truncate()
|
||||
const tasks = TaskFactory.create(1, {
|
||||
id: 1,
|
||||
}) as Task[]
|
||||
cy.visit(`/tasks/${tasks[0].id}`)
|
||||
|
||||
cy.intercept('**/tasks/*/attachments').as('uploadAttachment')
|
||||
|
||||
cy.get('.task-view .details.content.description .tiptap__editor .tiptap.ProseMirror', {timeout: 30_000})
|
||||
.pasteFile('image.jpg', 'image/jpeg')
|
||||
|
||||
cy.wait('@uploadAttachment')
|
||||
cy.get('.attachments .attachments .files button.attachment')
|
||||
.should('exist')
|
||||
cy.get('.task-view .details.content.description .tiptap__editor .tiptap.ProseMirror img')
|
||||
.should('be.visible')
|
||||
.and(($img) => {
|
||||
// "naturalWidth" and "naturalHeight" are set when the image loads
|
||||
expect($img[0].naturalWidth).to.be.greaterThan(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('Can set a reminder', () => {
|
||||
TaskReminderFactory.truncate()
|
||||
const tasks = TaskFactory.create(1, {
|
||||
|
||||
@@ -34,4 +34,28 @@
|
||||
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Cypress.Commands.add('pasteFile', {prevSubject: true}, (subject, fileName, fileType = 'image/png') => {
|
||||
// Load the file fixture as base64
|
||||
cy.fixture(fileName, 'base64').then((fileContent) => {
|
||||
// Convert base64 to a Blob
|
||||
const blob = Cypress.Blob.base64StringToBlob(fileContent, fileType)
|
||||
// Create a File object
|
||||
const testFile = new File([blob], fileName, {type: fileType})
|
||||
// Create a DataTransfer and add the file
|
||||
const dataTransfer = new DataTransfer()
|
||||
dataTransfer.items.add(testFile)
|
||||
|
||||
// Create the paste event with clipboardData containing the file
|
||||
const pasteEvent = new ClipboardEvent('paste', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
clipboardData: dataTransfer,
|
||||
})
|
||||
|
||||
// Dispatch the paste event on the target element
|
||||
subject[0].dispatchEvent(pasteEvent)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
12
frontend/cypress/support/index.d.ts
vendored
Normal file
12
frontend/cypress/support/index.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable<Subject = any> {
|
||||
/**
|
||||
* Pastes a file onto the subject element.
|
||||
* @param fileName The name of the file to paste
|
||||
* @param fileType The MIME type of the file (defaults to 'image/png')
|
||||
*/
|
||||
pasteFile(fileName: string, fileType?: string): Chainable<Subject>;
|
||||
}
|
||||
}
|
||||
@@ -333,15 +333,32 @@ const additionalLinkProtocols = [
|
||||
'notion',
|
||||
]
|
||||
|
||||
const MarkdownPasteHandler = Extension.create({
|
||||
name: 'markdownPasteHandler',
|
||||
const PasteHandler = Extension.create({
|
||||
name: 'pasteHandler',
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey('markdownPasteHandler'),
|
||||
key: new PluginKey('pasteHandler'),
|
||||
props: {
|
||||
handlePaste: (view, event) => {
|
||||
|
||||
// Handle images pasted from clipboard
|
||||
if (typeof props.uploadCallback !== 'undefined' && event.clipboardData?.items?.length > 0) {
|
||||
|
||||
for (const item of event.clipboardData.items) {
|
||||
console.log({item})
|
||||
if (item.kind === 'file' && item.type.startsWith('image/')) {
|
||||
const file = item.getAsFile()
|
||||
if (file) {
|
||||
uploadAndInsertFiles([file])
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle markdown text
|
||||
const text = event.clipboardData?.getData('text/plain')
|
||||
if (!text) return false
|
||||
|
||||
@@ -451,7 +468,7 @@ const extensions : Extensions = [
|
||||
suggestion: suggestionSetup(t),
|
||||
}),
|
||||
|
||||
MarkdownPasteHandler,
|
||||
PasteHandler,
|
||||
]
|
||||
|
||||
// Add a custom extension for the Escape key
|
||||
@@ -616,21 +633,10 @@ onMounted(async () => {
|
||||
|
||||
await nextTick()
|
||||
|
||||
if (typeof props.uploadCallback !== 'undefined') {
|
||||
const input = tiptapInstanceRef.value?.querySelectorAll('.tiptap__editor')[0]?.children[0]
|
||||
input?.addEventListener('paste', handleImagePaste)
|
||||
}
|
||||
|
||||
setModeAndValue(props.modelValue)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
nextTick(() => {
|
||||
if (typeof props.uploadCallback !== 'undefined') {
|
||||
const input = tiptapInstanceRef.value?.querySelectorAll('.tiptap__editor')[0]?.children[0]
|
||||
input?.removeEventListener('paste', handleImagePaste)
|
||||
}
|
||||
})
|
||||
if (props.editShortcut !== '') {
|
||||
document.removeEventListener('keydown', setFocusToEditor)
|
||||
}
|
||||
@@ -641,22 +647,6 @@ function setModeAndValue(value: string) {
|
||||
editor.value?.commands.setContent(value, false)
|
||||
}
|
||||
|
||||
function handleImagePaste(event) {
|
||||
if (event?.clipboardData?.items?.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
const image = event.clipboardData.items[0]
|
||||
if (image.kind === 'file' && image.type.startsWith('image/')) {
|
||||
if (typeof props.uploadCallback !== 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
uploadAndInsertFiles([image.getAsFile()])
|
||||
}
|
||||
}
|
||||
|
||||
// See https://github.com/github/hotkey/discussions/85#discussioncomment-5214660
|
||||
function setFocusToEditor(event) {
|
||||
|
||||
Reference in New Issue
Block a user