2
0

refactor(editor): ♻️ Undo / Redo buttons + structure refacto

Yet another huge refacto... While implementing undo and redo features I understood that I updated the stored typebot too many times (i.e. on each key input) so I had to rethink it entirely. I also moved around some files.
This commit is contained in:
Baptiste Arnaud
2022-02-02 08:05:02 +01:00
parent fc1d654772
commit 8a350eee6c
153 changed files with 1512 additions and 1352 deletions

View File

@ -0,0 +1,104 @@
import { Text } from '@chakra-ui/react'
import {
Step,
StartStep,
BubbleStepType,
InputStepType,
LogicStepType,
IntegrationStepType,
} from 'models'
import { isInputStep } from 'utils'
import { ButtonNodesList } from '../../ButtonNode'
import {
ConditionContent,
SetVariableContent,
TextBubbleContent,
VideoBubbleContent,
WebhookContent,
WithVariableContent,
} from './contents'
import { ConfigureContent } from './contents/ConfigureContent'
import { ImageBubbleContent } from './contents/ImageBubbleContent'
import { PlaceholderContent } from './contents/PlaceholderContent'
type Props = {
step: Step | StartStep
isConnectable?: boolean
}
export const StepNodeContent = ({ step }: Props) => {
if (isInputStep(step) && step.options.variableId) {
return <WithVariableContent step={step} />
}
switch (step.type) {
case BubbleStepType.TEXT: {
return <TextBubbleContent step={step} />
}
case BubbleStepType.IMAGE: {
return <ImageBubbleContent step={step} />
}
case BubbleStepType.VIDEO: {
return <VideoBubbleContent step={step} />
}
case InputStepType.TEXT:
case InputStepType.NUMBER:
case InputStepType.EMAIL:
case InputStepType.URL:
case InputStepType.PHONE: {
return (
<PlaceholderContent placeholder={step.options.labels.placeholder} />
)
}
case InputStepType.DATE: {
return <Text color={'gray.500'}>Pick a date...</Text>
}
case InputStepType.CHOICE: {
return <ButtonNodesList step={step} />
}
case LogicStepType.SET_VARIABLE: {
return <SetVariableContent step={step} />
}
case LogicStepType.CONDITION: {
return <ConditionContent step={step} />
}
case LogicStepType.REDIRECT: {
return (
<ConfigureContent
label={
step.options?.url ? `Redirect to ${step.options?.url}` : undefined
}
/>
)
}
case IntegrationStepType.GOOGLE_SHEETS: {
return (
<ConfigureContent
label={
step.options && 'action' in step.options
? step.options.action
: undefined
}
/>
)
}
case IntegrationStepType.GOOGLE_ANALYTICS: {
return (
<ConfigureContent
label={
step.options?.action
? `Track "${step.options?.action}" `
: undefined
}
/>
)
}
case IntegrationStepType.WEBHOOK: {
return <WebhookContent step={step} />
}
case 'start': {
return <Text>Start</Text>
}
default: {
return <Text>No input</Text>
}
}
}

View File

@ -0,0 +1,57 @@
import { Flex, Stack, HStack, Tag, Text } from '@chakra-ui/react'
import { useTypebot } from 'contexts/TypebotContext'
import { ConditionStep } from 'models'
import { SourceEndpoint } from '../../../../Endpoints/SourceEndpoint'
export const ConditionContent = ({ step }: { step: ConditionStep }) => {
const { typebot } = useTypebot()
return (
<Flex>
{step.options?.comparisons.allIds.length === 0 ? (
<Text color={'gray.500'}>Configure...</Text>
) : (
<Stack>
{step.options?.comparisons.allIds.map((comparisonId, idx) => {
const comparison = step.options?.comparisons.byId[comparisonId]
const variable =
typebot?.variables.byId[comparison?.variableId ?? '']
return (
<HStack key={comparisonId} spacing={1}>
{idx > 0 && <Text>{step.options?.logicalOperator ?? ''}</Text>}
{variable?.name && (
<Tag bgColor="orange.400">{variable.name}</Tag>
)}
{comparison.comparisonOperator && (
<Text>{comparison?.comparisonOperator}</Text>
)}
{comparison?.value && (
<Tag bgColor={'green.400'}>{comparison.value}</Tag>
)}
</HStack>
)
})}
</Stack>
)}
<SourceEndpoint
source={{
blockId: step.blockId,
stepId: step.id,
conditionType: 'true',
}}
pos="absolute"
top="7px"
right="15px"
/>
<SourceEndpoint
source={{
blockId: step.blockId,
stepId: step.id,
conditionType: 'false',
}}
pos="absolute"
bottom="7px"
right="15px"
/>
</Flex>
)
}

View File

@ -0,0 +1,10 @@
import React from 'react'
import { Text } from '@chakra-ui/react'
type Props = { label?: string }
export const ConfigureContent = ({ label }: Props) => (
<Text color={label ? 'currentcolor' : 'gray.500'} isTruncated>
{label ?? 'Configure...'}
</Text>
)

View File

