From 894167520dc829b72f721bdf63acc6038ff8816f Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Tue, 7 Nov 2023 12:11:21 +0530 Subject: [PATCH] fix: Fixed issue where non-image inlined files would not correctly be imported from a Markdown file --- packages/ui-services/src/Import/Importer.ts | 5 ++++- .../ImportModal/ImportModalController.ts | 10 ++++++++- .../ImportModal/ImportModalFileItem.tsx | 1 + .../SuperEditor/MarkdownTransformers.ts | 22 +++++++++++++++++++ .../Tools/HeadlessSuperConverter.tsx | 15 ++++++++----- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/packages/ui-services/src/Import/Importer.ts b/packages/ui-services/src/Import/Importer.ts index 636532dbc..b99a90f31 100644 --- a/packages/ui-services/src/Import/Importer.ts +++ b/packages/ui-services/src/Import/Importer.ts @@ -164,6 +164,10 @@ export class Importer { return this.mutator.insertItem(note) }), ) + return insertedItems + } + + async uploadAndReplaceInlineFilesInInsertedItems(insertedItems: DecryptedItemInterface[]) { for (const item of insertedItems) { if (!isNote(item)) { continue @@ -185,6 +189,5 @@ export class Importer { console.error(error) } } - return insertedItems } } diff --git a/packages/web/src/javascripts/Components/ImportModal/ImportModalController.ts b/packages/web/src/javascripts/Components/ImportModal/ImportModalController.ts index a5615752b..9e78791ae 100644 --- a/packages/web/src/javascripts/Components/ImportModal/ImportModalController.ts +++ b/packages/web/src/javascripts/Components/ImportModal/ImportModalController.ts @@ -21,6 +21,7 @@ export type ImportModalFile = ( | { status: 'ready'; payloads?: DecryptedTransferPayload[] } | { status: 'parsing' } | { status: 'importing' } + | { status: 'uploading-files' } | { status: 'success'; successMessage: string } | { status: 'error'; error: Error } ) & @@ -105,7 +106,14 @@ export class ImportModalController { }) try { - await this.importer.importFromTransferPayloads(payloads) + const insertedItems = await this.importer.importFromTransferPayloads(payloads) + + this.updateFile({ + ...file, + status: 'uploading-files', + }) + + await this.importer.uploadAndReplaceInlineFilesInInsertedItems(insertedItems) const notesImported = payloads.filter((payload) => payload.content_type === ContentType.TYPES.Note) const tagsImported = payloads.filter((payload) => payload.content_type === ContentType.TYPES.Tag) diff --git a/packages/web/src/javascripts/Components/ImportModal/ImportModalFileItem.tsx b/packages/web/src/javascripts/Components/ImportModal/ImportModalFileItem.tsx index 886a02569..f42638aac 100644 --- a/packages/web/src/javascripts/Components/ImportModal/ImportModalFileItem.tsx +++ b/packages/web/src/javascripts/Components/ImportModal/ImportModalFileItem.tsx @@ -103,6 +103,7 @@ const ImportModalFileItem = ({ {file.status === 'pending' && 'Could not auto-detect service. Please select manually.'} {file.status === 'parsing' && 'Parsing...'} {file.status === 'importing' && 'Importing...'} + {file.status === 'uploading-files' && 'Uploading and embedding files...'} {file.status === 'error' && JSON.stringify(file.error)} {file.status === 'success' && file.successMessage} diff --git a/packages/web/src/javascripts/Components/SuperEditor/MarkdownTransformers.ts b/packages/web/src/javascripts/Components/SuperEditor/MarkdownTransformers.ts index 0d3d4c0ba..b94d3c9f3 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/MarkdownTransformers.ts +++ b/packages/web/src/javascripts/Components/SuperEditor/MarkdownTransformers.ts @@ -31,6 +31,7 @@ import { $isRemoteImageNode, RemoteImageNode, } from './Plugins/RemoteImagePlugin/RemoteImageNode' +import { $createInlineFileNode, $isInlineFileNode, InlineFileNode } from './Plugins/InlineFilePlugin/InlineFileNode' const HorizontalRule: ElementTransformer = { dependencies: [HorizontalRuleNode], @@ -72,6 +73,26 @@ const IMAGE: TextMatchTransformer = { type: 'text-match', } +const INLINE_FILE: TextMatchTransformer = { + dependencies: [InlineFileNode], + export: (node) => { + if (!$isInlineFileNode(node)) { + return null + } + + return node.getTextContent() + }, + importRegExp: /(?:\[([^[]*)\])(?:\((data:(.*);[^(]+)\))/, + regExp: /(?:\[([^[]*)\])(?:\((data:(.*);[^(]+)\))$/, + replace: (textNode, match) => { + const [, name, src, mimeType] = match + const inlineFileNode = $createInlineFileNode(src, mimeType, name) + textNode.replace(inlineFileNode) + }, + trigger: ')', + type: 'text-match', +} + // Table transformer, taken from Lexical Playground const TABLE_ROW_REG_EXP = /^(?:\|)(.+)(?:\|)\s?$/ const TABLE_ROW_DIVIDER_REG_EXP = /^(\| ?:?-*:? ?)+\|\s?$/ @@ -224,6 +245,7 @@ export const MarkdownTransformers = [ TABLE, CHECK_LIST, IMAGE, + INLINE_FILE, ...ELEMENT_TRANSFORMERS, ...TEXT_FORMAT_TRANSFORMERS, ...TEXT_MATCH_TRANSFORMERS, diff --git a/packages/web/src/javascripts/Components/SuperEditor/Tools/HeadlessSuperConverter.tsx b/packages/web/src/javascripts/Components/SuperEditor/Tools/HeadlessSuperConverter.tsx index 10413d923..56ae5ae5b 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Tools/HeadlessSuperConverter.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Tools/HeadlessSuperConverter.tsx @@ -16,8 +16,9 @@ import { MarkdownTransformers } from '../MarkdownTransformers' import { $generateHtmlFromNodes, $generateNodesFromDOM } from '@lexical/html' import { FileNode } from '../Plugins/EncryptedFilePlugin/Nodes/FileNode' import { $createFileExportNode } from '../Lexical/Nodes/FileExportNode' -import { $createInlineFileNode, InlineFileNode } from '../Plugins/InlineFilePlugin/InlineFileNode' +import { $createInlineFileNode, $isInlineFileNode, InlineFileNode } from '../Plugins/InlineFilePlugin/InlineFileNode' import { $createFileNode } from '../Plugins/EncryptedFilePlugin/Nodes/FileUtils' +import { RemoteImageNode } from '../Plugins/RemoteImagePlugin/RemoteImageNode' export class HeadlessSuperConverter implements SuperConverterServiceInterface { private importEditor: LexicalEditor private exportEditor: LexicalEditor @@ -271,15 +272,19 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface { this.importEditor.update( () => { const inlineFileNodes = $nodesOfType(InlineFileNode) - if (inlineFileNodes.length === 0) { + const remoteImageNodes = $nodesOfType(RemoteImageNode).filter((node) => node.__src.startsWith('data:')) + const concatenatedNodes = [...inlineFileNodes, ...remoteImageNodes] + if (concatenatedNodes.length === 0) { resolve() return } Promise.all( - inlineFileNodes.map(async (node) => { + concatenatedNodes.map(async (node) => { const blob = await fetch(node.__src).then((response) => response.blob()) - const file = new File([blob], node.__fileName || generateUuid.execute().getValue(), { - type: node.__mimeType, + const name = $isInlineFileNode(node) ? node.__fileName : node.__alt + const mimeType = $isInlineFileNode(node) ? node.__mimeType : node.__src.split(';')[0].split(':')[1] + const file = new File([blob], name || generateUuid.execute().getValue(), { + type: mimeType, }) const uploadedFile = await uploadFile(file)