Add images - display, paste + uploading

This commit is contained in:
Sinclair Chen 2022-07-06 19:04:42 -07:00
parent 3f30becb93
commit ac436480f5
5 changed files with 36 additions and 2 deletions

View File

@ -8,6 +8,7 @@
}, },
"sideEffects": false, "sideEffects": false,
"dependencies": { "dependencies": {
"@tiptap/extension-image": "^2.0.0-beta.30",
"@tiptap/starter-kit": "^2.0.0-beta.190", "@tiptap/starter-kit": "^2.0.0-beta.190",
"lodash": "4.17.21" "lodash": "4.17.21"
}, },

View File

@ -1,6 +1,7 @@
import { MAX_TAG_LENGTH } from '../contract' import { MAX_TAG_LENGTH } from '../contract'
import { generateText, JSONContent, Extension } from '@tiptap/core' import { generateText, JSONContent, Extension } from '@tiptap/core'
import * as StarterKit from '@tiptap/starter-kit' // needed for cjs import to work on firebase 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) { export function parseTags(text: string) {
const regex = /(?:^|\s)(?:[#][a-z0-9_]+)/gi const regex = /(?:^|\s)(?:[#][a-z0-9_]+)/gi
@ -33,5 +34,5 @@ export function parseWordsAsTags(text: string) {
export function richTextToString(text: JSONContent | string) { export function richTextToString(text: JSONContent | string) {
return typeof text === 'string' return typeof text === 'string'
? text ? text
: generateText(text, [StarterKit as unknown as Extension]) : generateText(text, [StarterKit as unknown as Extension, Image])
} }

View File

@ -2,9 +2,11 @@ import CharacterCount from '@tiptap/extension-character-count'
import Placeholder from '@tiptap/extension-placeholder' import Placeholder from '@tiptap/extension-placeholder'
import { useEditor, EditorContent, JSONContent, Content } from '@tiptap/react' import { useEditor, EditorContent, JSONContent, Content } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit' import StarterKit from '@tiptap/starter-kit'
import { Image } from '@tiptap/extension-image'
import clsx from 'clsx' import clsx from 'clsx'
import { useEffect } from 'react' import { useEffect } from 'react'
import { Linkify } from './linkify' import { Linkify } from './linkify'
import { uploadImage } from 'web/lib/firebase/storage'
const LINE_HEIGHT = 2 const LINE_HEIGHT = 2
@ -26,11 +28,35 @@ export function useTextEditor(props: {
attributes: { attributes: {
class: clsx(proseClass, rowsClass, 'textarea textarea-bordered'), 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: [ extensions: [
StarterKit, StarterKit,
Placeholder.configure({ placeholder }), Placeholder.configure({ placeholder }),
CharacterCount.configure({ limit: max }), CharacterCount.configure({ limit: max }),
Image,
], ],
content: defaultValue, content: defaultValue,
}) })
@ -46,7 +72,7 @@ function RichContent(props: { content: JSONContent }) {
const { content } = props const { content } = props
const editor = useEditor({ const editor = useEditor({
editorProps: { attributes: { class: proseClass } }, editorProps: { attributes: { class: proseClass } },
extensions: [StarterKit], extensions: [StarterKit, Image],
content, content,
editable: false, editable: false,
}) })

View File

@ -25,6 +25,7 @@
"@nivo/line": "0.74.0", "@nivo/line": "0.74.0",
"@react-query-firebase/firestore": "0.4.2", "@react-query-firebase/firestore": "0.4.2",
"@tiptap/extension-character-count": "^2.0.0-beta.31", "@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/extension-placeholder": "^2.0.0-beta.53",
"@tiptap/react": "^2.0.0-beta.114", "@tiptap/react": "^2.0.0-beta.114",
"@tiptap/starter-kit": "^2.0.0-beta.190", "@tiptap/starter-kit": "^2.0.0-beta.190",

View File

@ -2974,6 +2974,11 @@
dependencies: dependencies:
prosemirror-state "1.4.1" 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": "@tiptap/extension-italic@^2.0.0-beta.28":
version "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" resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.28.tgz#bf88ecae64c8f2f69f1f508b802c1efd7454a84e"