Skip to content

Commit

Permalink
🐛 (js) Improve session remember behavior
Browse files Browse the repository at this point in the history
Make sure it correctly retrieves saved variables and doesn't clash with other embedded typebots
  • Loading branch information
baptisteArno committed Mar 2, 2023
1 parent c172a44 commit ba253cf
Show file tree
Hide file tree
Showing 16 changed files with 122 additions and 42 deletions.
9 changes: 7 additions & 2 deletions apps/builder/public/templates/customer-support.json
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,6 @@
"hostAvatar": {
"isEnabled": true
},
"guestAvatar": { "isEnabled": false },
"hostBubbles": { "color": "#303235", "backgroundColor": "#F7F8FF" },
"guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" }
},
Expand All @@ -451,7 +450,13 @@
}
},
"settings": {
"general": { "isBrandingEnabled": true },
"general": {
"isBrandingEnabled": true,
"isInputPrefillEnabled": true,
"isResultSavingEnabled": true,
"isHideQueryParamsEnabled": true,
"isNewResultOnRefreshEnabled": true
},
"metadata": {
"description": "Build beautiful conversational forms and embed them directly in your applications without a line of code. Triple your response rate and collect answers that has more value compared to a traditional form."
},
Expand Down
2 changes: 1 addition & 1 deletion apps/builder/public/templates/digital-product-payment.json
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,6 @@
"hostAvatar": {
"isEnabled": true
},
"guestAvatar": { "isEnabled": false },
"hostBubbles": { "color": "#303235", "backgroundColor": "#F7F8FF" },
"guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" }
},
Expand All @@ -480,6 +479,7 @@
"general": {
"isBrandingEnabled": true,
"isInputPrefillEnabled": true,
"isResultSavingEnabled": true,
"isHideQueryParamsEnabled": true,
"isNewResultOnRefreshEnabled": true
},
Expand Down
1 change: 1 addition & 0 deletions apps/builder/public/templates/faq.json
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@
"general": {
"isBrandingEnabled": true,
"isInputPrefillEnabled": true,
"isResultSavingEnabled": true,
"isHideQueryParamsEnabled": true,
"isNewResultOnRefreshEnabled": true
},
Expand Down
14 changes: 10 additions & 4 deletions apps/builder/public/templates/lead-gen.json
Original file line number Diff line number Diff line change
Expand Up @@ -336,19 +336,25 @@
"placeholderColor": "#9095A0"
},
"buttons": { "color": "#FFFFFF", "backgroundColor": "#0042DA" },
"hostBubbles": { "color": "#303235", "backgroundColor": "#F7F8FF" },
"guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" },
"hostAvatar": {
"isEnabled": true
}
},
"hostBubbles": { "color": "#303235", "backgroundColor": "#F7F8FF" },
"guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" }
},
"general": {
"font": "Open Sans",
"background": { "type": "Color", "content": "#ffffff" }
}
},
"settings": {
"general": { "isBrandingEnabled": true },
"general": {
"isBrandingEnabled": true,
"isInputPrefillEnabled": true,
"isResultSavingEnabled": true,
"isHideQueryParamsEnabled": true,
"isNewResultOnRefreshEnabled": true
},
"metadata": {
"description": "Build beautiful conversational forms and embed them directly in your applications without a line of code. Triple your response rate and collect answers that has more value compared to a traditional form."
},
Expand Down
2 changes: 2 additions & 0 deletions apps/builder/public/templates/lead-scoring.json
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,8 @@
"general": {
"isBrandingEnabled": true,
"isInputPrefillEnabled": true,
"isResultSavingEnabled": true,
"isHideQueryParamsEnabled": true,
"isNewResultOnRefreshEnabled": true
},
"metadata": {
Expand Down
2 changes: 2 additions & 0 deletions apps/builder/public/templates/onboarding.json
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@
"general": {
"isBrandingEnabled": true,
"isInputPrefillEnabled": true,
"isResultSavingEnabled": true,
"isHideQueryParamsEnabled": true,
"isNewResultOnRefreshEnabled": true
},
"metadata": {
Expand Down
2 changes: 2 additions & 0 deletions apps/builder/public/templates/quiz.json
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,8 @@
"general": {
"isBrandingEnabled": true,
"isInputPrefillEnabled": true,
"isResultSavingEnabled": true,
"isHideQueryParamsEnabled": true,
"isNewResultOnRefreshEnabled": true
},
"metadata": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ test.describe('Builder', () => {
await page.click('text=Save in variables')
await page.click('text=Add an entry >> nth=-1')
await page.click('input[placeholder="Select the data"]')
await page.click('text=data.map(item => item.name)')
await page.click('text=data.flatMap(item => item.name)')
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,12 @@ export const TypebotProvider = ({
)

useEffect(() => {
Router.events.on('routeChangeStart', () => saveTypebot())
const handleSaveTypebot = () => {
saveTypebot()
}
Router.events.on('routeChangeStart', handleSaveTypebot)
return () => {
Router.events.off('routeChangeStart', () => saveTypebot())
Router.events.off('routeChangeStart', handleSaveTypebot)
}
}, [saveTypebot])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export const GeneralSettingsForm = ({
: true
}
onCheckChange={handleNewResultOnRefreshChange}
moreInfoContent="If the user refreshes the page, its existing results will be overwritten. Disable this if you want to create a new results every time the user refreshes the page."
moreInfoContent="If the user refreshes the page or opens the typebot again during the same session, his previous variables will be prefilled and his new answers will override the previous ones."
/>
<SwitchWithLabel
label="Hide query params on bot start"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { checkChatsUsage } from '@/features/usage'
import {
parsePrefilledVariables,
prefillVariables,
deepParseVariable,
parseVariables,
injectVariablesFromExistingResult,
} from '@/features/variables'
import prisma from '@/lib/prisma'
import { publicProcedure } from '@/utils/server/trpc'
Expand All @@ -20,14 +21,15 @@ import {
Theme,
Typebot,
Variable,
VariableWithValue,
} from 'models'
import {
continueBotFlow,
getSession,
setResultAsCompleted,
startBotFlow,
} from '../utils'
import { env, omit } from 'utils'
import { env, isDefined, omit } from 'utils'

export const sendMessageProcedure = publicProcedure
.meta({
Expand Down Expand Up @@ -109,19 +111,24 @@ const startSession = async (startParams?: StartParams, userId?: string) => {

const typebot = await getTypebot(startParams, userId)

const startVariables = startParams.prefilledVariables
? parsePrefilledVariables(typebot.variables, startParams.prefilledVariables)
const prefilledVariables = startParams.prefilledVariables
? prefillVariables(typebot.variables, startParams.prefilledVariables)
: typebot.variables

const result = await getResult({
...startParams,
isPreview,
typebot: typebot.id,
startVariables,
prefilledVariables,
isNewResultOnRefreshEnabled:
typebot.settings.general.isNewResultOnRefreshEnabled ?? false,
})

const startVariables =
result && result.variables.length > 0
? injectVariablesFromExistingResult(prefilledVariables, result.variables)
: prefilledVariables

const initialState: SessionState = {
typebot: {
id: typebot.id,
Expand Down Expand Up @@ -293,35 +300,64 @@ const getResult = async ({
typebot,
isPreview,
resultId,
startVariables,
prefilledVariables,
isNewResultOnRefreshEnabled,
}: Pick<StartParams, 'isPreview' | 'resultId' | 'typebot'> & {
startVariables: Variable[]
prefilledVariables: Variable[]
isNewResultOnRefreshEnabled: boolean
}) => {
if (isPreview || typeof typebot !== 'string') return undefined
const data = {
isCompleted: false,
typebotId: typebot,
variables: startVariables.filter((variable) => variable.value),
} satisfies Prisma.ResultUncheckedCreateInput
if (isPreview || typeof typebot !== 'string') return
const select = {
id: true,
variables: true,
hasStarted: true,
} satisfies Prisma.ResultSelect
return (

const existingResult =
resultId && !isNewResultOnRefreshEnabled
? await prisma.result.update({
? ((await prisma.result.findFirst({
where: { id: resultId },
data,
select,
})
: await prisma.result.create({
data,
select,
})
) as Pick<Result, 'id' | 'variables' | 'hasStarted'>
})) as Pick<Result, 'id' | 'variables' | 'hasStarted'>)
: undefined

if (existingResult) {
const prefilledVariableWithValue = prefilledVariables.filter(
(prefilledVariable) => isDefined(prefilledVariable.value)
)
const updatedResult = {
variables: prefilledVariableWithValue.concat(
existingResult.variables.filter(
(resultVariable) =>
isDefined(resultVariable.value) &&
!prefilledVariableWithValue.some(
(prefilledVariable) =>
prefilledVariable.name === resultVariable.name
)
)
) as VariableWithValue[],
}
await prisma.result.updateMany({
where: { id: existingResult.id },
data: updatedResult,
})
return {
id: existingResult.id,
variables: updatedResult.variables,
hasStarted: existingResult.hasStarted,
}
} else {
return (await prisma.result.create({
data: {
isCompleted: false,
typebotId: typebot,
variables: prefilledVariables.filter((variable) =>
isDefined(variable.value)
),
},
select,
})) as Pick<Result, 'id' | 'variables' | 'hasStarted'>
}
}

const parseDynamicThemeInState = (theme: Theme) => {
Expand Down
19 changes: 18 additions & 1 deletion apps/viewer/src/features/variables/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import prisma from '@/lib/prisma'
import {
Result,
SessionState,
StartParams,
Typebot,
Expand Down Expand Up @@ -119,7 +120,7 @@ export const deepParseVariable =
return { ...newObj, [key]: currentValue }
}, {} as T)

export const parsePrefilledVariables = (
export const prefillVariables = (
variables: Typebot['variables'],
prefilledVariables: NonNullable<StartParams['prefilledVariables']>
): Variable[] =>
Expand All @@ -132,6 +133,22 @@ export const parsePrefilledVariables = (
}
})

export const injectVariablesFromExistingResult = (
variables: Typebot['variables'],
resultVariables: Result['variables']
): Variable[] =>
variables.map((variable) => {
const resultVariable = resultVariables.find(
(resultVariable) =>
resultVariable.name === variable.name && !variable.value
)
if (!resultVariable) return variable
return {
...variable,
value: resultVariable.value,
}
})

export const updateVariables =
(state: SessionState) =>
async (newVariables: VariableWithUnknowValue[]): Promise<SessionState> => ({
Expand Down
2 changes: 1 addition & 1 deletion packages/js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@typebot.io/js",
"version": "0.0.17",
"version": "0.0.18",
"description": "Javascript library to display typebots on your website",
"type": "module",
"main": "dist/index.js",
Expand Down
7 changes: 5 additions & 2 deletions packages/js/src/components/Bot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@ export const Bot = (props: BotProps & { class?: string }) => {
urlParams.forEach((value, key) => {
prefilledVariables[key] = value
})
const typebotIdFromProps =
typeof props.typebot === 'string' ? props.typebot : undefined
const { data, error } = await getInitialChatReplyQuery({
typebot: props.typebot,
apiHost: props.apiHost,
isPreview: props.isPreview ?? false,
resultId: isNotEmpty(props.resultId)
? props.resultId
: getExistingResultIdFromSession(),
: getExistingResultIdFromSession(typebotIdFromProps),
startGroupId: props.startGroupId,
prefilledVariables: {
...prefilledVariables,
Expand All @@ -66,7 +68,8 @@ export const Bot = (props: BotProps & { class?: string }) => {

if (!data) return setError(new Error("Error! Couldn't initiate the chat."))

if (data.resultId) setResultInSession(data.resultId)
if (data.resultId && typebotIdFromProps)
setResultInSession(typebotIdFromProps, data.resultId)
setInitialChatReply(data)
setCustomCss(data.typebot.theme.customCss ?? '')

Expand Down
11 changes: 7 additions & 4 deletions packages/js/src/utils/sessionStorage.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
const sessionStorageKey = 'resultId'

export const getExistingResultIdFromSession = () => {
export const getExistingResultIdFromSession = (typebotId?: string) => {
if (!typebotId) return
try {
return sessionStorage.getItem(sessionStorageKey) ?? undefined
return (
sessionStorage.getItem(`${sessionStorageKey}-${typebotId}`) ?? undefined
)
} catch {
/* empty */
}
}

export const setResultInSession = (resultId: string) => {
export const setResultInSession = (typebotId: string, resultId: string) => {
try {
return sessionStorage.setItem(sessionStorageKey, resultId)
return sessionStorage.setItem(`${sessionStorageKey}-${typebotId}`, resultId)
} catch {
/* empty */
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@typebot.io/react",
"version": "0.0.17",
"version": "0.0.18",
"description": "React library to display typebots on your website",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down

4 comments on commit ba253cf

@vercel
Copy link

@vercel vercel bot commented on ba253cf Mar 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

builder-v2-typebot-io.vercel.app
builder-v2-git-main-typebot-io.vercel.app
app.typebot.io

@vercel
Copy link

@vercel vercel bot commented on ba253cf Mar 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on ba253cf Mar 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2 – ./apps/viewer

bot.incusservices.com
bot.meuesocial.com.br
bot.mycompany.reviews
bot.outstandbrand.com
bot.ramonmatos.com.br
bot.robertohairlab.it
bot.sharemyreview.net
bot.truongnguyen.live
botz.cloudsiteapp.com
mainmenu.diddancing.com
manualhandlingcourse.ie
register.kandabrand.com
signup.hypemarketing.in
subfooter.wpwakanda.com
survey.hypemarketing.in
testbot.afterorigin.com
typebot.influencer.love
91181264.your-access.one
ai.chromebookstoreph.com
contextone.wpwakanda.com
form.sergiolimajr.com.br
hunterbot.saleshunter.ai
invite.bridesquadapp.com
link.cascadigital.com.br
onboarding.growthside.io
reward.onlinebotdemo.xyz
stap.venturemarketing.in
type.opaulovieira.com.br
aibot.angrybranding.co.uk
bot.aidigitalmarketing.kr
bot.amicidisanfaustino.it
bot.arraesecenteno.com.br
bot.blackboxsports.com.br
bot.cabinrentalagency.com
bot.fusionstarreviews.com
boyfriend-breakup.riku.ai
brigadeirosemdrama.com.br
chat.ertcrebateportal.com
chat.thehomebuyersusa.com
chat.thisiscrushhouse.com
forms.hiabhaykulkarni.com
healthandsafetycourses.uk
sellmyharleylouisiana.com
testbot.sharemyreview.net
typebot-viewer.vercel.app
verfica.botmachine.com.br
bot.adventureconsulting.hu
bot2.fusionstarreviews.com
casestudyemb.wpwakanda.com
chat.atlasoutfittersk9.com
configurator.bouclidom.com
help.atlasoutfittersk9.com
herbalife.barrettamario.it
homepageonly.wpwakanda.com
liveconvert.kandalearn.com
mainmenu1one.wpwakanda.com
tarian.theiofoundation.org
ted.meujalecobrasil.com.br
type.dericsoncalari.com.br
bot.pinpointinteractive.com
bot.polychromes-project.com
bot.seidinembroseanchetu.it
chatbot.berbelanjabiz.trade
designguide.techyscouts.com
jcapp.virtuesocialmedia.com
liveconvert2.kandalearn.com
presente.empresarias.com.mx
sell.sellthemotorhome.co.uk
anamnese.odontopavani.com.br
austin.channelautomation.com
bot.marketingplusmindset.com
bot.seidibergamoseanchetu.it
desabafe.sergiolimajr.com.br
download.venturemarketing.in
jc-app.virtuesocialmedia.com
piazzatorre.barrettamario.it
type.cookieacademyonline.com
bot.brigadeirosemdrama.com.br
forms.escoladeautomacao.com.br
onboarding.libertydreamcare.ie
type.talitasouzamarques.com.br
agendamento.sergiolimajr.com.br
anamnese.clinicamegasjdr.com.br
bookings.littlepartymonkeys.com
bot.comercializadoraomicron.com
elevateyourmind.groovepages.com
viewer-v2-typebot-io.vercel.app
yourfeedback.comebackreward.com
gerador.verificadordehospedes.com
personal-trainer.barrettamario.it
preagendamento.sergiolimajr.com.br
studiotecnicoimmobiliaremerelli.it
download.thailandmicespecialist.com
register.thailandmicespecialist.com
bot.studiotecnicoimmobiliaremerelli.it
pesquisa.escolamodacomproposito.com.br
anamnese.clinicaramosodontologia.com.br
chrome-os-inquiry-system.itschromeos.com
viewer-v2-git-main-typebot-io.vercel.app
main-menu-for-itschromeos.itschromeos.com

@vercel
Copy link

@vercel vercel bot commented on ba253cf Mar 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs – ./apps/docs

Please sign in to comment.