@ -0,0 +1,16 @@
import { Box, Text, Image } from '@chakra-ui/react'
import { ImageBubbleStep } from 'models'
export const ImageBubbleContent = ({ step }: { step: ImageBubbleStep }) =>
!step.content?.url ? (
<Text color={'gray.500'}>Click to edit...</Text>
) : (
<Box w="full">
<Image
src={step.content?.url}
alt="Step image"
rounded="md"
objectFit="cover"
/>
</Box>
)

View File

@ -0,0 +1,8 @@
import React from 'react'
import { Text } from '@chakra-ui/react'
type Props = { placeholder: string }
export const PlaceholderContent = ({ placeholder }: Props) => (
<Text color={'gray.500'}>{placeholder}</Text>
)

View File

@ -0,0 +1,17 @@
import { Text } from '@chakra-ui/react'
import { useTypebot } from 'contexts/TypebotContext'
import { SetVariableStep } from 'models'
export const SetVariableContent = ({ step }: { step: SetVariableStep }) => {
const { typebot } = useTypebot()
const variableName =
typebot?.variables.byId[step.options?.variableId ?? '']?.name ?? ''
const expression = step.options?.expressionToEvaluate ?? ''
return (
<Text color={'gray.500'}>
{variableName === '' && expression === ''
? 'Click to edit...'
: `${variableName} = ${expression}`}
</Text>
)
}

View File

@ -0,0 +1,26 @@
import { Flex } from '@chakra-ui/react'
import { useTypebot } from 'contexts/TypebotContext'
import { TextBubbleStep } from 'models'
import React from 'react'
import { parseVariableHighlight } from 'services/utils'
type Props = {
step: TextBubbleStep
}
export const TextBubbleContent = ({ step }: Props) => {
const { typebot } = useTypebot()
return (
<Flex
flexDir={'column'}
opacity={step.content.html === '' ? '0.5' : '1'}
className="slate-html-container"
dangerouslySetInnerHTML={{
__html:
step.content.html === ''
? `<p>Click to edit...</p>`
: parseVariableHighlight(step.content.html, typebot),
}}
/>
)
}

View File

@ -0,0 +1,51 @@
import { Box, Text } from '@chakra-ui/react'
import { VideoBubbleStep, VideoBubbleContentType } from 'models'
export const VideoBubbleContent = ({ step }: { step: VideoBubbleStep }) => {
if (!step.content?.url || !step.content.type)
return <Text color="gray.500">Click to edit...</Text>
switch (step.content.type) {
case VideoBubbleContentType.URL:
return (
<Box w="full" h="120px" pos="relative">
<video
key={step.content.url}
controls
style={{
width: '100%',
height: '100%',
position: 'absolute',
left: '0',
top: '0',
borderRadius: '10px',
}}
>
<source src={step.content.url} />
</video>
</Box>
)
case VideoBubbleContentType.VIMEO:
case VideoBubbleContentType.YOUTUBE: {
const baseUrl =
step.content.type === VideoBubbleContentType.VIMEO
? 'https://player.vimeo.com/video'
: 'https://www.youtube.com/embed'
return (
<Box w="full" h="120px" pos="relative">
<iframe
src={`${baseUrl}/${step.content.id}`}
allowFullScreen
style={{
width: '100%',
height: '100%',
position: 'absolute',
left: '0',
top: '0',
borderRadius: '10px',
}}
/>
</Box>
)
}
}
}

View File

@ -0,0 +1,22 @@
import { Text } from '@chakra-ui/react'
import { useTypebot } from 'contexts/TypebotContext'
import { WebhookStep } from 'models'
import { useMemo } from 'react'
type Props = {
step: WebhookStep
}
export const WebhookContent = ({ step }: Props) => {
const { typebot } = useTypebot()
const webhook = useMemo(
() => typebot?.webhooks.byId[step.options?.webhookId ?? ''],
[step.options?.webhookId, typebot?.webhooks.byId]
)
if (!webhook?.url) return <Text color="gray.500">Configure...</Text>
return (
<Text isTruncated pr="6">
{webhook.method} {webhook.url}
</Text>
)
}

View File

@ -0,0 +1,28 @@
import { InputStep } from 'models'
import { chakra, Text } from '@chakra-ui/react'
import React from 'react'
import { useTypebot } from 'contexts/TypebotContext'
type Props = {
step: InputStep
}
export const WithVariableContent = ({ step }: Props) => {
const { typebot } = useTypebot()
const variableName =
typebot?.variables.byId[step.options.variableId as string].name
return (
<Text>
Collect{' '}
<chakra.span
bgColor="orange.400"
color="white"
rounded="md"
py="0.5"
px="1"
>
{variableName}
</chakra.span>
</Text>
)
}

View File

@ -0,0 +1,6 @@
export * from './ConditionContent'
export * from './SetVariableContent'
export * from './WithVariableContent'
export * from './VideoBubbleContent'
export * from './WebhookContent'
export * from './TextBubbleContent'

View File

@ -0,0 +1 @@
export { StepNodeContent } from './StepNodeContent'