feat(bot): ⚡️ Improve first paint delay
This commit is contained in:
@ -3,7 +3,7 @@ import { Answer, PublicTypebot, VariableWithValue } from 'models'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { upsertAnswer } from 'services/answer'
|
||||
import { isDefined } from 'utils'
|
||||
import { isDefined, isNotDefined } from 'utils'
|
||||
import { SEO } from '../components/Seo'
|
||||
import { createResult, updateResult } from '../services/result'
|
||||
import { ErrorPage } from './ErrorPage'
|
||||
@ -34,6 +34,7 @@ export const TypebotPage = ({
|
||||
const [resultId, setResultId] = useState<string | undefined>()
|
||||
|
||||
useEffect(() => {
|
||||
setShowTypebot(true)
|
||||
const urlParams = new URLSearchParams(location.search)
|
||||
clearQueryParams()
|
||||
const predefinedVariables: { [key: string]: string } = {}
|
||||
@ -68,7 +69,6 @@ export const TypebotPage = ({
|
||||
setResultInSession(result.id)
|
||||
}
|
||||
}
|
||||
setShowTypebot(true)
|
||||
}
|
||||
|
||||
const handleNewVariables = async (variables: VariableWithValue[]) => {
|
||||
@ -108,6 +108,7 @@ export const TypebotPage = ({
|
||||
onNewAnswer={handleNewAnswer}
|
||||
onCompleted={handleCompleted}
|
||||
onVariablesUpdated={handleNewVariables}
|
||||
isLoading={isNotDefined(resultId)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -10,7 +10,7 @@
|
||||
"db": "*",
|
||||
"models": "*",
|
||||
"qs": "^6.10.3",
|
||||
"react-frame-component": "5.2.2-alpha.1",
|
||||
"react-frame-component": "5.2.3",
|
||||
"react-phone-number-input": "^3.1.52",
|
||||
"react-scroll": "^1.8.7",
|
||||
"react-transition-group": "^4.4.2",
|
||||
|
@ -33,6 +33,7 @@ export const AvatarSideContainer = forwardRef(
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
if (!document) return
|
||||
setShow(true)
|
||||
const resizeObserver = new ResizeObserver(refreshTopOffset)
|
||||
resizeObserver.observe(document.body)
|
||||
|
@ -12,7 +12,7 @@ type Props = {
|
||||
export const showAnimationDuration = 400
|
||||
|
||||
export const EmbedBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
const { typebot, isLoading } = useTypebot()
|
||||
const messageContainer = useRef<HTMLDivElement | null>(null)
|
||||
const [isTyping, setIsTyping] = useState(true)
|
||||
|
||||
@ -22,9 +22,10 @@ export const EmbedBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTyping || isLoading) return
|
||||
showContentAfterMediaLoad()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
}, [isLoading])
|
||||
|
||||
const showContentAfterMediaLoad = () => {
|
||||
setTimeout(() => {
|
||||
|
@ -14,7 +14,7 @@ export const showAnimationDuration = 400
|
||||
export const mediaLoadingFallbackTimeout = 5000
|
||||
|
||||
export const ImageBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
const { typebot, isLoading } = useTypebot()
|
||||
const messageContainer = useRef<HTMLDivElement | null>(null)
|
||||
const image = useRef<HTMLImageElement | null>(null)
|
||||
const [isTyping, setIsTyping] = useState(true)
|
||||
@ -25,9 +25,10 @@ export const ImageBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTyping || isLoading) return
|
||||
showContentAfterMediaLoad()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
}, [isLoading])
|
||||
|
||||
const showContentAfterMediaLoad = () => {
|
||||
if (!image.current) return
|
||||
|
@ -19,7 +19,7 @@ const defaultTypingEmulation = {
|
||||
}
|
||||
|
||||
export const TextBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
const { typebot, isLoading } = useTypebot()
|
||||
const messageContainer = useRef<HTMLDivElement | null>(null)
|
||||
const [isTyping, setIsTyping] = useState(true)
|
||||
|
||||
@ -28,6 +28,7 @@ export const TextBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTyping || isLoading) return
|
||||
const typingTimeout = computeTypingTimeout(
|
||||
block.content.plainText,
|
||||
typebot.settings?.typingEmulation ?? defaultTypingEmulation
|
||||
@ -36,7 +37,7 @@ export const TextBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
onTypingEnd()
|
||||
}, typingTimeout)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
}, [isLoading])
|
||||
|
||||
const onTypingEnd = () => {
|
||||
setIsTyping(false)
|
||||
|
@ -19,14 +19,15 @@ export const showAnimationDuration = 400
|
||||
export const mediaLoadingFallbackTimeout = 5000
|
||||
|
||||
export const VideoBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
const { typebot, isLoading } = useTypebot()
|
||||
const messageContainer = useRef<HTMLDivElement | null>(null)
|
||||
const [isTyping, setIsTyping] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTyping || isLoading) return
|
||||
showContentAfterMediaLoad()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
}, [isLoading])
|
||||
|
||||
const showContentAfterMediaLoad = () => {
|
||||
setTimeout(() => {
|
||||
|
@ -42,7 +42,7 @@ export const StripePaymentForm = ({ options, onSuccess }: Props) => {
|
||||
description: error.name + ' ' + error.message,
|
||||
details: error.message,
|
||||
})
|
||||
if (!data) return
|
||||
if (!data || !frameDocument || !frameWindow?.Stripe) return
|
||||
await initStripe(frameDocument)
|
||||
setStripe(frameWindow.Stripe(data.publicKey))
|
||||
setClientSecret(data.clientSecret)
|
||||
|
@ -5,7 +5,7 @@ import { useFrame } from 'react-frame-component'
|
||||
import { setCssVariablesValue } from '../services/theme'
|
||||
import { useAnswers } from '../contexts/AnswersContext'
|
||||
import { Group, Edge, PublicTypebot, Theme, VariableWithValue } from 'models'
|
||||
import { byId, isNotDefined } from 'utils'
|
||||
import { byId, isDefined, isNotDefined } from 'utils'
|
||||
import { animateScroll as scroll } from 'react-scroll'
|
||||
import { LinkedTypebot, useTypebot } from 'contexts/TypebotContext'
|
||||
import { ChatContext } from 'contexts/ChatContext'
|
||||
@ -83,8 +83,13 @@ export const ConversationContainer = ({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const prefilledVariables = injectPredefinedVariables(predefinedVariables)
|
||||
updateVariables(prefilledVariables)
|
||||
if (
|
||||
isDefined(predefinedVariables) &&
|
||||
Object.keys(predefinedVariables).length > 0
|
||||
) {
|
||||
const prefilledVariables = injectPredefinedVariables(predefinedVariables)
|
||||
updateVariables(prefilledVariables)
|
||||
}
|
||||
displayNextGroup({
|
||||
edgeId: startGroupId
|
||||
? undefined
|
||||
@ -92,13 +97,13 @@ export const ConversationContainer = ({
|
||||
groupId: startGroupId,
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
}, [predefinedVariables])
|
||||
|
||||
const injectPredefinedVariables = (predefinedVariables?: {
|
||||
const injectPredefinedVariables = (predefinedVariables: {
|
||||
[key: string]: string | undefined
|
||||
}) => {
|
||||
const prefilledVariables: VariableWithValue[] = []
|
||||
Object.keys(predefinedVariables ?? {}).forEach((key) => {
|
||||
Object.keys(predefinedVariables).forEach((key) => {
|
||||
const matchingVariable = typebot.variables.find(
|
||||
(v) => v.name.toLowerCase() === key.toLowerCase()
|
||||
)
|
||||
@ -112,6 +117,7 @@ export const ConversationContainer = ({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!frameDocument) return
|
||||
setCssVariablesValue(theme, frameDocument.body.style)
|
||||
}, [theme, frameDocument])
|
||||
|
||||
|
@ -6,6 +6,7 @@ export const LiteBadge = () => {
|
||||
const liteBadge = useRef<HTMLAnchorElement | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!document) return
|
||||
const container = document.querySelector(
|
||||
'[data-testid="container"]'
|
||||
) as HTMLDivElement
|
||||
|
@ -31,6 +31,7 @@ export type TypebotViewerProps = {
|
||||
predefinedVariables?: { [key: string]: string | undefined }
|
||||
resultId?: string
|
||||
startGroupId?: string
|
||||
isLoading?: boolean
|
||||
onNewGroupVisible?: (edge: Edge) => void
|
||||
onNewAnswer?: (answer: Answer) => Promise<void>
|
||||
onNewLog?: (log: Omit<Log, 'id' | 'createdAt' | 'resultId'>) => void
|
||||
@ -42,6 +43,7 @@ export const TypebotViewer = ({
|
||||
typebot,
|
||||
apiHost = process.env.NEXT_PUBLIC_VIEWER_URL?.split(',')[0],
|
||||
isPreview = false,
|
||||
isLoading = false,
|
||||
style,
|
||||
resultId,
|
||||
startGroupId,
|
||||
@ -98,6 +100,7 @@ export const TypebotViewer = ({
|
||||
apiHost={apiHost}
|
||||
isPreview={isPreview}
|
||||
onNewLog={handleNewLog}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
<AnswersContext
|
||||
resultId={resultId}
|
||||
|
@ -25,6 +25,7 @@ const typebotContext = createContext<{
|
||||
apiHost: string
|
||||
isPreview: boolean
|
||||
linkedBotQueue: LinkedTypebotQueue
|
||||
isLoading: boolean
|
||||
setCurrentTypebotId: (id: string) => void
|
||||
updateVariableValue: (variableId: string, value: string) => void
|
||||
createEdge: (edge: Edge) => void
|
||||
@ -44,11 +45,13 @@ export const TypebotContext = ({
|
||||
typebot,
|
||||
apiHost,
|
||||
isPreview,
|
||||
isLoading,
|
||||
onNewLog,
|
||||
}: {
|
||||
children: ReactNode
|
||||
typebot: PublicTypebot
|
||||
apiHost: string
|
||||
isLoading: boolean
|
||||
isPreview: boolean
|
||||
onNewLog: (log: Omit<Log, 'id' | 'createdAt' | 'resultId'>) => void
|
||||
}) => {
|
||||
@ -123,6 +126,7 @@ export const TypebotContext = ({
|
||||
injectLinkedTypebot,
|
||||
onNewLog,
|
||||
linkedBotQueue,
|
||||
isLoading,
|
||||
pushEdgeIdInLinkedTypebotQueue,
|
||||
popEdgeIdFromLinkedTypebotQueue,
|
||||
currentTypebotId,
|
||||
|
@ -12440,10 +12440,10 @@ react-focus-lock@2.5.2:
|
||||
use-callback-ref "^1.2.5"
|
||||
use-sidecar "^1.0.5"
|
||||
|
||||
react-frame-component@5.2.2-alpha.1:
|
||||
version "5.2.2-alpha.1"
|
||||
resolved "https://registry.yarnpkg.com/react-frame-component/-/react-frame-component-5.2.2-alpha.1.tgz#fcfec38e0670ea78b3e9b6d00ef7a18613b3b089"
|
||||
integrity sha512-pCXLlBDO8+yh4qbax8M2U+Cl+5tFxyZ0p6HzD0MP+P47u3ZK4fPiOzWQIOi7Eand4tq6nawKZC+vsSHC2KT1Zg==
|
||||
react-frame-component@5.2.3:
|
||||
version "5.2.3"
|
||||
resolved "https://registry.yarnpkg.com/react-frame-component/-/react-frame-component-5.2.3.tgz#2d5d1e29b23d5b915c839b44980d03bb9cafc453"
|
||||
integrity sha512-r+h0o3r/uqOLNT724z4CRVkxQouKJvoi3OPfjqWACD30Y87rtEmeJrNZf1WYPGknn1Y8200HAjx7hY/dPUGgmA==
|
||||
|
||||
react-helmet-async@*, react-helmet-async@^1.2.3, react-helmet-async@^1.3.0:
|
||||
version "1.3.0"
|
||||
|
Reference in New Issue
Block a user