Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blt 1459 queue position component in botonic #2986

Draft
wants to merge 5 commits into
base: master-lts
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: prepare handoffState
  • Loading branch information
vanbasten17 committed Mar 7, 2025
commit d38a9696bc7b117b2bfb951a07a8ac86d749fe9c
61 changes: 45 additions & 16 deletions packages/botonic-react/src/components/webchat-settings.tsx
Original file line number Diff line number Diff line change
@@ -4,7 +4,11 @@ import React, { useContext } from 'react'
import { renderComponent } from '../util/react'
import { stringifyWithRegexs } from '../util/regexs'
import { WebchatContext } from '../webchat/context'
import { PersistentMenuOptionsTheme, ThemeProps } from '../webchat/theme/types'
import {
HandoffState,
PersistentMenuOptionsTheme,
ThemeProps,
} from '../webchat/theme/types'
import { BlockInputOption } from './index-types'

export interface WebchatSettingsProps {
@@ -15,11 +19,13 @@ export interface WebchatSettingsProps {
enableUserInput?: boolean
persistentMenu?: PersistentMenuOptionsTheme
theme?: ThemeProps
handoffState?: Partial<HandoffState>
user?: { extra_data?: any }
}

export const WebchatSettings = ({
theme,
handoffState,
blockInputs,
persistentMenu,
enableEmojiPicker,
@@ -51,49 +57,72 @@ export const WebchatSettings = ({
enableAttachments,
enableUserInput,
enableAnimations,
handoffState,
})
return (
//@ts-ignore
<message
type={INPUT.WEBCHAT_SETTINGS}
settings={stringifyWithRegexs({ theme: updatedTheme, user })}
settings={stringifyWithRegexs({
theme: updatedTheme,
user,
handoffState,
})}
/>
)
}
return renderComponent({ renderBrowser, renderNode })
}

export const normalizeWebchatSettings = (settings: WebchatSettingsProps) => {
let {
const {
theme,
blockInputs,
persistentMenu,
enableEmojiPicker,
enableAttachments,
enableUserInput,
enableAnimations,
handoffState,
} = settings
if (!theme) theme = {}
if (!theme.userInput) theme.userInput = {}

// Normalize theme settings
const normalizedTheme = theme || {}
if (!normalizedTheme.userInput) {
normalizedTheme.userInput = {}
}
if (!normalizedTheme.animations) {
normalizedTheme.animations = {}
}

if (persistentMenu !== undefined) {
theme.userInput.persistentMenu = persistentMenu
normalizedTheme.userInput.persistentMenu = persistentMenu
}
if (enableEmojiPicker !== undefined) {
if (!theme.userInput.emojiPicker) theme.userInput.emojiPicker = {}
theme.userInput.emojiPicker.enable = enableEmojiPicker
if (!normalizedTheme.userInput.emojiPicker) {
normalizedTheme.userInput.emojiPicker = {}
}

normalizedTheme.userInput.emojiPicker.enable = enableEmojiPicker
}
if (enableAttachments !== undefined) {
if (!theme.userInput.attachments) theme.userInput.attachments = {}
theme.userInput.attachments.enable = enableAttachments
if (!normalizedTheme.userInput.attachments) {
normalizedTheme.userInput.attachments = {}
}
normalizedTheme.userInput.attachments.enable = enableAttachments
}
if (enableUserInput !== undefined) {
theme.userInput.enable = enableUserInput
normalizedTheme.userInput.enable = enableUserInput
}
if (blockInputs !== undefined) {
normalizedTheme.userInput.blockInputs = blockInputs
}
if (blockInputs !== undefined) theme.userInput.blockInputs = blockInputs

if (!theme.animations) theme.animations = {}
if (enableAnimations !== undefined) {
theme.animations.enable = enableAnimations
normalizedTheme.animations.enable = enableAnimations
}

return {
updatedTheme: normalizedTheme,
updatedHandoffState: handoffState,
}
return theme
}
3 changes: 3 additions & 0 deletions packages/botonic-react/src/dev-app.jsx
Original file line number Diff line number Diff line change
@@ -56,6 +56,7 @@ export class DevApp extends WebchatApp {
getComponent(host, optionsAtRuntime = {}) {
let {
theme = {},
handoffState = {},
persistentMenu,
coverComponent,
blockInputs,
@@ -74,6 +75,7 @@ export class DevApp extends WebchatApp {
...webchatOptions
} = optionsAtRuntime
theme = merge(this.theme, theme)
handoffState = merge(this.handoffState, handoffState)
persistentMenu = persistentMenu || this.persistentMenu
coverComponent = coverComponent || this.coverComponent
blockInputs = blockInputs || this.blockInputs
@@ -97,6 +99,7 @@ export class DevApp extends WebchatApp {
host={this.host}
shadowDOM={this.shadowDOM}
theme={theme}
handoffState={handoffState}
persistentMenu={persistentMenu}
coverComponent={coverComponent}
blockInputs={blockInputs}
3 changes: 3 additions & 0 deletions packages/botonic-react/src/index-types.ts
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@ import { CloseWebviewOptions } from './contexts'
import { UseWebchat } from './webchat/context/use-webchat'
import {
CoverComponentOptions,
HandoffState,
PersistentMenuOptionsTheme,
ThemeProps,
} from './webchat/theme/types'
@@ -89,6 +90,7 @@ interface AddBotResponseArgs {

export interface WebchatArgs {
theme?: ThemeProps
handoffState?: Partial<HandoffState>
persistentMenu?: PersistentMenuOptionsTheme
coverComponent?: CoverComponentOptions
blockInputs?: BlockInputOption[]
@@ -117,6 +119,7 @@ export interface WebchatProps {
webchatHooks?: UseWebchat
initialSession?: any
initialDevSettings?: any
handoffState?: Partial<HandoffState>
onStateChange: (args: OnStateChangeArgs) => void

shadowDOM?: any
7 changes: 7 additions & 0 deletions packages/botonic-react/src/webchat-app.tsx
Original file line number Diff line number Diff line change
@@ -22,13 +22,15 @@ import { msgToBotonic } from './msg-to-botonic'
import { isShadowDOMSupported, onDOMLoaded } from './util/dom'
import {
CoverComponentOptions,
HandoffState,
PersistentMenuOptionsTheme,
ThemeProps,
} from './webchat/theme/types'
import { Webchat } from './webchat/webchat'

export class WebchatApp {
public theme?: ThemeProps
public handoffState?: Partial<HandoffState>
public persistentMenu?: PersistentMenuOptionsTheme
public coverComponent?: CoverComponentOptions
public blockInputs?: BlockInputOption[]
@@ -63,6 +65,7 @@ export class WebchatApp {

constructor({
theme = {},
handoffState = {},
persistentMenu,
coverComponent,
blockInputs,
@@ -87,6 +90,7 @@ export class WebchatApp {
server,
}: WebchatArgs) {
this.theme = theme
this.handoffState = handoffState
this.persistentMenu = persistentMenu
this.coverComponent = coverComponent
this.blockInputs = blockInputs
@@ -377,6 +381,7 @@ export class WebchatApp {
getComponent(host: HTMLDivElement, optionsAtRuntime: WebchatArgs = {}) {
let {
theme = {},
handoffState = {},
persistentMenu,
coverComponent,
blockInputs,
@@ -401,6 +406,7 @@ export class WebchatApp {
...webchatOptions
} = optionsAtRuntime
theme = merge(this.theme, theme)
handoffState = merge(this.handoffState, handoffState)
persistentMenu = persistentMenu || this.persistentMenu
coverComponent = coverComponent || this.coverComponent
blockInputs = blockInputs || this.blockInputs
@@ -431,6 +437,7 @@ export class WebchatApp {
host={this.host}
shadowDOM={this.shadowDOM}
theme={theme}
handoffState={handoffState}
persistentMenu={persistentMenu}
coverComponent={coverComponent}
blockInputs={blockInputs}
2 changes: 1 addition & 1 deletion packages/botonic-react/src/webchat/context/actions.ts
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ export enum WebchatAction {
TOGGLE_PERSISTENT_MENU = 'togglePersistentMenu',
TOGGLE_WEBCHAT = 'toggleWebchat',
UPDATE_DEV_SETTINGS = 'updateDevSettings',
UPDATE_HANDOFF = 'updateHandoff',
UPDATE_HANDOFF_STATE = 'updateHandoffState',
UPDATE_LAST_MESSAGE_DATE = 'updateLastMessageDate',
UPDATE_LAST_ROUTE_PATH = 'updateLastRoutePath',
UPDATE_LATEST_INPUT = 'updateLatestInput',
5 changes: 3 additions & 2 deletions packages/botonic-react/src/webchat/context/types.ts
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import { Reply, WebchatSettingsProps, Webview } from '../../components'
import { CloseWebviewOptions } from '../../contexts'
import { TrackEventFunction, WebchatMessage } from '../../index-types'
import { WebchatStateTheme } from '../index-types'
import { ThemeProps } from '../theme/types'
import { HandoffState, ThemeProps } from '../theme/types'

export interface ErrorMessage {
message?: string
@@ -31,9 +31,10 @@ export interface WebchatState {
webviewParams: null
session: Partial<CoreSession>
lastRoutePath?: string
handoff: boolean
handoffState: HandoffState
theme: WebchatStateTheme // TODO: type this as ThemeProps
themeUpdates: Partial<WebchatStateTheme> // TODO: type this as Partial<ThemeProps>
handoffStateUpdates: Partial<HandoffState>
error: ErrorMessage
online: boolean
devSettings: DevSettings
34 changes: 27 additions & 7 deletions packages/botonic-react/src/webchat/context/use-webchat.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import { Reply } from '../../components'
import { Webview } from '../../components/index-types'
import { COLORS, WEBCHAT } from '../../constants'
import { WebchatMessage } from '../../index-types'
import { ThemeProps } from '../theme/types'
import { HandoffState, ThemeProps } from '../theme/types'
import { WebchatAction } from './actions'
import { ClientInput, DevSettings, ErrorMessage, WebchatState } from './types'
import { webchatReducer } from './webchat-reducer'
@@ -22,7 +22,16 @@ export const webchatInitialState: WebchatState = {
webviewParams: null,
session: { user: undefined },
lastRoutePath: undefined,
handoff: false,
handoffState: {
isHandoff: false,
previousQueuePosition: null,
previousQueuePositionNotifiedAt: null,
currentQueuePosition: null,
currentQueuePositionNotifiedAt: null,
agentName: null,
agentImage: null,
},
handoffStateUpdates: {},
// TODO: type create a defaultTheme using ThemeProps, and put this in initialState
theme: {
headerTitle: WEBCHAT.DEFAULTS.TITLE,
@@ -66,7 +75,10 @@ export interface UseWebchat {
togglePersistentMenu: (toggle: boolean) => void
toggleWebchat: (toggle: boolean) => void
updateDevSettings: (settings: DevSettings) => void
updateHandoff: (handoff: boolean) => void
updateHandoffState: (
handoffState: Partial<HandoffState>,
handoffStateUpdates?: Partial<HandoffState>
) => void
updateLastMessageDate: (date: string) => void
updateLastRoutePath: (path: string) => void
updateLatestInput: (input: ClientInput) => void
@@ -148,11 +160,19 @@ export function useWebchat(): UseWebchat {
payload: path,
})

const updateHandoff = (handoff: boolean) =>
const updateHandoffState = (
handoffState: Partial<HandoffState>,
handoffStateUpdates?: Partial<HandoffState>
) => {
const payload =
handoffStateUpdates !== undefined
? { ...handoffState, ...handoffStateUpdates }
: handoffState
webchatDispatch({
type: WebchatAction.UPDATE_HANDOFF,
payload: handoff,
type: WebchatAction.UPDATE_HANDOFF_STATE,
payload,
})
}

const updateTheme = (theme: ThemeProps, themeUpdates?: ThemeProps) => {
const payload =
@@ -268,7 +288,7 @@ export function useWebchat(): UseWebchat {
togglePersistentMenu,
toggleWebchat,
updateDevSettings,
updateHandoff,
updateHandoffState,
updateLastMessageDate,
updateLastRoutePath,
updateLatestInput,
7 changes: 5 additions & 2 deletions packages/botonic-react/src/webchat/context/webchat-reducer.ts
Original file line number Diff line number Diff line change
@@ -21,8 +21,11 @@ export function webchatReducer(
...state,
...action.payload,
}
case WebchatAction.UPDATE_HANDOFF:
return { ...state, handoff: action.payload }
case WebchatAction.UPDATE_HANDOFF_STATE:
return {
...state,
handoffState: { ...state.handoffState, ...action.payload },
}
case WebchatAction.TOGGLE_WEBCHAT: {
const isWebchatOpen = action.payload
return {
10 changes: 10 additions & 0 deletions packages/botonic-react/src/webchat/theme/types.ts
Original file line number Diff line number Diff line change
@@ -117,3 +117,13 @@ export interface ThemeProps {
}
imagePreviewer?: React.ComponentType<ImagePreviewerProps>
}

export interface HandoffState {
isHandoff: boolean
previousQueuePosition: number | null
previousQueuePositionNotifiedAt: string | null
currentQueuePosition: number | null
currentQueuePositionNotifiedAt: string | null
agentName: string | null
agentImage: string | null
}
6 changes: 5 additions & 1 deletion packages/botonic-react/src/webchat/webchat-dev.jsx
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ const initialSession = {
// eslint-disable-next-line react/display-name
export const WebchatDev = forwardRef((props, ref) => {
const webchatHooks = useWebchat()
const { webchatState, updateTheme } = webchatHooks
const { webchatState, updateTheme, updateHandoffState } = webchatHooks

/* TODO: webchatState.theme should be included in the dependencies array
together with props.theme. The problem is that this effect modifies webchatState
@@ -56,6 +56,10 @@ export const WebchatDev = forwardRef((props, ref) => {
updateTheme(merge(webchatState.theme, props.theme))
}, [props.theme])

useEffect(() => {
updateHandoffState(merge(webchatState.handoffState, props.handoffState))
}, [props.handoffState])

return (
<>
<Webchat
Loading