Skip to content

Commit

Permalink
⚡ Remember result in either local or session storage (#514)
Browse files Browse the repository at this point in the history
Closes #513
  • Loading branch information
baptisteArno authored May 16, 2023
1 parent 0ae2a4b commit 27b009d
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 50 deletions.
2 changes: 1 addition & 1 deletion apps/builder/src/components/SwitchWithRelatedSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const SwitchWithRelatedSettings = ({ children, ...props }: Props) => (
<Stack
borderWidth={props.initialValue ? 1 : undefined}
rounded="md"
p={props.initialValue ? '4' : undefined}
p={props.initialValue ? '3' : undefined}
spacing={4}
>
<SwitchWithLabel {...props} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
import { Flex, FormLabel, Stack, Switch, useDisclosure } from '@chakra-ui/react'
import {
Flex,
FormControl,
FormLabel,
HStack,
Stack,
Switch,
Tag,
useDisclosure,
Text,
} from '@chakra-ui/react'
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import { Plan } from '@typebot.io/prisma'
import { GeneralSettings } from '@typebot.io/schemas'
import { GeneralSettings, rememberUserStorages } from '@typebot.io/schemas'
import React from 'react'
import { isDefined } from '@typebot.io/lib'
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
import { ChangePlanModal } from '@/features/billing/components/ChangePlanModal'
import { LockTag } from '@/features/billing/components/LockTag'
import { isFreePlan } from '@/features/billing/helpers/isFreePlan'
import { useI18n } from '@/locales'
import { SwitchWithRelatedSettings } from '@/components/SwitchWithRelatedSettings'
import { DropdownList } from '@/components/DropdownList'
import { MoreInfoTooltip } from '@/components/MoreInfoTooltip'

type Props = {
generalSettings: GeneralSettings
Expand All @@ -31,10 +44,13 @@ export const GeneralSettingsForm = ({
})
}

const handleNewResultOnRefreshChange = (isRememberSessionChecked: boolean) =>
const toggleRememberUser = (isEnabled: boolean) =>
onGeneralSettingsChange({
...generalSettings,
isNewResultOnRefreshEnabled: !isRememberSessionChecked,
rememberUser: {
...generalSettings.rememberUser,
isEnabled,
},
})

const handleInputPrefillChange = (isInputPrefillEnabled: boolean) =>
Expand All @@ -49,6 +65,17 @@ export const GeneralSettingsForm = ({
isHideQueryParamsEnabled,
})

const updateRememberUserStorage = (
storage: NonNullable<GeneralSettings['rememberUser']>['storage']
) =>
onGeneralSettingsChange({
...generalSettings,
rememberUser: {
...generalSettings.rememberUser,
storage,
},
})

return (
<Stack spacing={6}>
<ChangePlanModal
Expand Down Expand Up @@ -77,22 +104,46 @@ export const GeneralSettingsForm = ({
onCheckChange={handleInputPrefillChange}
moreInfoContent="Inputs are automatically pre-filled whenever their associated variable has a value"
/>
<SwitchWithLabel
label="Remember session"
initialValue={
isDefined(generalSettings.isNewResultOnRefreshEnabled)
? !generalSettings.isNewResultOnRefreshEnabled
: true
}
onCheckChange={handleNewResultOnRefreshChange}
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"
initialValue={generalSettings.isHideQueryParamsEnabled ?? true}
onCheckChange={handleHideQueryParamsChange}
moreInfoContent="If your URL contains query params, they will be automatically hidden when the bot starts."
/>
<SwitchWithRelatedSettings
label={'Remember user'}
moreInfoContent="If enabled, user previous variables will be prefilled and his new answers will override the previous ones."
initialValue={
generalSettings.rememberUser?.isEnabled ??
(isDefined(generalSettings.isNewResultOnRefreshEnabled)
? !generalSettings.isNewResultOnRefreshEnabled
: false)
}
onCheckChange={toggleRememberUser}
>
<FormControl as={HStack} justifyContent="space-between">
<FormLabel mb="0">
Storage:&nbsp;
<MoreInfoTooltip>
<Stack>
<Text>
Choose <Tag size="sm">session</Tag> to remember the user as
long as he does not closes the tab or the browser.
</Text>
<Text>
Choose <Tag size="sm">local</Tag> to remember the user
forever.
</Text>
</Stack>
</MoreInfoTooltip>
</FormLabel>
<DropdownList
currentItem={generalSettings.rememberUser?.storage ?? 'session'}
onItemSelect={updateRememberUserStorage}
items={rememberUserStorages}
></DropdownList>
</FormControl>
</SwitchWithRelatedSettings>
</Stack>
)
}
2 changes: 1 addition & 1 deletion apps/docs/docs/editor/settings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ The general settings represent the general behaviors of your typebot.

- **Typebot.io branding**: If enabled, will show a "Made with Typebot" badge displayed at the bottom of your bot.
- **Prefill input**: If enabled, the inputs will be automatically pre-filled whenever their associated variable has a value.
- **Remember session**: If enabled, when a user refreshes the page, its existing answers will be overwritten.
- **Hide query params on bot start**: If enabled, the query params will be hidden when the bot starts.
- **Remember user**: If enabled, user previous variables will be prefilled and his new answers will override the previous ones.

## Typing emulation

Expand Down
13 changes: 8 additions & 5 deletions apps/viewer/src/features/chat/api/sendMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,11 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
isPreview: startParams.isPreview || typeof startParams.typebot !== 'string',
typebotId: typebot.id,
prefilledVariables,
isNewResultOnRefreshEnabled:
typebot.settings.general.isNewResultOnRefreshEnabled ?? false,
isRememberUserEnabled:
typebot.settings.general.rememberUser?.isEnabled ??
(isDefined(typebot.settings.general.isNewResultOnRefreshEnabled)
? !typebot.settings.general.isNewResultOnRefreshEnabled
: false),
})

const startVariables =
Expand Down Expand Up @@ -291,11 +294,11 @@ const getResult = async ({
isPreview,
resultId,
prefilledVariables,
isNewResultOnRefreshEnabled,
isRememberUserEnabled,
}: Pick<StartParams, 'isPreview' | 'resultId'> & {
typebotId: string
prefilledVariables: Variable[]
isNewResultOnRefreshEnabled: boolean
isRememberUserEnabled: boolean
}) => {
if (isPreview) return
const select = {
Expand All @@ -305,7 +308,7 @@ const getResult = async ({
} satisfies Prisma.ResultSelect

const existingResult =
resultId && !isNewResultOnRefreshEnabled
resultId && isRememberUserEnabled
? ((await prisma.result.findFirst({
where: { id: resultId },
select,
Expand Down
5 changes: 4 additions & 1 deletion apps/viewer/src/features/settings/settings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ test('Result should be overwritten on page refresh', async ({ page }) => {
...defaultSettings,
general: {
...defaultSettings.general,
isNewResultOnRefreshEnabled: false,
rememberUser: {
isEnabled: true,
storage: 'session',
},
},
},
...parseDefaultGroupWithBlock({
Expand Down
2 changes: 1 addition & 1 deletion packages/embeds/js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@typebot.io/js",
"version": "0.0.51",
"version": "0.0.52",
"description": "Javascript library to display typebots on your website",
"type": "module",
"main": "dist/index.js",
Expand Down
13 changes: 8 additions & 5 deletions packages/embeds/js/src/components/Bot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { setIsMobile } from '@/utils/isMobileSignal'
import { BotContext, InitialChatReply, OutgoingLog } from '@/types'
import { ErrorMessage } from './ErrorMessage'
import {
getExistingResultIdFromSession,
setResultInSession,
} from '@/utils/sessionStorage'
getExistingResultIdFromStorage,
setResultInStorage,
} from '@/utils/storage'
import { setCssVariablesValue } from '@/utils/setCssVariablesValue'
import immutableCss from '../assets/immutable.css'

Expand Down Expand Up @@ -52,7 +52,7 @@ export const Bot = (props: BotProps & { class?: string }) => {
isPreview: props.isPreview ?? false,
resultId: isNotEmpty(props.resultId)
? props.resultId
: getExistingResultIdFromSession(typebotIdFromProps),
: getExistingResultIdFromStorage(typebotIdFromProps),
startGroupId: props.startGroupId,
prefilledVariables: {
...prefilledVariables,
Expand All @@ -76,7 +76,10 @@ export const Bot = (props: BotProps & { class?: string }) => {
if (!data) return setError(new Error("Error! Couldn't initiate the chat."))

if (data.resultId && typebotIdFromProps)
setResultInSession(typebotIdFromProps, data.resultId)
setResultInStorage(data.typebot.settings.general.rememberUser?.storage)(
typebotIdFromProps,
data.resultId
)
setInitialChatReply(data)
setCustomCss(data.typebot.theme.customCss ?? '')

Expand Down
20 changes: 0 additions & 20 deletions packages/embeds/js/src/utils/sessionStorage.ts

This file was deleted.

29 changes: 29 additions & 0 deletions packages/embeds/js/src/utils/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const sessionStorageKey = 'resultId'

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

export const setResultInStorage =
(storageType: 'local' | 'session' = 'session') =>
(typebotId: string, resultId: string) => {
try {
;(storageType === 'session' ? localStorage : sessionStorage).removeItem(
`${sessionStorageKey}-${typebotId}`
)
return (
storageType === 'session' ? sessionStorage : localStorage
).setItem(`${sessionStorageKey}-${typebotId}`, resultId)
} catch {
/* empty */
}
}
2 changes: 1 addition & 1 deletion packages/embeds/react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@typebot.io/react",
"version": "0.0.51",
"version": "0.0.52",
"description": "React library to display typebots on your website",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
12 changes: 11 additions & 1 deletion packages/schemas/features/typebot/settings.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { z } from 'zod'

export const rememberUserStorages = ['session', 'local'] as const

const generalSettings = z.object({
isBrandingEnabled: z.boolean(),
isTypingEmulationEnabled: z.boolean().optional(),
isInputPrefillEnabled: z.boolean().optional(),
isHideQueryParamsEnabled: z.boolean().optional(),
isNewResultOnRefreshEnabled: z.boolean().optional(),
rememberUser: z
.object({
isEnabled: z.boolean().optional(),
storage: z.enum(rememberUserStorages).optional(),
})
.optional(),
})

const typingEmulation = z.object({
Expand All @@ -32,7 +40,9 @@ export const settingsSchema = z.object({
export const defaultSettings: Settings = {
general: {
isBrandingEnabled: true,
isNewResultOnRefreshEnabled: true,
rememberUser: {
isEnabled: false,
},
isInputPrefillEnabled: true,
isHideQueryParamsEnabled: true,
},
Expand Down

4 comments on commit 27b009d

@vercel
Copy link

@vercel vercel bot commented on 27b009d May 16, 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 27b009d May 16, 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

bii.bj
1stop.au
wasap.nl
yobot.me
klujo.com
me.cr8.ai
wachat.io
wassep.io
247987.com
8jours.top
aginap.com
ai.mprs.in
bee.cr8.ai
bot.aws.bj
bot.bbc.bj
cat.cr8.ai
finplex.be
jxi.cr8.ai
nepkit.com
pig.cr8.ai
sat.cr8.ai
wachats.me
wsapio.com
bot.aipr.kr
bot.joof.it
bot.kloo.me
bull.cr8.ai
docs.cr8.ai
minipost.uk
mole.cr8.ai
team.cr8.ai
wachapp.app
wolf.cr8.ai
ai.meant.com
bot.grace.bj
cinecorn.com
gniorder.com
help.taxt.co
kusamint.com
rhino.cr8.ai
sheep.cr8.ai
snake.cr8.ai
svhm.mprs.in
tiger.cr8.ai
video.cr8.ai
yoda.riku.ai
ov1.wpwakanda.com
ov2.wpwakanda.com
ov3.wpwakanda.com
support.triplo.ai
viewer.typebot.io
welcome.triplo.ai
1988.bouclidom.com
amancarseat.online
andreimayer.com.br
presente.empresarias.com.mx
register.algorithmpress.com
sell.sellthemotorhome.co.uk
anamnese.odontopavani.com.br
austin.channelautomation.com
bot.marketingplusmindset.com
bot.seidibergamoseanchetu.it
desabafe.sergiolimajr.com.br
download.venturemarketing.in
open.campus.aalen.university
piazzatorre.barrettamario.it
poll.mosaicohairboutique.com
type.cookieacademyonline.com
upload.atlasoutfittersk9.com
bot.brigadeirosemdrama.com.br
tuttirecepcao.fratucci.com.br
forms.escoladeautomacao.com.br
onboarding.libertydreamcare.ie
recepcao.tutti.fratucci.com.br
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
baleia.testeeventos.progenbr.com
bot.cabin-rentals-of-georgia.net
open.campus.bot.aalen.university
sondaggio.mosaicohairboutique.it
baleia.testegabinete.progenbr.com
gerador.verificadordehospedes.com
personal-trainer.barrettamario.it
sondaggio.mosaicohairboutique.com
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 27b009d May 16, 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

docs-git-main-typebot-io.vercel.app
docs.typebot.io
docs-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 27b009d May 16, 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

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

Please sign in to comment.