2
0

feat: 🔥 Remove save button

This commit is contained in:
Baptiste Arnaud
2022-02-21 19:02:44 +01:00
parent fd822a35a7
commit 1b900b3f5d
4 changed files with 53 additions and 62 deletions

View File

@ -37,7 +37,7 @@ export const GoogleSheetsSettingsBody = ({
onOptionsChange, onOptionsChange,
stepId, stepId,
}: Props) => { }: Props) => {
const { save, hasUnsavedChanges } = useTypebot() const { save } = useTypebot()
const { sheets, isLoading } = useSheets({ const { sheets, isLoading } = useSheets({
credentialsId: options?.credentialsId, credentialsId: options?.credentialsId,
spreadsheetId: options?.spreadsheetId, spreadsheetId: options?.spreadsheetId,
@ -83,10 +83,7 @@ export const GoogleSheetsSettingsBody = ({
} }
const handleCreateNewClick = async () => { const handleCreateNewClick = async () => {
if (hasUnsavedChanges) { await save()
const errorToastId = await save()
if (errorToastId) return
}
const linkElement = document.createElement('a') const linkElement = document.createElement('a')
linkElement.href = getGoogleSheetsConsentScreenUrl( linkElement.href = getGoogleSheetsConsentScreenUrl(
window.location.href, window.location.href,

View File

@ -1,33 +0,0 @@
import { IconButton, Text, Tooltip } from '@chakra-ui/react'
import { CheckIcon, SaveIcon } from 'assets/icons'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import React from 'react'
export const SaveButton = () => {
const { save, isSavingLoading, hasUnsavedChanges } = useTypebot()
const handleSaveClick = async () => {
await save()
}
return (
<>
{hasUnsavedChanges && (
<Text fontSize="sm" color="gray.500">
Unsaved changes
</Text>
)}
<Tooltip label="Save changes">
<IconButton
isDisabled={!hasUnsavedChanges}
onClick={handleSaveClick}
isLoading={isSavingLoading}
icon={
hasUnsavedChanges ? <SaveIcon /> : <CheckIcon color="green.400" />
}
aria-label={hasUnsavedChanges ? 'Save' : 'Saved'}
/>
</Tooltip>
</>
)
}

View File

@ -1,4 +1,12 @@
import { Flex, HStack, Button, IconButton, Tooltip } from '@chakra-ui/react' import {
Flex,
HStack,
Button,
IconButton,
Tooltip,
Spinner,
Text,
} from '@chakra-ui/react'
import { ChevronLeftIcon, RedoIcon, UndoIcon } from 'assets/icons' import { ChevronLeftIcon, RedoIcon, UndoIcon } from 'assets/icons'
import { NextChakraLink } from 'components/nextChakra/NextChakraLink' import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
import { RightPanel, useEditor } from 'contexts/EditorContext' import { RightPanel, useEditor } from 'contexts/EditorContext'
@ -7,7 +15,6 @@ import { useRouter } from 'next/router'
import React from 'react' import React from 'react'
import { PublishButton } from '../buttons/PublishButton' import { PublishButton } from '../buttons/PublishButton'
import { EditableTypebotName } from './EditableTypebotName' import { EditableTypebotName } from './EditableTypebotName'
import { SaveButton } from './SaveButton'
export const headerHeight = 56 export const headerHeight = 56
@ -21,7 +28,7 @@ export const TypebotHeader = () => {
redo, redo,
canUndo, canUndo,
canRedo, canRedo,
publishedTypebot, isSavingLoading,
} = useTypebot() } = useTypebot()
const { setRightPanel } = useEditor() const { setRightPanel } = useEditor()
@ -96,7 +103,13 @@ export const TypebotHeader = () => {
</Button> </Button>
)} )}
</HStack> </HStack>
<Flex pos="absolute" left="1rem" justify="center" align="center"> <HStack
pos="absolute"
left="1rem"
justify="center"
align="center"
spacing="6"
>
<HStack alignItems="center"> <HStack alignItems="center">
<IconButton <IconButton
aria-label="Back" aria-label="Back"
@ -129,10 +142,17 @@ export const TypebotHeader = () => {
/> />
</Tooltip> </Tooltip>
</HStack> </HStack>
</Flex> {isSavingLoading && (
<HStack>
<Spinner speed="0.7s" size="sm" color="gray.400" />
<Text fontSize="sm" color="gray.400">
Saving...
</Text>
</HStack>
)}
</HStack>
<HStack right="40px" pos="absolute"> <HStack right="40px" pos="absolute">
<SaveButton />
<Button onClick={handlePreviewClick}>Preview</Button> <Button onClick={handlePreviewClick}>Preview</Button>
<PublishButton /> <PublishButton />
</HStack> </HStack>

View File

@ -1,4 +1,4 @@
import { ToastId, useToast } from '@chakra-ui/react' import { useToast } from '@chakra-ui/react'
import { PublicTypebot, Settings, Theme, Typebot } from 'models' import { PublicTypebot, Settings, Theme, Typebot } from 'models'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { import {
@ -32,7 +32,8 @@ import useUndo from 'services/utils/useUndo'
import { useDebounce } from 'use-debounce' import { useDebounce } from 'use-debounce'
import { itemsAction, ItemsActions } from './actions/items' import { itemsAction, ItemsActions } from './actions/items'
import { generate } from 'short-uuid' import { generate } from 'short-uuid'
const autoSaveTimeout = 40000 import { deepEqual } from 'fast-equals'
const autoSaveTimeout = 10000
type UpdateTypebotPayload = Partial<{ type UpdateTypebotPayload = Partial<{
theme: Theme theme: Theme
@ -49,9 +50,8 @@ const typebotContext = createContext<
publishedTypebot?: PublicTypebot publishedTypebot?: PublicTypebot
isPublished: boolean isPublished: boolean
isPublishing: boolean isPublishing: boolean
hasUnsavedChanges: boolean
isSavingLoading: boolean isSavingLoading: boolean
save: () => Promise<ToastId | undefined> save: () => Promise<void>
undo: () => void undo: () => void
redo: () => void redo: () => void
canRedo: boolean canRedo: boolean
@ -93,6 +93,13 @@ export const TypebotContext = ({
}), }),
}) })
useEffect(() => {
if (!typebot || !localTypebot || deepEqual(typebot, localTypebot)) return
if (typebot?.blocks.length === localTypebot?.blocks.length)
setLocalTypebot({ ...typebot })
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [typebot])
const [ const [
{ present: localTypebot }, { present: localTypebot },
{ {
@ -106,12 +113,16 @@ export const TypebotContext = ({
] = useUndo<Typebot | undefined>(undefined) ] = useUndo<Typebot | undefined>(undefined)
const saveTypebot = async () => { const saveTypebot = async () => {
if (deepEqual(typebot, localTypebot)) return
const typebotToSave = currentTypebotRef.current const typebotToSave = currentTypebotRef.current
if (!typebotToSave) return if (!typebotToSave) return
setIsSavingLoading(true) setIsSavingLoading(true)
const { error } = await updateTypebot(typebotToSave.id, typebotToSave) const { error } = await updateTypebot(typebotToSave.id, typebotToSave)
setIsSavingLoading(false) setIsSavingLoading(false)
if (error) return toast({ title: error.name, description: error.message }) if (error) {
toast({ title: error.name, description: error.message })
return
}
mutate({ typebot: typebotToSave }) mutate({ typebot: typebotToSave })
window.removeEventListener('beforeunload', preventUserFromRefreshing) window.removeEventListener('beforeunload', preventUserFromRefreshing)
} }
@ -130,18 +141,9 @@ export const TypebotContext = ({
}) })
} }
const hasUnsavedChanges = useMemo(
() =>
isDefined(typebot) &&
isDefined(localTypebot) &&
!checkIfTypebotsAreEqual(localTypebot, typebot),
[typebot, localTypebot]
)
useAutoSave({ useAutoSave({
handler: saveTypebot, handler: saveTypebot,
item: localTypebot, item: localTypebot,
canSave: hasUnsavedChanges,
debounceTimeout: autoSaveTimeout, debounceTimeout: autoSaveTimeout,
}) })
@ -257,7 +259,6 @@ export const TypebotContext = ({
value={{ value={{
typebot: localTypebot, typebot: localTypebot,
publishedTypebot, publishedTypebot,
hasUnsavedChanges,
isSavingLoading, isSavingLoading,
save: saveTypebot, save: saveTypebot,
undo, undo,
@ -306,18 +307,24 @@ export const useFetchedTypebot = ({
const useAutoSave = <T,>({ const useAutoSave = <T,>({
handler, handler,
item, item,
canSave,
debounceTimeout, debounceTimeout,
}: { }: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
handler: (item?: T) => Promise<any> handler: (item?: T) => Promise<any>
item?: T item?: T
canSave: boolean
debounceTimeout: number debounceTimeout: number
}) => { }) => {
const [debouncedItem] = useDebounce(item, debounceTimeout) const [debouncedItem] = useDebounce(item, debounceTimeout)
useEffect(() => {
const save = () => handler(item)
document.addEventListener('visibilitychange', save)
return () => {
document.removeEventListener('visibilitychange', save)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return useEffect(() => { return useEffect(() => {
if (canSave) handler(item) handler(item)
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedItem]) }, [debouncedItem])
} }