@botonic/react 0.37.1 → 0.38.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/lib/cjs/components/custom-message.js +2 -3
  2. package/lib/cjs/components/custom-message.js.map +1 -1
  3. package/lib/cjs/webchat/context/actions.d.ts +1 -0
  4. package/lib/cjs/webchat/context/actions.js +1 -0
  5. package/lib/cjs/webchat/context/actions.js.map +1 -1
  6. package/lib/cjs/webchat/context/index.js +3 -0
  7. package/lib/cjs/webchat/context/index.js.map +1 -1
  8. package/lib/cjs/webchat/context/messages-reducer.js +55 -21
  9. package/lib/cjs/webchat/context/messages-reducer.js.map +1 -1
  10. package/lib/cjs/webchat/context/types.d.ts +1 -0
  11. package/lib/cjs/webchat/context/use-webchat.d.ts +1 -0
  12. package/lib/cjs/webchat/context/use-webchat.js +5 -0
  13. package/lib/cjs/webchat/context/use-webchat.js.map +1 -1
  14. package/lib/cjs/webchat/custom-messages/rating/index.js +18 -34
  15. package/lib/cjs/webchat/custom-messages/rating/index.js.map +1 -1
  16. package/lib/cjs/webchat/tracking.d.ts +6 -6
  17. package/lib/cjs/webchat/tracking.js +24 -5
  18. package/lib/cjs/webchat/tracking.js.map +1 -1
  19. package/lib/cjs/webchat/webchat.js +2 -1
  20. package/lib/cjs/webchat/webchat.js.map +1 -1
  21. package/lib/esm/components/custom-message.js +2 -3
  22. package/lib/esm/components/custom-message.js.map +1 -1
  23. package/lib/esm/webchat/context/actions.d.ts +1 -0
  24. package/lib/esm/webchat/context/actions.js +1 -0
  25. package/lib/esm/webchat/context/actions.js.map +1 -1
  26. package/lib/esm/webchat/context/index.js +3 -0
  27. package/lib/esm/webchat/context/index.js.map +1 -1
  28. package/lib/esm/webchat/context/messages-reducer.js +55 -21
  29. package/lib/esm/webchat/context/messages-reducer.js.map +1 -1
  30. package/lib/esm/webchat/context/types.d.ts +1 -0
  31. package/lib/esm/webchat/context/use-webchat.d.ts +1 -0
  32. package/lib/esm/webchat/context/use-webchat.js +5 -0
  33. package/lib/esm/webchat/context/use-webchat.js.map +1 -1
  34. package/lib/esm/webchat/custom-messages/rating/index.js +18 -33
  35. package/lib/esm/webchat/custom-messages/rating/index.js.map +1 -1
  36. package/lib/esm/webchat/tracking.d.ts +6 -6
  37. package/lib/esm/webchat/tracking.js +24 -5
  38. package/lib/esm/webchat/tracking.js.map +1 -1
  39. package/lib/esm/webchat/webchat.js +2 -1
  40. package/lib/esm/webchat/webchat.js.map +1 -1
  41. package/package.json +2 -2
  42. package/src/components/custom-message.tsx +2 -3
  43. package/src/webchat/context/actions.ts +1 -0
  44. package/src/webchat/context/index.tsx +3 -0
  45. package/src/webchat/context/messages-reducer.ts +90 -23
  46. package/src/webchat/context/types.ts +4 -0
  47. package/src/webchat/context/use-webchat.ts +14 -0
  48. package/src/webchat/custom-messages/rating/index.tsx +18 -35
  49. package/src/webchat/tracking.ts +45 -7
  50. package/src/webchat/webchat.tsx +2 -0
@@ -1,5 +1,4 @@
1
1
  import { SENDERS } from '../../index-types'
2
- import { isCustom } from '../../message-utils'
3
2
  import { WebchatAction } from './actions'
4
3
  import { WebchatState } from './types'
5
4
 
