Skip to content

Commit

Permalink
♻️ Improve file upload management
Browse files Browse the repository at this point in the history
Closes #138
  • Loading branch information
baptisteArno committed Nov 7, 2022
1 parent 1f44e8f commit d102fe1
Show file tree
Hide file tree
Showing 16 changed files with 110 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const MyAccountForm = () => {
<Stack>
<UploadButton
size="sm"
filePath={`public/users/${user?.id}/avatar`}
filePath={`users/${user?.id}/avatar`}
leftIcon={<UploadIcon />}
onFileUploaded={handleFileUploaded}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,14 @@ export const WorkspaceSettingsForm = ({ onClose }: { onClose: () => void }) => {
<FormControl>
<FormLabel>Icon</FormLabel>
<Flex>
<EditableEmojiOrImageIcon
icon={workspace?.icon}
onChangeIcon={handleChangeIcon}
boxSize="40px"
/>
{workspace && (
<EditableEmojiOrImageIcon
uploadFilePath={`workspaces/${workspace.id}/icon`}
icon={workspace.icon}
onChangeIcon={handleChangeIcon}
boxSize="40px"
/>
)}
</Flex>
</FormControl>
<FormControl>
Expand Down
8 changes: 6 additions & 2 deletions apps/builder/components/settings/MetadataForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ import { CodeEditor } from 'components/shared/CodeEditor'
import { MoreInfoTooltip } from 'components/shared/MoreInfoTooltip'

type Props = {
typebotId: string
typebotName: string
metadata: Metadata
onMetadataChange: (metadata: Metadata) => void
}

export const MetadataForm = ({
typebotId,
typebotName,
metadata,
onMetadataChange,
Expand Down Expand Up @@ -57,7 +59,8 @@ export const MetadataForm = ({
</PopoverTrigger>
<PopoverContent p="4">
<ImageUploadContent
url={metadata.favIconUrl ?? ''}
filePath={`typebots/${typebotId}/favIcon`}
defaultUrl={metadata.favIconUrl ?? ''}
onSubmit={handleFavIconSubmit}
isGiphyEnabled={false}
/>
Expand All @@ -81,7 +84,8 @@ export const MetadataForm = ({
</PopoverTrigger>
<PopoverContent p="4">
<ImageUploadContent
url={metadata.imageUrl}
filePath={`typebots/${typebotId}/ogImage`}
defaultUrl={metadata.imageUrl}
onSubmit={handleImageSubmit}
isGiphyEnabled={false}
/>
Expand Down
1 change: 1 addition & 0 deletions apps/builder/components/settings/SettingsSideMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export const SettingsSideMenu = () => {
<AccordionPanel pb={4} px="6">
{typebot && (
<MetadataForm
typebotId={typebot.id}
typebotName={typebot.name}
metadata={typebot.settings.metadata}
onMetadataChange={handleMetadataChange}
Expand Down
5 changes: 4 additions & 1 deletion apps/builder/components/shared/EditableEmojiOrImageIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import { EmojiOrImageIcon } from './EmojiOrImageIcon'
import { ImageUploadContent } from './ImageUploadContent'

type Props = {
uploadFilePath: string
icon?: string | null
onChangeIcon: (icon: string) => void
boxSize?: string
}

export const EditableEmojiOrImageIcon = ({
uploadFilePath,
icon,
onChangeIcon,
boxSize,
Expand Down Expand Up @@ -47,7 +49,8 @@ export const EditableEmojiOrImageIcon = ({
</Tooltip>
<PopoverContent p="2">
<ImageUploadContent
url={icon ?? ''}
filePath={uploadFilePath}
defaultUrl={icon ?? ''}
onSubmit={onChangeIcon}
isGiphyEnabled={false}
isEmojiEnabled={true}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const BlockNode = ({
setFocusedGroupId,
previewingEdge,
} = useGraph()
const { updateBlock } = useTypebot()
const { typebot, updateBlock } = useTypebot()
const [isConnecting, setIsConnecting] = useState(false)
const [isPopoverOpened, setIsPopoverOpened] = useState(
openedBlockId === block.id
Expand Down Expand Up @@ -227,8 +227,9 @@ export const BlockNode = ({
</SettingsModal>
</>
)}
{isMediaBubbleBlock(block) && (
{typebot && isMediaBubbleBlock(block) && (
<MediaBubblePopoverContent
typebotId={typebot.id}
block={block}
onContentChange={handleContentChange}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { EmbedUploadContent } from './EmbedUploadContent'
import { VideoUploadContent } from './VideoUploadContent'

type Props = {
typebotId: string
block: Exclude<BubbleBlock, TextBubbleBlock>
onContentChange: (content: BubbleBlockContent) => void
}
Expand All @@ -39,14 +40,19 @@ export const MediaBubblePopoverContent = (props: Props) => {
)
}

export const MediaBubbleContent = ({ block, onContentChange }: Props) => {
export const MediaBubbleContent = ({
typebotId,
block,
onContentChange,
}: Props) => {
const handleImageUrlChange = (url: string) => onContentChange({ url })

switch (block.type) {
case BubbleBlockType.IMAGE: {
return (
<ImageUploadContent
url={block.content?.url}
filePath={`typebots/${typebotId}/blocks/${block.id}`}
defaultUrl={block.content?.url}
onSubmit={handleImageUrlChange}
/>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ import { useState } from 'react'
import { Button, Flex, HStack, Stack } from '@chakra-ui/react'
import { UploadButton } from '../buttons/UploadButton'
import { GiphySearchForm } from './GiphySearchForm'
import { useTypebot } from 'contexts/TypebotContext'
import { Input } from '../Textbox/Input'
import { EmojiSearchableList } from './emoji/EmojiSearchableList'

type Props = {
url?: string
filePath: string
includeFileName?: boolean
defaultUrl?: string
isEmojiEnabled?: boolean
isGiphyEnabled?: boolean
onSubmit: (url: string) => void
onClose?: () => void
}

export const ImageUploadContent = ({
url,
filePath,
includeFileName,
defaultUrl,
onSubmit,
isEmojiEnabled = false,
isGiphyEnabled = true,
Expand Down Expand Up @@ -67,56 +70,76 @@ export const ImageUploadContent = ({
)}
</HStack>

<BodyContent tab={currentTab} onSubmit={handleSubmit} url={url} />
<BodyContent
filePath={filePath}
includeFileName={includeFileName}
tab={currentTab}
onSubmit={handleSubmit}
defaultUrl={defaultUrl}
/>
</Stack>
)
}

const BodyContent = ({
includeFileName,
filePath,
tab,
url,
defaultUrl,
onSubmit,
}: {
includeFileName?: boolean
filePath: string
tab: 'upload' | 'link' | 'giphy' | 'emoji'
url?: string
defaultUrl?: string
onSubmit: (url: string) => void
}) => {
switch (tab) {
case 'upload':
return <UploadFileContent onNewUrl={onSubmit} />
return (
<UploadFileContent
filePath={filePath}
includeFileName={includeFileName}
onNewUrl={onSubmit}
/>
)
case 'link':
return <EmbedLinkContent initialUrl={url} onNewUrl={onSubmit} />
return <EmbedLinkContent defaultUrl={defaultUrl} onNewUrl={onSubmit} />
case 'giphy':
return <GiphyContent onNewUrl={onSubmit} />
case 'emoji':
return <EmojiSearchableList onEmojiSelected={onSubmit} />
}
}

type ContentProps = { initialUrl?: string; onNewUrl: (url: string) => void }
type ContentProps = { onNewUrl: (url: string) => void }

const UploadFileContent = ({ onNewUrl }: ContentProps) => {
const { typebot } = useTypebot()
return (
<Flex justify="center" py="2">
<UploadButton
filePath={`public/typebots/${typebot?.id}`}
onFileUploaded={onNewUrl}
includeFileName
colorScheme="blue"
>
Choose an image
</UploadButton>
</Flex>
)
}
const UploadFileContent = ({
filePath,
includeFileName,
onNewUrl,
}: ContentProps & { filePath: string; includeFileName?: boolean }) => (
<Flex justify="center" py="2">
<UploadButton
filePath={filePath}
onFileUploaded={onNewUrl}
includeFileName={includeFileName}
colorScheme="blue"
>
Choose an image
</UploadButton>
</Flex>
)

const EmbedLinkContent = ({ initialUrl, onNewUrl }: ContentProps) => (
const EmbedLinkContent = ({
defaultUrl,
onNewUrl,
}: ContentProps & { defaultUrl?: string }) => (
<Stack py="2">
<Input
placeholder={'Paste the image link...'}
onChange={onNewUrl}
defaultValue={initialUrl ?? ''}
defaultValue={defaultUrl ?? ''}
/>
</Stack>
)
Expand Down
11 changes: 7 additions & 4 deletions apps/builder/components/shared/TypebotHeader/TypebotHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,13 @@ export const TypebotHeader = () => {
size="sm"
/>
<HStack spacing={1}>
<EditableEmojiOrImageIcon
icon={typebot?.icon}
onChangeIcon={handleChangeIcon}
/>
{typebot && (
<EditableEmojiOrImageIcon
uploadFilePath={`typebots/${typebot.id}/icon`}
icon={typebot?.icon}
onChangeIcon={handleChangeIcon}
/>
)}
{typebot?.name && (
<EditableTypebotName
name={typebot?.name}
Expand Down
2 changes: 1 addition & 1 deletion apps/builder/components/shared/buttons/UploadButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const UploadButton = ({
files: [
{
file: await compressFile(file),
path: filePath + (includeFileName ? `/${file.name}` : ''),
path: `public/${filePath}${includeFileName ? `/${file.name}` : ''}`,
},
],
})
Expand Down
5 changes: 4 additions & 1 deletion apps/builder/components/theme/ChatSettings/AvatarForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import { ImageUploadContent } from 'components/shared/ImageUploadContent'
import { DefaultAvatar } from 'assets/DefaultAvatar'

type Props = {
uploadFilePath: string
title: string
avatarProps?: AvatarProps
isDefaultCheck?: boolean
onAvatarChange: (avatarProps: AvatarProps) => void
}

export const AvatarForm = ({
uploadFilePath,
title,
avatarProps,
isDefaultCheck = false,
Expand Down Expand Up @@ -71,7 +73,8 @@ export const AvatarForm = ({
<Portal>
<PopoverContent p="4">
<ImageUploadContent
url={avatarProps?.url}
filePath={uploadFilePath}
defaultUrl={avatarProps?.url}
onSubmit={handleImageUrl}
/>
</PopoverContent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ import { HostBubbles } from './HostBubbles'
import { InputsTheme } from './InputsTheme'

type Props = {
typebotId: string
chatTheme: ChatTheme
onChatThemeChange: (chatTheme: ChatTheme) => void
}

export const ChatThemeSettings = ({ chatTheme, onChatThemeChange }: Props) => {
export const ChatThemeSettings = ({
typebotId,
chatTheme,
onChatThemeChange,
}: Props) => {
const handleHostBubblesChange = (hostBubbles: ContainerColors) =>
onChatThemeChange({ ...chatTheme, hostBubbles })
const handleGuestBubblesChange = (guestBubbles: ContainerColors) =>
Expand All @@ -30,12 +35,14 @@ export const ChatThemeSettings = ({ chatTheme, onChatThemeChange }: Props) => {
return (
<Stack spacing={6}>
<AvatarForm
uploadFilePath={`typebots/${typebotId}/hostAvatar`}
title="Bot avatar"
avatarProps={chatTheme.hostAvatar}
isDefaultCheck
onAvatarChange={handleHostAvatarChange}
/>
<AvatarForm
uploadFilePath={`typebots/${typebotId}/guestAvatar`}
title="User avatar"
avatarProps={chatTheme.guestAvatar}
onAvatarChange={handleGuestAvatarChange}
Expand Down
1 change: 1 addition & 0 deletions apps/builder/components/theme/ThemeSideMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export const ThemeSideMenu = () => {
<AccordionPanel pb={4}>
{typebot && (
<ChatThemeSettings
typebotId={typebot.id}
chatTheme={typebot.theme.chat}
onChatThemeChange={handleChatThemeChange}
/>
Expand Down
4 changes: 3 additions & 1 deletion apps/builder/playwright/tests/accountSettings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ test('should display user info properly', async ({ page }) => {
await expect(page.locator('img >> nth=1')).toHaveAttribute(
'src',
new RegExp(
`${process.env.S3_ENDPOINT}/${process.env.S3_BUCKET}/public/users/${userId}/avatar`,
`${process.env.S3_ENDPOINT}${
process.env.S3_PORT ? `:${process.env.S3_PORT}` : ''
}/${process.env.S3_BUCKET}/public/users/${userId}/avatar`,
'gm'
)
)
Expand Down
9 changes: 5 additions & 4 deletions apps/builder/playwright/tests/bubbles/image.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ test.describe.parallel('Image bubble block', () => {
)
await expect(page.locator('img')).toHaveAttribute(
'src',
new RegExp(
`${process.env.S3_ENDPOINT}/${process.env.S3_BUCKET}/public/typebots/${typebotId}/avatar.jpg`,
'gm'
)
`${process.env.S3_SSL === 'false' ? 'http://' : 'https://'}${
process.env.S3_ENDPOINT
}${process.env.S3_PORT ? `:${process.env.S3_PORT}` : ''}/${
process.env.S3_BUCKET
}/public/typebots/${typebotId}/blocks/block1`
)
})

Expand Down
Loading

5 comments on commit d102fe1

@vercel
Copy link

@vercel vercel bot commented on d102fe1 Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d102fe1 Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d102fe1 Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2-alpha – ./apps/viewer

ns8.vn
yobot.me
247987.com
8jours.top
bot.aws.bj
bot.bbc.bj
finplex.be
sat.cr8.ai
bot.aipr.kr
minipost.uk
bot.maitempah.com
bot.phuonghub.com
bot.reviewzer.com
cares.urlabout.me
fmm.wpwakanda.com
gentleman-shop.fr
k1.kandabrand.com
lb.ticketfute.com
ov1.wpwakanda.com
ov2.wpwakanda.com
ov3.wpwakanda.com
1988.bouclidom.com
andreimayer.com.br
bot.megafox.com.br
bot.neferlopez.com
bots.robomotion.io
cadu.uninta.edu.br
dicanatural.online
goalsettingbot.com
positivobra.com.br
survey.digienge.io
this-is-a-test.com
zap.techadviser.in
bot.digitalbled.com
bot.eventhub.com.au
carsalesenquiry.com
demo.botscientis.us
forms.webisharp.com
kbsub.wpwakanda.com
live.botscientis.us
mentoria.omelhor.vc
nutrisamirbayde.com
order.maitempah.com
quest.wpwakanda.com
test.botscientis.us
typebot.stillio.com
bium.gratirabbit.com
bot.ansuraniphone.my
bot.cotemeuplano.com
chat.hayurihijab.com
chatbee.agfunnel.com
click.sevenoways.com
connect.growthguy.in
get.freebotoffer.xyz
kuiz.sistemniaga.com
offer.botscientis.us
sellmycarglasgow.com
talkbot.agfunnel.com
tenorioadvogados.com
uppity.wpwakanda.com
abutton.wpwakanda.com
aidigitalmarketing.kr
bbutton.wpwakanda.com
bot.incusservices.com
bot.meuesocial.com.br
bot.ramonmatos.com.br
cdd.searchcube.com.sg
chat.missarkansas.org
sbutton.wpwakanda.com
815639944.21000000.one
aplicacao.bmind.com.br
apply.ansuraniphone.my
bbutton.wpwwakanda.com
bot.louismarcondes.com
bot.t20worldcup.com.au
c23111azqw.nigerias.io
felipewelington.com.br
form.searchcube.com.sg
gcase.barrettamario.it
help.giversforgood.com
info.clickasuransi.com
kodawariab736.skeep.it
premium.kandabrand.com
report.gratirabbit.com
resume.gratirabbit.com
view.onlinebotdemo.xyz
83242573.actualizar.xyz
bot.blackboxtips.com.br
bot.upgradesolutions.eu
help.comebackreward.com
mainmenu.diddancing.com
register.kandabrand.com
signup.hypemarketing.in
subfooter.wpwakanda.com
survey.hypemarketing.in
testbot.afterorigin.com
91181264.your-access.one
ai.chromebookstoreph.com
form.sergiolimajr.com.br
hunterbot.saleshunter.ai

@vercel
Copy link

@vercel vercel bot commented on d102fe1 Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs – ./apps/docs

docs.typebot.io
docs-typebot-io.vercel.app
docs-git-main-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on d102fe1 Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

app.typebot.io
builder-v2-git-main-typebot-io.vercel.app
builder-v2-typebot-io.vercel.app

Please sign in to comment.