(s3) Improve storage management and type safety

Closes #756
This commit is contained in:
Baptiste Arnaud
2023-09-08 15:28:11 +02:00
parent 43be38cf50
commit fbb198af9d
47 changed files with 790 additions and 128 deletions

View File

@@ -10,16 +10,17 @@ import {
import React from 'react'
import { EmojiOrImageIcon } from './EmojiOrImageIcon'
import { ImageUploadContent } from './ImageUploadContent'
import { FilePathUploadProps } from '@/features/upload/api/generateUploadUrl'
type Props = {
uploadFilePath: string
uploadFileProps: FilePathUploadProps
icon?: string | null
onChangeIcon: (icon: string) => void
boxSize?: string
}
export const EditableEmojiOrImageIcon = ({
uploadFilePath,
uploadFileProps,
icon,
onChangeIcon,
boxSize,
@@ -54,7 +55,7 @@ export const EditableEmojiOrImageIcon = ({
</Tooltip>
<PopoverContent p="2">
<ImageUploadContent
filePath={uploadFilePath}
uploadFileProps={uploadFileProps}
defaultUrl={icon ?? ''}
onSubmit={onChangeIcon}
excludedTabs={['giphy', 'unsplash']}

View File

@@ -6,12 +6,12 @@ import { TextInput } from '../inputs/TextInput'
import { EmojiSearchableList } from './emoji/EmojiSearchableList'
import { UnsplashPicker } from './UnsplashPicker'
import { IconPicker } from './IconPicker'
import { FilePathUploadProps } from '@/features/upload/api/generateUploadUrl'
type Tabs = 'link' | 'upload' | 'giphy' | 'emoji' | 'unsplash' | 'icon'
type Props = {
filePath: string | undefined
includeFileName?: boolean
uploadFileProps: FilePathUploadProps | undefined
defaultUrl?: string
imageSize?: 'small' | 'regular' | 'thumb'
initialTab?: Tabs
@@ -36,8 +36,7 @@ const defaultDisplayedTabs: Tabs[] = [
]
export const ImageUploadContent = ({
filePath,
includeFileName,
uploadFileProps,
defaultUrl,
onSubmit,
imageSize = 'regular',
@@ -123,8 +122,7 @@ export const ImageUploadContent = ({
</HStack>
<BodyContent
filePath={filePath}
includeFileName={includeFileName}
uploadFileProps={uploadFileProps}
tab={currentTab}
imageSize={imageSize}
onSubmit={handleSubmit}
@@ -135,15 +133,13 @@ export const ImageUploadContent = ({
}
const BodyContent = ({
includeFileName,
filePath,
uploadFileProps,
tab,
defaultUrl,
imageSize,
onSubmit,
}: {
includeFileName?: boolean
filePath: string | undefined
uploadFileProps?: FilePathUploadProps
tab: Tabs
defaultUrl?: string
imageSize: 'small' | 'regular' | 'thumb'
@@ -151,11 +147,10 @@ const BodyContent = ({
}) => {
switch (tab) {
case 'upload': {
if (!filePath) return null
if (!uploadFileProps) return null
return (
<UploadFileContent
filePath={filePath}
includeFileName={includeFileName}
uploadFileProps={uploadFileProps}
onNewUrl={onSubmit}
/>
)
@@ -176,16 +171,14 @@ const BodyContent = ({
type ContentProps = { onNewUrl: (url: string) => void }
const UploadFileContent = ({
filePath,
includeFileName,
uploadFileProps,
onNewUrl,
}: ContentProps & { filePath: string; includeFileName?: boolean }) => (
}: ContentProps & { uploadFileProps: FilePathUploadProps }) => (
<Flex justify="center" py="2">
<UploadButton
fileType="image"
filePath={filePath}
filePathProps={uploadFileProps}
onFileUploaded={onNewUrl}
includeFileName={includeFileName}
colorScheme="blue"
>
Choose an image

View File

@@ -1,25 +1,44 @@
import { useToast } from '@/hooks/useToast'
import { Button, ButtonProps, chakra } from '@chakra-ui/react'
import { ChangeEvent, useState } from 'react'
import { uploadFiles } from '@typebot.io/lib/s3/uploadFiles'
import { FilePathUploadProps } from '@/features/upload/api/generateUploadUrl'
import { trpc } from '@/lib/trpc'
import { compressFile } from '@/helpers/compressFile'
type UploadButtonProps = {
fileType: 'image' | 'audio'
filePath: string
includeFileName?: boolean
filePathProps: FilePathUploadProps
onFileUploaded: (url: string) => void
} & ButtonProps
export const UploadButton = ({
fileType,
filePath,
includeFileName,
filePathProps,
onFileUploaded,
...props
}: UploadButtonProps) => {
const [isUploading, setIsUploading] = useState(false)
const { showToast } = useToast()
const [file, setFile] = useState<File>()
const { mutate } = trpc.generateUploadUrl.useMutation({
onSettled: () => {
setIsUploading(false)
},
onSuccess: async (data) => {
const upload = await fetch(data.presignedUrl, {
method: 'PUT',
body: file,
})
if (!upload.ok) {
showToast({ description: 'Error while trying to upload the file.' })
return
}
onFileUploaded(data.fileUrl + '?v=' + Date.now())
},
})
const handleInputChange = async (e: ChangeEvent<HTMLInputElement>) => {
if (!e.target?.files) return
@@ -27,16 +46,11 @@ export const UploadButton = ({
const file = e.target.files[0] as File | undefined
if (!file)
return showToast({ description: 'Could not read file.', status: 'error' })
const urls = await uploadFiles({
files: [
{
file: await compressFile(file),
path: `public/${filePath}${includeFileName ? `/${file.name}` : ''}`,
},
],
setFile(await compressFile(file))
mutate({
filePathProps,
fileType: file.type,
})
if (urls.length && urls[0]) onFileUploaded(urls[0] + '?v=' + Date.now())
setIsUploading(false)
}
return (