@@ -14,6 +13,8 @@ export const messagesReducer = (
14
13
  return addMessageComponent(state, action)
15
14
  case WebchatAction.UPDATE_MESSAGE:
16
15
  return updateMessageReducer(state, action)
16
+ case WebchatAction.UPDATE_CUSTOM_MESSAGE_PROPS:
17
+ return updateCustomMessagePropsReducer(state, action)
17
18
  case WebchatAction.UPDATE_REPLIES:
18
19
  return { ...state, replies: action.payload }
19
20
  case WebchatAction.REMOVE_REPLIES:
@@ -90,32 +91,29 @@ function updateMessageReducer(
90
91
  state: WebchatState,
91
92
  action: { type: WebchatAction; payload?: any }
92
93
  ) {
93
- const msgIndex = state.messagesJSON.map(m => m.id).indexOf(action.payload.id)
94
+ const messageId = action.payload.id
95
+ const msgIndex = state.messagesJSON.map(m => m.id).indexOf(messageId)
94
96
  if (msgIndex > -1) {
95
97
  const msgComponent = state.messagesComponents[msgIndex]
96
- let updatedMessageComponents = {}
98
+ let updatedMsgComponent = {}
97
99
  if (msgComponent) {
98
- const updatedMsgComponent = {
100
+ updatedMsgComponent = {
99
101
  ...msgComponent,
100
102
  ...{
101
103
  props: { ...msgComponent.props, ack: action.payload.ack },
102
104
  },
103
105
  }
106
+ }
104
107
 
105
- if (isCustom(action.payload)) {
106
- // If the message is a custom message, update the json property to update props.
107
- // If user close and open the chat the message will render again with the new props.
108
- updatedMsgComponent.props.json = action.payload.data.json
109
- }
108
+ const updatedMessagesComponents = msgComponent
109
+ ? getUpdatedMessagesComponents(state, msgIndex, updatedMsgComponent)
110
+ : state.messagesComponents
110
111
 
111
- updatedMessageComponents = {
112
- messagesComponents: [
113
- ...state.messagesComponents.slice(0, msgIndex),
114
- { ...updatedMsgComponent },
115
- ...state.messagesComponents.slice(msgIndex + 1),
116
- ],
117
- }
118
- }
112
+ const messageJSON = state.messagesJSON.find(m => m.id === messageId)
113
+
114
+ const updatedMessagesJSON = messageJSON
115
+ ? getUpdatedMessagesJSON(state, msgIndex, action.payload)
116
+ : state.messagesJSON
119
117
 
120
118
  const numUnreadMessages = state.messagesComponents.filter(
121
119
  messageComponent => messageComponent.props.isUnread
@@ -123,12 +121,8 @@ function updateMessageReducer(
123
121
 
124
122
  return {
125
123
  ...state,
126
- messagesJSON: [
127
- ...state.messagesJSON.slice(0, msgIndex),
128
- { ...action.payload },
129
- ...state.messagesJSON.slice(msgIndex + 1),
130
- ],
131
- ...updatedMessageComponents,
124
+ messagesJSON: updatedMessagesJSON,
125
+ messagesComponents: updatedMessagesComponents,
132
126
  numUnreadMessages,
133
127
  }
134
128
  }
@@ -150,3 +144,76 @@ function addMessageReducer(
150
144
  messagesJSON: [...(state.messagesJSON || []), action.payload],
151
145
  }
152
146
  }
147
+
148
+ function updateCustomMessagePropsReducer(
149
+ state: WebchatState,
150
+ action: { type: WebchatAction; payload?: any }
151
+ ) {
152
+ const { messageId, props } = action.payload
153
+
154
+ if (!messageId) {
155
+ return state
156
+ }
157
+
158
+ // Similar to updateMessageReducer but only for custom messages when update props
159
+ const msgIndex = state.messagesJSON.map(m => m.id).indexOf(messageId)
160
+ if (msgIndex > -1) {
161
+ const msgComponent = state.messagesComponents[msgIndex]
162
+ if (msgComponent) {
163
+ msgComponent.props = {
164
+ ...msgComponent.props,
165
+ ack: action.payload.ack,
166
+ ...props,
167
+ }
168
+ }
169
+
170
+ const updatedMessagesComponents = msgComponent
171
+ ? getUpdatedMessagesComponents(state, msgIndex, msgComponent)
172
+ : state.messagesComponents
173
+
174
+ const messageJSON = state.messagesJSON.find(m => m.id === messageId)
175
+ if (messageJSON) {
176
+ messageJSON.data = {
177
+ ...messageJSON.data,
178
+ ...props,
179
+ }
180
+ }
181
+
182
+ const updatedMessagesJSON = messageJSON
183
+ ? getUpdatedMessagesJSON(state, msgIndex, messageJSON)
184
+ : state.messagesJSON
185
+
186
+ return {
187
+ ...state,
188
+ messagesJSON: updatedMessagesJSON,
189
+ messagesComponents: updatedMessagesComponents,
190
+ }
191
+ }
192
+
193
+ return state
194
+ }
195
+
196
+ // Helper functions to update messagesComponents and messagesJSON
197
+ function getUpdatedMessagesComponents(
198
+ state: WebchatState,
199
+ msgIndex: number,
200
+ updatedMessageComponent: any
201
+ ) {
202
+ return [
203
+ ...state.messagesComponents.slice(0, msgIndex),
204
+ { ...updatedMessageComponent },
205
+ ...state.messagesComponents.slice(msgIndex + 1),
206
+ ]
207
+ }
208
+
209
+ function getUpdatedMessagesJSON(
210
+ state: WebchatState,
211
+ msgIndex: number,
212
+ messageJSON: any
213
+ ) {
214
+ return [
215
+ ...state.messagesJSON.slice(0, msgIndex),
216
+ { ...messageJSON },
217
+ ...state.messagesJSON.slice(msgIndex + 1),
218
+ ]
219
+ }
@@ -75,6 +75,10 @@ export interface WebchatContextProps {
75
75
  toggleEmojiPicker: (toggle: boolean) => void
76
76
  togglePersistentMenu: (toggle: boolean) => void
77
77
  toggleCoverComponent: (toggle: boolean) => void
78
+ updateCustomMessageProps: (
79
+ json: Record<string, any>,
80
+ messageId?: string
81
+ ) => void
78
82
  updateLatestInput: (input: ClientInput) => void
79
83
  updateMessage: (message: WebchatMessage) => void
80
84
  updateReplies: (replies: (typeof Reply)[]) => void
@@ -55,6 +55,10 @@ export interface UseWebchat {
55
55
  toggleEmojiPicker: (toggle: boolean) => void
56
56
  togglePersistentMenu: (toggle: boolean) => void
57
57
  toggleWebchat: (toggle: boolean) => void
58
+ updateCustomMessageProps: (
59
+ props: Record<string, any>,
60
+ messageId?: string
61
+ ) => void
58
62
  updateDevSettings: (settings: DevSettings) => void
59
63
  updateHandoff: (handoff: boolean) => void
60
64
  updateLastMessageDate: (date: string) => void
@@ -102,6 +106,15 @@ export function useWebchat(theme?: WebchatTheme): UseWebchat {
102
106
  payload: message,
103
107
  })
104
108
 
109
+ const updateCustomMessageProps = (
110
+ props: Record<string, any>,
111
+ messageId?: string
112
+ ) =>
113
+ webchatDispatch({
114
+ type: WebchatAction.UPDATE_CUSTOM_MESSAGE_PROPS,
115
+ payload: { messageId, props },
116
+ })
117
+
105
118
  const updateMessage = (message: WebchatMessage) =>
106
119
  webchatDispatch({ type: WebchatAction.UPDATE_MESSAGE, payload: message })
107
120
 
@@ -260,6 +273,7 @@ export function useWebchat(theme?: WebchatTheme): UseWebchat {
260
273
  toggleEmojiPicker,
261
274
  togglePersistentMenu,
262
275
  toggleWebchat,
276
+ updateCustomMessageProps,
263
277
  updateDevSettings,
264
278
  updateHandoff,
265
279
  updateLastMessageDate,
@@ -1,5 +1,4 @@
1
1
  import { INPUT, InputType } from '@botonic/core'
2
- import merge from 'lodash.merge'
3
2
  import React, { useContext, useState } from 'react'
4
3
  import { ThemeContext } from 'styled-components'
5
4
 
@@ -14,56 +13,40 @@ interface CustomRatingMessageProps {
14
13
  messageText: string
15
14
  buttonText: string
16
15
  ratingType: RatingType
17
- json: { messageId?: string; valueSent?: number }
16
+ id?: string // This id not exist in local development
17
+ valueSent?: number
18
18
  }
19
19
 
20
20
  const CustomRatingMessage: React.FC<CustomRatingMessageProps> = props => {
21
- const { payloads, messageText, buttonText, ratingType } = props
22
- const { webchatState, updateMessage, sendInput } = useContext(WebchatContext)
21
+ const { payloads, messageText, buttonText, ratingType, id, valueSent } = props
22
+ const { updateCustomMessageProps, sendInput } = useContext(WebchatContext)
23
23
 
24
24
  const theme = useContext(ThemeContext)
25
25
  const color = theme?.brand?.color ?? ''
26
26
 
27
- const [ratingValue, setRatingValue] = useState(
28
- props.json?.valueSent ? props.json.valueSent : -1
29
- )
27
+ const [ratingValue, setRatingValue] = useState(valueSent ? valueSent : -1)
30
28
  const [showRating, setShowRating] = useState(true)
31
29
 
32
30
  const ratingChanged = (newRating: number) => {
33
31
  setRatingValue(newRating)
34
32
  }
35
33
 
36
- const updateMessageJSON = (messageId?: string) => {
37
- if (messageId) {
38
- const messageToUpdate = webchatState.messagesJSON.filter(m => {
39
- return m.id === messageId
40
- })[0]
41
- const messageInfo = {
42
- data: {
43
- json: {
44
- valueSent: ratingValue,
45
- },
46
- },
47
- }
48
- const updatedMsg = merge(messageToUpdate, messageInfo)
49
- updateMessage(updatedMsg)
50
- }
51
- }
52
-
53
34
  const handleButtonSend = () => {
54
- if (ratingValue !== -1) {
55
- setShowRating(false)
35
+ if (ratingValue === -1) return
56
36
 
57
- const payload = payloads[ratingValue - 1]
37
+ const json = {
38
+ valueSent: ratingValue,
39
+ }
40
+ updateCustomMessageProps(json, id)
58
41
 
59
- updateMessageJSON(props.json?.messageId)
42
+ setShowRating(false)
43
+ const payload = payloads[ratingValue - 1]
60
44
 
61
- const input = {
62
- type: INPUT.POSTBACK as InputType,
63
- payload,
64
- }
65
- void sendInput(input)
45
+ const input = {
46
+ type: INPUT.POSTBACK as InputType,
47
+ payload,
66
48
  }
49
+ void sendInput(input)
67
50
  }
68
51
 
69
52
  const disabled = ratingValue === -1
@@ -77,10 +60,10 @@ const CustomRatingMessage: React.FC<CustomRatingMessageProps> = props => {
77
60
  onRatingChange={ratingChanged}
78
61
  ratingValue={ratingValue}
79
62
  ratingType={ratingType}
80
- valueSent={props.json?.valueSent}
63
+ valueSent={valueSent}
81
64
  />
82
65
  </MessageBubble>
83
- {!props.json?.valueSent && showRating && (
66
+ {!props?.valueSent && showRating && (
84
67
  <Button
85
68
  autodisable={true}
86
69
  disabled={disabled}
@@ -1,3 +1,8 @@
1
+ import {
2
+ EventCustom,
3
+ EventFeedback,
4
+ EventFeedbackKnowledgebase,
5
+ } from '@botonic/core'
1
6
  import { useContext } from 'react'
2
7
  import { v7 as uuidv7 } from 'uuid'
3
8
 
@@ -14,12 +19,23 @@ interface TrackKnowledgebaseFeedbackArgs {
14
19
  inferenceId?: string
15
20
  }
16
21
 
17
- export enum FeedbackOption {
22
+ enum FeedbackOption {
18
23
  ThumbsUp = 'thumbsUp',
19
24
  ThumbsDown = 'thumbsDown',
20
25
  }
21
26
 
22
- export function useTracking() {
27
+ interface UseTracking {
28
+ trackKnowledgebaseFeedback: ({
29
+ messageId,
30
+ isUseful,
31
+ botInteractionId,
32
+ inferenceId,
33
+ }: TrackKnowledgebaseFeedbackArgs) => Promise<void>
34
+ trackCustomEvent: (event: EventCustom) => Promise<void>
35
+ trackFeedbackEvent: (event: EventFeedback) => Promise<void>
36
+ }
37
+
38
+ export function useTracking(): UseTracking {
23
39
  const { webchatState, trackEvent } = useContext(WebchatContext)
24
40
 
25
41
  const getRequest = () => {
@@ -46,10 +62,13 @@ export function useTracking() {
46
62
  if (!trackEvent) {
47
63
  return
48
64
  }
65
+ const request = getRequest()
49
66
 
50
- const args = {
51
- knowledgebaseInferenceId: inferenceId,
52
- feedbackBotInteractionId: botInteractionId,
67
+ // inferenceId and botInteractionId are strings, but in local development they are undefined
68
+ const event: EventFeedbackKnowledgebase = {
69
+ action: EventAction.FeedbackKnowledgebase,
70
+ knowledgebaseInferenceId: inferenceId as string,
71
+ feedbackBotInteractionId: botInteractionId as string,
53
72
  feedbackTargetId: messageId,
54
73
  feedbackGroupId: uuidv7(),
55
74
  possibleOptions: [FeedbackOption.ThumbsDown, FeedbackOption.ThumbsUp],
@@ -57,11 +76,30 @@ export function useTracking() {
57
76
  option: isUseful ? FeedbackOption.ThumbsUp : FeedbackOption.ThumbsDown,
58
77
  value: isUseful ? 1 : 0,
59
78
  }
79
+ const { action, ...eventArgs } = event
80
+
81
+ await trackEvent(request, action, eventArgs)
82
+ }
83
+
84
+ const trackCustomEvent = async (event: EventCustom) => {
85
+ if (!trackEvent) {
86
+ return
87
+ }
60
88
 
61
89
  const request = getRequest()
90
+ const { action, ...eventArgs } = event
91
+ await trackEvent(request, action, eventArgs)
92
+ }
62
93
 
63
- await trackEvent(request, EventAction.FeedbackKnowledgebase, args)
94
+ const trackFeedbackEvent = async (event: EventFeedback) => {
95
+ if (!trackEvent) {
96
+ return
97
+ }
98
+
99
+ const request = getRequest()
100
+ const { action, ...eventArgs } = event
101
+ await trackEvent(request, action, eventArgs)
64
102
  }
65
103
 
66
- return { trackKnowledgebaseFeedback }
104
+ return { trackKnowledgebaseFeedback, trackCustomEvent, trackFeedbackEvent }
67
105
  }
@@ -86,6 +86,7 @@ const Webchat = forwardRef<WebchatRef | null, WebchatProps>((props, ref) => {
86
86
  toggleEmojiPicker,
87
87
  togglePersistentMenu,
88
88
  toggleWebchat,
89
+ updateCustomMessageProps,
89
90
  updateDevSettings,
90
91
  updateHandoff,
91
92
  updateLastMessageDate,
@@ -650,6 +651,7 @@ const Webchat = forwardRef<WebchatRef | null, WebchatProps>((props, ref) => {
650
651
  toggleEmojiPicker,
651
652
  togglePersistentMenu,
652
653
  toggleCoverComponent,
654
+ updateCustomMessageProps,
653
655
  updateLatestInput,
654
656
  updateMessage,
655
657
  updateReplies,