✨ Allow user to share a flow publicly and make it duplicatable
Closes #360
This commit is contained in:
@@ -16,10 +16,17 @@ import { GraphDndProvider } from '@/features/graph/providers/GraphDndProvider'
|
||||
import { GraphProvider } from '@/features/graph/providers/GraphProvider'
|
||||
import { GroupsCoordinatesProvider } from '@/features/graph/providers/GroupsCoordinateProvider'
|
||||
import { EventsCoordinatesProvider } from '@/features/graph/providers/EventsCoordinateProvider'
|
||||
import { TypebotNotFoundPage } from './TypebotNotFoundPage'
|
||||
|
||||
export const EditorPage = () => {
|
||||
const { typebot, isReadOnly } = useTypebot()
|
||||
const { typebot, currentUserMode, is404 } = useTypebot()
|
||||
const backgroundImage = useColorModeValue(
|
||||
'radial-gradient(#c6d0e1 1px, transparent 0)',
|
||||
'radial-gradient(#2f2f39 1px, transparent 0)'
|
||||
)
|
||||
const bgColor = useColorModeValue('#f4f5f8', 'gray.850')
|
||||
|
||||
if (is404) return <TypebotNotFoundPage />
|
||||
return (
|
||||
<EditorProvider>
|
||||
<Seo title={typebot?.name ? `${typebot.name} | Editor` : 'Editor'} />
|
||||
@@ -30,18 +37,19 @@ export const EditorPage = () => {
|
||||
flex="1"
|
||||
pos="relative"
|
||||
h="full"
|
||||
bgColor={useColorModeValue('#f4f5f8', 'gray.850')}
|
||||
backgroundImage={useColorModeValue(
|
||||
'radial-gradient(#c6d0e1 1px, transparent 0)',
|
||||
'radial-gradient(#2f2f39 1px, transparent 0)'
|
||||
)}
|
||||
bgColor={bgColor}
|
||||
backgroundImage={backgroundImage}
|
||||
backgroundSize="40px 40px"
|
||||
backgroundPosition="-19px -19px"
|
||||
>
|
||||
{typebot ? (
|
||||
<GraphDndProvider>
|
||||
{!isReadOnly && <BlocksSideBar />}
|
||||
<GraphProvider isReadOnly={isReadOnly}>
|
||||
{currentUserMode === 'write' && <BlocksSideBar />}
|
||||
<GraphProvider
|
||||
isReadOnly={
|
||||
currentUserMode === 'read' || currentUserMode === 'guest'
|
||||
}
|
||||
>
|
||||
<GroupsCoordinatesProvider groups={typebot.groups}>
|
||||
<EventsCoordinatesProvider events={typebot.events}>
|
||||
<Graph flex="1" typebot={typebot} key={typebot.id} />
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import {
|
||||
BuoyIcon,
|
||||
ChevronLeftIcon,
|
||||
PlayIcon,
|
||||
RedoIcon,
|
||||
UndoIcon,
|
||||
} from '@/components/icons'
|
||||
@@ -23,7 +24,7 @@ import Link from 'next/link'
|
||||
import { EditableEmojiOrImageIcon } from '@/components/EditableEmojiOrImageIcon'
|
||||
import { useUndoShortcut } from '@/hooks/useUndoShortcut'
|
||||
import { useDebouncedCallback } from 'use-debounce'
|
||||
import { CollaborationMenuButton } from '@/features/collaboration/components/CollaborationMenuButton'
|
||||
import { ShareTypebotButton } from '@/features/share/components/ShareTypebotButton'
|
||||
import { PublishButton } from '@/features/publish/components/PublishButton'
|
||||
import { headerHeight } from '../constants'
|
||||
import { RightPanel, useEditor } from '../providers/EditorProvider'
|
||||
@@ -31,6 +32,7 @@ import { useTypebot } from '../providers/TypebotProvider'
|
||||
import { SupportBubble } from '@/components/SupportBubble'
|
||||
import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
|
||||
import { useTranslate } from '@tolgee/react'
|
||||
import { GuestTypebotHeader } from './UnauthenticatedTypebotHeader'
|
||||
|
||||
export const TypebotHeader = () => {
|
||||
const { t } = useTranslate()
|
||||
@@ -45,6 +47,7 @@ export const TypebotHeader = () => {
|
||||
canUndo,
|
||||
canRedo,
|
||||
isSavingLoading,
|
||||
currentUserMode,
|
||||
} = useTypebot()
|
||||
const {
|
||||
setRightPanel,
|
||||
@@ -58,6 +61,7 @@ export const TypebotHeader = () => {
|
||||
setUndoShortcutTooltipOpen(false)
|
||||
}, 1000)
|
||||
const { isOpen, onOpen } = useDisclosure()
|
||||
const headerBgColor = useColorModeValue('white', 'gray.900')
|
||||
|
||||
const handleNameSubmit = (name: string) =>
|
||||
updateTypebot({ updates: { name } })
|
||||
@@ -86,6 +90,7 @@ export const TypebotHeader = () => {
|
||||
: window.open('https://docs.typebot.io', '_blank')
|
||||
}
|
||||
|
||||
if (currentUserMode === 'guest') return <GuestTypebotHeader />
|
||||
return (
|
||||
<Flex
|
||||
w="full"
|
||||
@@ -95,7 +100,7 @@ export const TypebotHeader = () => {
|
||||
h={`${headerHeight}px`}
|
||||
zIndex={100}
|
||||
pos="relative"
|
||||
bgColor={useColorModeValue('white', 'gray.900')}
|
||||
bgColor={headerBgColor}
|
||||
flexShrink={0}
|
||||
>
|
||||
{isOpen && <SupportBubble autoShowDelay={0} />}
|
||||
@@ -203,33 +208,35 @@ export const TypebotHeader = () => {
|
||||
)
|
||||
</HStack>
|
||||
|
||||
<HStack>
|
||||
<Tooltip
|
||||
label={isUndoShortcutTooltipOpen ? 'Changes reverted!' : 'Undo'}
|
||||
isOpen={isUndoShortcutTooltipOpen ? true : undefined}
|
||||
hasArrow={isUndoShortcutTooltipOpen}
|
||||
>
|
||||
<IconButton
|
||||
display={['none', 'flex']}
|
||||
icon={<UndoIcon />}
|
||||
size="sm"
|
||||
aria-label="Undo"
|
||||
onClick={undo}
|
||||
isDisabled={!canUndo}
|
||||
/>
|
||||
</Tooltip>
|
||||
{currentUserMode === 'write' && (
|
||||
<HStack>
|
||||
<Tooltip
|
||||
label={isUndoShortcutTooltipOpen ? 'Changes reverted!' : 'Undo'}
|
||||
isOpen={isUndoShortcutTooltipOpen ? true : undefined}
|
||||
hasArrow={isUndoShortcutTooltipOpen}
|
||||
>
|
||||
<IconButton
|
||||
display={['none', 'flex']}
|
||||
icon={<UndoIcon />}
|
||||
size="sm"
|
||||
aria-label="Undo"
|
||||
onClick={undo}
|
||||
isDisabled={!canUndo}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip label="Redo">
|
||||
<IconButton
|
||||
display={['none', 'flex']}
|
||||
icon={<RedoIcon />}
|
||||
size="sm"
|
||||
aria-label="Redo"
|
||||
onClick={redo}
|
||||
isDisabled={!canRedo}
|
||||
/>
|
||||
</Tooltip>
|
||||
</HStack>
|
||||
<Tooltip label="Redo">
|
||||
<IconButton
|
||||
display={['none', 'flex']}
|
||||
icon={<RedoIcon />}
|
||||
size="sm"
|
||||
aria-label="Redo"
|
||||
onClick={redo}
|
||||
isDisabled={!canRedo}
|
||||
/>
|
||||
</Tooltip>
|
||||
</HStack>
|
||||
)}
|
||||
<Button leftIcon={<BuoyIcon />} onClick={handleHelpClick} size="sm">
|
||||
{t('editor.headers.helpButton.label')}
|
||||
</Button>
|
||||
@@ -246,19 +253,20 @@ export const TypebotHeader = () => {
|
||||
|
||||
<HStack right="40px" pos="absolute" display={['none', 'flex']}>
|
||||
<Flex pos="relative">
|
||||
<CollaborationMenuButton isLoading={isNotDefined(typebot)} />
|
||||
<ShareTypebotButton isLoading={isNotDefined(typebot)} />
|
||||
</Flex>
|
||||
{router.pathname.includes('/edit') && isNotDefined(rightPanel) && (
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
onClick={handlePreviewClick}
|
||||
isLoading={isNotDefined(typebot)}
|
||||
leftIcon={<PlayIcon />}
|
||||
size="sm"
|
||||
>
|
||||
{t('editor.headers.previewButton.label')}
|
||||
</Button>
|
||||
)}
|
||||
<PublishButton size="sm" />
|
||||
{currentUserMode === 'write' && <PublishButton size="sm" />}
|
||||
</HStack>
|
||||
</Flex>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { ChevronLeftIcon } from '@/components/icons'
|
||||
import { useUser } from '@/features/account/hooks/useUser'
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
Heading,
|
||||
Link,
|
||||
VStack,
|
||||
Text,
|
||||
Spinner,
|
||||
} from '@chakra-ui/react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export const TypebotNotFoundPage = () => {
|
||||
const { replace, asPath } = useRouter()
|
||||
const { user, isLoading } = useUser()
|
||||
|
||||
useEffect(() => {
|
||||
if (user || isLoading) return
|
||||
replace({
|
||||
pathname: '/signin',
|
||||
query: {
|
||||
redirectPath: asPath,
|
||||
},
|
||||
})
|
||||
}, [asPath, isLoading, replace, user])
|
||||
|
||||
return (
|
||||
<Flex justify="center" align="center" w="full" h="100vh">
|
||||
{user ? (
|
||||
<VStack spacing={6}>
|
||||
<VStack>
|
||||
<Heading>404</Heading>
|
||||
<Text fontSize="xl">Typebot not found.</Text>
|
||||
</VStack>
|
||||
<Button
|
||||
as={Link}
|
||||
href="/typebots"
|
||||
colorScheme="blue"
|
||||
leftIcon={<ChevronLeftIcon />}
|
||||
>
|
||||
Dashboard
|
||||
</Button>
|
||||
</VStack>
|
||||
) : (
|
||||
<Spinner />
|
||||
)}
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
import {
|
||||
Flex,
|
||||
HStack,
|
||||
Button,
|
||||
useColorModeValue,
|
||||
Divider,
|
||||
Text,
|
||||
} from '@chakra-ui/react'
|
||||
import { CopyIcon, PlayIcon } from '@/components/icons'
|
||||
import { useRouter } from 'next/router'
|
||||
import React from 'react'
|
||||
import { isNotDefined } from '@typebot.io/lib'
|
||||
import Link from 'next/link'
|
||||
import { headerHeight } from '../constants'
|
||||
import { RightPanel, useEditor } from '../providers/EditorProvider'
|
||||
import { useTypebot } from '../providers/TypebotProvider'
|
||||
import { useTranslate } from '@tolgee/react'
|
||||
import { TypebotLogo } from '@/components/TypebotLogo'
|
||||
import { EmojiOrImageIcon } from '@/components/EmojiOrImageIcon'
|
||||
import { useUser } from '@/features/account/hooks/useUser'
|
||||
|
||||
export const GuestTypebotHeader = () => {
|
||||
const { t } = useTranslate()
|
||||
const router = useRouter()
|
||||
const { user } = useUser()
|
||||
const { typebot, save } = useTypebot()
|
||||
const {
|
||||
setRightPanel,
|
||||
rightPanel,
|
||||
setStartPreviewAtGroup,
|
||||
setStartPreviewAtEvent,
|
||||
} = useEditor()
|
||||
|
||||
const handlePreviewClick = async () => {
|
||||
setStartPreviewAtGroup(undefined)
|
||||
setStartPreviewAtEvent(undefined)
|
||||
save().then()
|
||||
setRightPanel(RightPanel.PREVIEW)
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex
|
||||
w="full"
|
||||
borderBottomWidth="1px"
|
||||
justify="center"
|
||||
align="center"
|
||||
h={`${headerHeight}px`}
|
||||
zIndex={100}
|
||||
pos="relative"
|
||||
bgColor={useColorModeValue('white', 'gray.900')}
|
||||
flexShrink={0}
|
||||
>
|
||||
<HStack
|
||||
display={['none', 'flex']}
|
||||
pos={{ base: 'absolute', xl: 'static' }}
|
||||
right={{ base: 280, xl: 0 }}
|
||||
>
|
||||
<Button
|
||||
as={Link}
|
||||
href={`/typebots/${typebot?.id}/edit`}
|
||||
colorScheme={router.pathname.includes('/edit') ? 'blue' : 'gray'}
|
||||
variant={router.pathname.includes('/edit') ? 'outline' : 'ghost'}
|
||||
size="sm"
|
||||
>
|
||||
{t('editor.headers.flowButton.label')}
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
href={`/typebots/${typebot?.id}/theme`}
|
||||
colorScheme={router.pathname.endsWith('theme') ? 'blue' : 'gray'}
|
||||
variant={router.pathname.endsWith('theme') ? 'outline' : 'ghost'}
|
||||
size="sm"
|
||||
>
|
||||
{t('editor.headers.themeButton.label')}
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
href={`/typebots/${typebot?.id}/settings`}
|
||||
colorScheme={router.pathname.endsWith('settings') ? 'blue' : 'gray'}
|
||||
variant={router.pathname.endsWith('settings') ? 'outline' : 'ghost'}
|
||||
size="sm"
|
||||
>
|
||||
{t('editor.headers.settingsButton.label')}
|
||||
</Button>
|
||||
</HStack>
|
||||
<HStack
|
||||
pos="absolute"
|
||||
left="1rem"
|
||||
justify="center"
|
||||
align="center"
|
||||
spacing="6"
|
||||
>
|
||||
<HStack alignItems="center" spacing={3}>
|
||||
{typebot && (
|
||||
<EmojiOrImageIcon icon={typebot.icon} emojiFontSize="2xl" />
|
||||
)}
|
||||
<Text
|
||||
noOfLines={2}
|
||||
maxW="150px"
|
||||
overflow="hidden"
|
||||
fontSize="14px"
|
||||
minW="30px"
|
||||
minH="20px"
|
||||
>
|
||||
{typebot?.name}
|
||||
</Text>
|
||||
</HStack>
|
||||
</HStack>
|
||||
|
||||
<HStack
|
||||
right="1rem"
|
||||
pos="absolute"
|
||||
display={['none', 'flex']}
|
||||
spacing={4}
|
||||
>
|
||||
<HStack>
|
||||
{typebot?.id && (
|
||||
<Button
|
||||
as={Link}
|
||||
href={
|
||||
!user
|
||||
? {
|
||||
pathname: `/register`,
|
||||
query: {
|
||||
redirectPath: `/typebots/${typebot.id}/duplicate`,
|
||||
},
|
||||
}
|
||||
: `/typebots/${typebot.id}/duplicate`
|
||||
}
|
||||
leftIcon={<CopyIcon />}
|
||||
isLoading={isNotDefined(typebot)}
|
||||
size="sm"
|
||||
>
|
||||
Duplicate
|
||||
</Button>
|
||||
)}
|
||||
{router.pathname.includes('/edit') && isNotDefined(rightPanel) && (
|
||||
<Button
|
||||
colorScheme="blue"
|
||||
onClick={handlePreviewClick}
|
||||
isLoading={isNotDefined(typebot)}
|
||||
leftIcon={<PlayIcon />}
|
||||
size="sm"
|
||||
>
|
||||
Play bot
|
||||
</Button>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
{!user && (
|
||||
<>
|
||||
<Divider orientation="vertical" h="25px" borderColor="gray.400" />
|
||||
<Button
|
||||
as={Link}
|
||||
href={`/register`}
|
||||
leftIcon={<TypebotLogo width="20px" />}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
>
|
||||
Try Typebot
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</HStack>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
@@ -207,7 +207,7 @@ test('Preview from group should work', async ({ page }) => {
|
||||
page.locator('typebot-standard').locator('text="Hello this is group 2"')
|
||||
).toBeVisible()
|
||||
await page.click('[aria-label="Close"]')
|
||||
await page.click('text="Preview"')
|
||||
await page.click('text="Test"')
|
||||
await expect(
|
||||
page.locator('typebot-standard').locator('text="Hello this is group 1"')
|
||||
).toBeVisible()
|
||||
|
||||
@@ -23,8 +23,11 @@ const initialState = {
|
||||
future: [],
|
||||
}
|
||||
|
||||
type Params = { isReadOnly?: boolean }
|
||||
|
||||
export const useUndo = <T extends { updatedAt: Date }>(
|
||||
initialPresent?: T
|
||||
initialPresent?: T,
|
||||
params?: Params
|
||||
): [T | undefined, Actions<T>] => {
|
||||
const [history, setHistory] = useState<History<T>>(initialState)
|
||||
const presentRef = useRef<T | null>(initialPresent ?? null)
|
||||
@@ -33,6 +36,7 @@ export const useUndo = <T extends { updatedAt: Date }>(
|
||||
const canRedo = history.future.length !== 0
|
||||
|
||||
const undo = useCallback(() => {
|
||||
if (params?.isReadOnly) return
|
||||
const { past, present, future } = history
|
||||
if (past.length === 0 || !present) return
|
||||
|
||||
@@ -47,9 +51,10 @@ export const useUndo = <T extends { updatedAt: Date }>(
|
||||
future: [present, ...future],
|
||||
})
|
||||
presentRef.current = newPresent
|
||||
}, [history])
|
||||
}, [history, params?.isReadOnly])
|
||||
|
||||
const redo = useCallback(() => {
|
||||
if (params?.isReadOnly) return
|
||||
const { past, present, future } = history
|
||||
if (future.length === 0) return
|
||||
const next = future[0]
|
||||
@@ -61,11 +66,12 @@ export const useUndo = <T extends { updatedAt: Date }>(
|
||||
future: newFuture,
|
||||
})
|
||||
presentRef.current = next
|
||||
}, [history])
|
||||
}, [history, params?.isReadOnly])
|
||||
|
||||
const set = useCallback(
|
||||
(newPresentArg: T | ((current: T) => T) | undefined) => {
|
||||
const { past, present } = history
|
||||
if (isDefined(present) && params?.isReadOnly) return
|
||||
const newPresent =
|
||||
typeof newPresentArg === 'function'
|
||||
? newPresentArg(presentRef.current as T)
|
||||
@@ -92,16 +98,17 @@ export const useUndo = <T extends { updatedAt: Date }>(
|
||||
})
|
||||
presentRef.current = newPresent
|
||||
},
|
||||
[history]
|
||||
[history, params?.isReadOnly]
|
||||
)
|
||||
|
||||
const flush = useCallback(() => {
|
||||
if (params?.isReadOnly) return
|
||||
setHistory({
|
||||
present: presentRef.current ?? undefined,
|
||||
past: [],
|
||||
future: [],
|
||||
})
|
||||
}, [])
|
||||
}, [params?.isReadOnly])
|
||||
|
||||
return [history.present, { set, undo, redo, flush, canUndo, canRedo }]
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { isDefined, omit } from '@typebot.io/lib'
|
||||
import { edgesAction, EdgesActions } from './typebotActions/edges'
|
||||
@@ -52,7 +53,8 @@ const typebotContext = createContext<
|
||||
typebot?: TypebotV6
|
||||
publishedTypebot?: PublicTypebotV6
|
||||
publishedTypebotVersion?: PublicTypebot['version']
|
||||
isReadOnly?: boolean
|
||||
currentUserMode: 'guest' | 'read' | 'write'
|
||||
is404: boolean
|
||||
isPublished: boolean
|
||||
isSavingLoading: boolean
|
||||
save: () => Promise<TypebotV6 | undefined>
|
||||
@@ -84,6 +86,7 @@ export const TypebotProvider = ({
|
||||
}) => {
|
||||
const { push } = useRouter()
|
||||
const { showToast } = useToast()
|
||||
const [is404, setIs404] = useState(false)
|
||||
|
||||
const {
|
||||
data: typebotData,
|
||||
@@ -96,13 +99,10 @@ export const TypebotProvider = ({
|
||||
retry: 0,
|
||||
onError: (error) => {
|
||||
if (error.data?.httpStatus === 404) {
|
||||
showToast({
|
||||
status: 'info',
|
||||
description: "Couldn't find typebot. Redirecting...",
|
||||
})
|
||||
push('/typebots')
|
||||
setIs404(true)
|
||||
return
|
||||
}
|
||||
setIs404(false)
|
||||
showToast({
|
||||
title: 'Could not fetch typebot',
|
||||
description: error.message,
|
||||
@@ -112,6 +112,9 @@ export const TypebotProvider = ({
|
||||
},
|
||||
})
|
||||
},
|
||||
onSuccess: () => {
|
||||
setIs404(false)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -119,7 +122,10 @@ export const TypebotProvider = ({
|
||||
trpc.typebot.getPublishedTypebot.useQuery(
|
||||
{ typebotId: typebotId as string, migrateToLatestVersion: true },
|
||||
{
|
||||
enabled: isDefined(typebotId),
|
||||
enabled:
|
||||
isDefined(typebotId) &&
|
||||
(typebotData?.currentUserMode === 'read' ||
|
||||
typebotData?.currentUserMode === 'write'),
|
||||
onError: (error) => {
|
||||
showToast({
|
||||
title: 'Could not fetch published typebot',
|
||||
@@ -153,11 +159,16 @@ export const TypebotProvider = ({
|
||||
const typebot = typebotData?.typebot as TypebotV6
|
||||
const publishedTypebot = (publishedTypebotData?.publishedTypebot ??
|
||||
undefined) as PublicTypebotV6 | undefined
|
||||
const isReadOnly = ['read', 'guest'].includes(
|
||||
typebotData?.currentUserMode ?? 'guest'
|
||||
)
|
||||
|
||||
const [
|
||||
localTypebot,
|
||||
{ redo, undo, flush, canRedo, canUndo, set: setLocalTypebot },
|
||||
] = useUndo<TypebotV6>(undefined)
|
||||
] = useUndo<TypebotV6>(undefined, {
|
||||
isReadOnly,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!typebot && isDefined(localTypebot)) setLocalTypebot(undefined)
|
||||
@@ -182,7 +193,7 @@ export const TypebotProvider = ({
|
||||
|
||||
const saveTypebot = useCallback(
|
||||
async (updates?: Partial<TypebotV6>) => {
|
||||
if (!localTypebot || !typebot || typebotData?.isReadOnly) return
|
||||
if (!localTypebot || !typebot || isReadOnly) return
|
||||
const typebotToSave = { ...localTypebot, ...updates }
|
||||
if (dequal(omit(typebot, 'updatedAt'), omit(typebotToSave, 'updatedAt')))
|
||||
return
|
||||
@@ -194,13 +205,7 @@ export const TypebotProvider = ({
|
||||
setLocalTypebot({ ...newTypebot })
|
||||
return newTypebot
|
||||
},
|
||||
[
|
||||
localTypebot,
|
||||
setLocalTypebot,
|
||||
typebot,
|
||||
typebotData?.isReadOnly,
|
||||
updateTypebot,
|
||||
]
|
||||
[isReadOnly, localTypebot, setLocalTypebot, typebot, updateTypebot]
|
||||
)
|
||||
|
||||
useAutoSave(
|
||||
@@ -232,7 +237,7 @@ export const TypebotProvider = ({
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!localTypebot || !typebot || typebotData?.isReadOnly) return
|
||||
if (!localTypebot || !typebot || isReadOnly) return
|
||||
if (!areTypebotsEqual(localTypebot, typebot)) {
|
||||
window.addEventListener('beforeunload', preventUserFromRefreshing)
|
||||
}
|
||||
@@ -240,7 +245,7 @@ export const TypebotProvider = ({
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', preventUserFromRefreshing)
|
||||
}
|
||||
}, [localTypebot, typebot, typebotData?.isReadOnly])
|
||||
}, [localTypebot, typebot, isReadOnly])
|
||||
|
||||
const updateLocalTypebot = async ({
|
||||
updates,
|
||||
@@ -249,7 +254,7 @@ export const TypebotProvider = ({
|
||||
updates: UpdateTypebotPayload
|
||||
save?: boolean
|
||||
}) => {
|
||||
if (!localTypebot) return
|
||||
if (!localTypebot || isReadOnly) return
|
||||
const newTypebot = { ...localTypebot, ...updates }
|
||||
setLocalTypebot(newTypebot)
|
||||
if (save) await saveTypebot(newTypebot)
|
||||
@@ -269,8 +274,9 @@ export const TypebotProvider = ({
|
||||
typebot: localTypebot,
|
||||
publishedTypebot,
|
||||
publishedTypebotVersion: publishedTypebotData?.version,
|
||||
isReadOnly: typebotData?.isReadOnly,
|
||||
currentUserMode: typebotData?.currentUserMode ?? 'guest',
|
||||
isSavingLoading: isSaving,
|
||||
is404,
|
||||
save: saveTypebot,
|
||||
undo,
|
||||
redo,
|
||||
|
||||
Reference in New Issue
Block a user