Upload images to S3 on selection

This commit is contained in:
ekzyis 2023-10-19 02:14:17 +02:00 committed by ekzyis
parent de7b8547ec
commit 7a83813c85
2 changed files with 60 additions and 3 deletions

View File

@ -222,8 +222,12 @@ export function MarkdownInput ({ label, topLevel, groupClassName, onChange, setH
<Nav.Link className={styles.previewTab} eventKey='preview' disabled={!meta.value}>preview</Nav.Link> <Nav.Link className={styles.previewTab} eventKey='preview' disabled={!meta.value}>preview</Nav.Link>
</Nav.Item> </Nav.Item>
<span className='ms-auto text-muted d-flex align-items-center'> <span className='ms-auto text-muted d-flex align-items-center'>
<ImageUpload className='d-flex align-items-center me-1'> <ImageUpload
<AddImageIcon width={18} height={18} onClick={() => console.log('icon click')} /> className='d-flex align-items-center me-1' onSuccess={(url) => {
console.log('image uploaded to', url)
}}
>
<AddImageIcon width={18} height={18} />
</ImageUpload> </ImageUpload>
<a <a
className='d-flex align-items-center' className='d-flex align-items-center'

View File

@ -6,6 +6,8 @@ import { useMe } from './me'
import { Dropdown } from 'react-bootstrap' import { Dropdown } from 'react-bootstrap'
import { UPLOAD_TYPES_ALLOW } from '../lib/constants' import { UPLOAD_TYPES_ALLOW } from '../lib/constants'
import { useToast } from './toast' import { useToast } from './toast'
import gql from 'graphql-tag'
import { useMutation } from '@apollo/client'
export function decodeOriginalUrl (imgproxyUrl) { export function decodeOriginalUrl (imgproxyUrl) {
const parts = imgproxyUrl.split('/') const parts = imgproxyUrl.split('/')
@ -139,6 +141,57 @@ export function ImageUpload ({ children, className, onSelect, onSuccess }) {
const ref = useRef() const ref = useRef()
const toaster = useToast() const toaster = useToast()
const [getSignedPOST] = useMutation(
gql`
mutation getSignedPOST($type: String!, $size: Int!, $width: Int!, $height: Int!) {
getSignedPOST(type: $type, size: $size, width: $width, height: $height) {
url
fields
}
}`)
const s3Upload = useCallback(file => {
const img = new window.Image()
img.src = window.URL.createObjectURL(file)
img.onload = async () => {
let data
try {
({ data } = await getSignedPOST({
variables: {
type: file.type,
size: file.size,
width: img.width,
height: img.height
}
}))
} catch (e) {
toaster.danger(e.message || e.toString?.())
return
}
const form = new FormData()
Object.keys(data.getSignedPOST.fields).forEach(key => form.append(key, data.getSignedPOST.fields[key]))
form.append('Content-Type', file.type)
form.append('Cache-Control', 'max-age=31536000')
form.append('acl', 'public-read')
form.append('file', file)
const res = await fetch(data.getSignedPOST.url, {
method: 'POST',
body: form
})
if (!res.ok) {
// TODO make sure this is actually a helpful error message and does not expose anything to the user we don't want
toaster.danger(res.statusText)
return
}
const url = `https://${process.env.NEXT_PUBLIC_AWS_UPLOAD_BUCKET}.s3.amazonaws.com/${data.getSignedPOST.fields.key}`
onSuccess?.(url)
}
}, [toaster, getSignedPOST])
return ( return (
<> <>
<input <input
@ -155,7 +208,7 @@ export function ImageUpload ({ children, className, onSelect, onSuccess }) {
toaster.danger(`image must be ${UPLOAD_TYPES_ALLOW.map(t => t.replace('image/', '')).join(', ')}`) toaster.danger(`image must be ${UPLOAD_TYPES_ALLOW.map(t => t.replace('image/', '')).join(', ')}`)
return return
} }
onSelect?.(file) s3Upload(file)
// TODO find out if this is needed and if so, why (copied from components/upload.js) // TODO find out if this is needed and if so, why (copied from components/upload.js)
e.target.value = null e.target.value = null
}} }}