From ac436480f50a8244ee0bc2b6c21d29e811294d80 Mon Sep 17 00:00:00 2001 From: Sinclair Chen Date: Wed, 6 Jul 2022 19:04:42 -0700 Subject: [PATCH] Add images - display, paste + uploading --- common/package.json | 1 + common/util/parse.ts | 3 ++- web/components/editor.tsx | 28 +++++++++++++++++++++++++++- web/package.json | 1 + yarn.lock | 5 +++++ 5 files changed, 36 insertions(+), 2 deletions(-) diff --git a/common/package.json b/common/package.json index 5d17a4d2..d0cb09aa 100644 --- a/common/package.json +++ b/common/package.json @@ -8,6 +8,7 @@ }, "sideEffects": false, "dependencies": { + "@tiptap/extension-image": "^2.0.0-beta.30", "@tiptap/starter-kit": "^2.0.0-beta.190", "lodash": "4.17.21" }, diff --git a/common/util/parse.ts b/common/util/parse.ts index 63da6fee..21cf1760 100644 --- a/common/util/parse.ts +++ b/common/util/parse.ts @@ -1,6 +1,7 @@ import { MAX_TAG_LENGTH } from '../contract' import { generateText, JSONContent, Extension } from '@tiptap/core' import * as StarterKit from '@tiptap/starter-kit' // needed for cjs import to work on firebase +import { Image } from '@tiptap/extension-image' export function parseTags(text: string) { const regex = /(?:^|\s)(?:[#][a-z0-9_]+)/gi @@ -33,5 +34,5 @@ export function parseWordsAsTags(text: string) { export function richTextToString(text: JSONContent | string) { return typeof text === 'string' ? text - : generateText(text, [StarterKit as unknown as Extension]) + : generateText(text, [StarterKit as unknown as Extension, Image]) } diff --git a/web/components/editor.tsx b/web/components/editor.tsx index fb442975..8f6f6929 100644 --- a/web/components/editor.tsx +++ b/web/components/editor.tsx @@ -2,9 +2,11 @@ import CharacterCount from '@tiptap/extension-character-count' import Placeholder from '@tiptap/extension-placeholder' import { useEditor, EditorContent, JSONContent, Content } from '@tiptap/react' import StarterKit from '@tiptap/starter-kit' +import { Image } from '@tiptap/extension-image' import clsx from 'clsx' import { useEffect } from 'react' import { Linkify } from './linkify' +import { uploadImage } from 'web/lib/firebase/storage' const LINE_HEIGHT = 2 @@ -26,11 +28,35 @@ export function useTextEditor(props: { attributes: { class: clsx(proseClass, rowsClass, 'textarea textarea-bordered'), }, + handlePaste(view, event) { + const files = Array.from(event.clipboardData?.files ?? []).filter( + (file) => file.type.startsWith('image') + ) + + if (!files.length) { + return // if no files pasted, use default paste handler + } + + event.preventDefault() + ;(async () => { + // TODO: show loading state, compress large images + const urls = await Promise.all( + files.map((file) => uploadImage('default', file)) + ) + let trans = view.state.tr + urls.forEach((src: any) => { + const node = view.state.schema.nodes.image.create({ src }) + trans = trans.insert(view.state.selection.to, node) + }) + view.dispatch(trans) + })() + }, }, extensions: [ StarterKit, Placeholder.configure({ placeholder }), CharacterCount.configure({ limit: max }), + Image, ], content: defaultValue, }) @@ -46,7 +72,7 @@ function RichContent(props: { content: JSONContent }) { const { content } = props const editor = useEditor({ editorProps: { attributes: { class: proseClass } }, - extensions: [StarterKit], + extensions: [StarterKit, Image], content, editable: false, }) diff --git a/web/package.json b/web/package.json index 917e2b18..85e962fc 100644 --- a/web/package.json +++ b/web/package.json @@ -25,6 +25,7 @@ "@nivo/line": "0.74.0", "@react-query-firebase/firestore": "0.4.2", "@tiptap/extension-character-count": "^2.0.0-beta.31", + "@tiptap/extension-image": "^2.0.0-beta.30", "@tiptap/extension-placeholder": "^2.0.0-beta.53", "@tiptap/react": "^2.0.0-beta.114", "@tiptap/starter-kit": "^2.0.0-beta.190", diff --git a/yarn.lock b/yarn.lock index d0070cbb..1e05d431 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2974,6 +2974,11 @@ dependencies: prosemirror-state "1.4.1" +"@tiptap/extension-image@^2.0.0-beta.30": + version "2.0.0-beta.30" + resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.0.0-beta.30.tgz#60c6cfd09bfd017a3d8b1feaf0931462ffd71a60" + integrity sha512-VhEmgiKkZMiKR7hbpJgIlIUS/QNjSGI5ER7mKDAbuV1IB5yb6nGjZ6o3Exrr2/CaTaW5hQarBC1z2Xgdu05EGg== + "@tiptap/extension-italic@^2.0.0-beta.28": version "2.0.0-beta.28" resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.28.tgz#bf88ecae64c8f2f69f1f508b802c1efd7454a84e"