2
0

🐛 Fix potential infinite redirect when session is remembered

Closes #1404
This commit is contained in:
Baptiste Arnaud
2024-04-02 13:31:43 +02:00
parent 89dec4a6dc
commit 50d515cf37
5 changed files with 31 additions and 11 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@typebot.io/js", "name": "@typebot.io/js",
"version": "0.2.58", "version": "0.2.59",
"description": "Javascript library to display typebots on your website", "description": "Javascript library to display typebots on your website",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",

View File

@ -70,7 +70,7 @@ type Props = {
export const ConversationContainer = (props: Props) => { export const ConversationContainer = (props: Props) => {
let chatContainer: HTMLDivElement | undefined let chatContainer: HTMLDivElement | undefined
const [chatChunks, setChatChunks] = persist( const [chatChunks, setChatChunks, isRecovered] = persist(
createSignal<ChatChunkType[]>([ createSignal<ChatChunkType[]>([
{ {
input: props.initialChatReply.input, input: props.initialChatReply.input,
@ -81,6 +81,11 @@ export const ConversationContainer = (props: Props) => {
{ {
key: `typebot-${props.context.typebot.id}-chatChunks`, key: `typebot-${props.context.typebot.id}-chatChunks`,
storage: props.context.storage, storage: props.context.storage,
onRecovered: () => {
setTimeout(() => {
chatContainer?.scrollTo(0, chatContainer.scrollHeight)
}, 200)
},
} }
) )
const [dynamicTheme, setDynamicTheme] = createSignal< const [dynamicTheme, setDynamicTheme] = createSignal<
@ -297,7 +302,10 @@ export const ConversationContainer = (props: Props) => {
(chatChunk.messages.length > 0 && isSending())) (chatChunk.messages.length > 0 && isSending()))
} }
hasError={hasError() && index() === chatChunks().length - 1} hasError={hasError() && index() === chatChunks().length - 1}
isTransitionDisabled={index() !== chatChunks().length - 1} isTransitionDisabled={
index() !== chatChunks().length - 1 ||
(!chatChunk.input && isRecovered())
}
onNewBubbleDisplayed={handleNewBubbleDisplayed} onNewBubbleDisplayed={handleNewBubbleDisplayed}
onAllBubblesDisplayed={handleAllBubblesDisplayed} onAllBubblesDisplayed={handleAllBubblesDisplayed}
onSubmit={sendMessage} onSubmit={sendMessage}

View File

@ -1,19 +1,24 @@
// Copied from https://github.com/solidjs-community/solid-primitives/blob/main/packages/storage/src/types.ts // Copied from https://github.com/solidjs-community/solid-primitives/blob/main/packages/storage/src/types.ts
// Simplifying and adding a `isEnabled` prop // Simplified version
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants' import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
import type { Setter, Signal } from 'solid-js' import type { Setter, Signal } from 'solid-js'
import { untrack } from 'solid-js' import { createSignal, untrack } from 'solid-js'
import { reconcile } from 'solid-js/store' import { reconcile } from 'solid-js/store'
type Params = { type Params = {
key: string key: string
storage: 'local' | 'session' | undefined storage: 'local' | 'session' | undefined
onRecovered?: () => void
} }
export function persist<T>(signal: Signal<T>, params: Params): Signal<T> { export function persist<T>(
if (!params.storage) return signal signal: Signal<T>,
params: Params
): [...Signal<T>, () => boolean] {
const [isRecovered, setIsRecovered] = createSignal(false)
if (!params.storage) return [...signal, () => false]
const storage = parseRememberUserStorage( const storage = parseRememberUserStorage(
params.storage || defaultSettings.general.rememberUser.storage params.storage || defaultSettings.general.rememberUser.storage
@ -26,12 +31,17 @@ export function persist<T>(signal: Signal<T>, params: Params): Signal<T> {
? (data: string) => (signal[1] as any)(() => deserialize(data)) ? (data: string) => (signal[1] as any)(() => deserialize(data))
: (data: string) => (signal[1] as any)(reconcile(deserialize(data))) : (data: string) => (signal[1] as any)(reconcile(deserialize(data)))
if (init) set(init) if (init) {
set(init)
setIsRecovered(true)
params.onRecovered?.()
}
return [ return [
signal[0], signal[0],
typeof signal[0] === 'function' typeof signal[0] === 'function'
? (value?: T | ((prev: T) => T)) => { ? (value?: T | ((prev: T) => T)) => {
setIsRecovered(false)
const output = (signal[1] as Setter<T>)(value as any) const output = (signal[1] as Setter<T>)(value as any)
if (value) storage.setItem(params.key, serialize(output)) if (value) storage.setItem(params.key, serialize(output))
@ -39,11 +49,13 @@ export function persist<T>(signal: Signal<T>, params: Params): Signal<T> {
return output return output
} }
: (...args: any[]) => { : (...args: any[]) => {
setIsRecovered(false)
;(signal[1] as any)(...args) ;(signal[1] as any)(...args)
const value = serialize(untrack(() => signal[0] as any)) const value = serialize(untrack(() => signal[0] as any))
storage.setItem(params.key, value) storage.setItem(params.key, value)
}, },
] as typeof signal isRecovered,
] as [...typeof signal, () => boolean]
} }
const parseRememberUserStorage = ( const parseRememberUserStorage = (

View File

@ -1,6 +1,6 @@
{ {
"name": "@typebot.io/nextjs", "name": "@typebot.io/nextjs",
"version": "0.2.58", "version": "0.2.59",
"description": "Convenient library to display typebots on your Next.js website", "description": "Convenient library to display typebots on your Next.js website",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{ {
"name": "@typebot.io/react", "name": "@typebot.io/react",
"version": "0.2.58", "version": "0.2.59",
"description": "Convenient library to display typebots on your React app", "description": "Convenient library to display typebots on your React app",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",