@envive-ai/react-widgets-v3 0.3.21 → 0.3.23
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.
- package/dist/debug/chatEmbed.cjs +1 -1
- package/dist/debug/chatEmbed.js +1 -1
- package/dist/debug/reportIssue.cjs +1 -1
- package/dist/debug/reportIssue.js +1 -1
- package/dist/hocs/withBaseWidget/types.d.cts +5 -6
- package/dist/hocs/withBaseWidget/types.d.ts +5 -6
- package/dist/hocs/withBaseWidget/withBaseWidget.cjs +37 -43
- package/dist/hocs/withBaseWidget/withBaseWidget.d.cts +2 -2
- package/dist/hocs/withBaseWidget/withBaseWidget.d.ts +2 -2
- package/dist/hocs/withBaseWidget/withBaseWidget.js +39 -45
- package/dist/packages/widgets/dist/SearchResults/SearchResultsWidget.d.ts +2 -2
- package/dist/packages/widgets/dist/SearchZeroState/SearchZeroStateWidget.d.ts +2 -2
- package/dist/packages/widgets/dist/SuggestionBar/SuggestionBar.d.ts +2 -2
- package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.cjs +34 -8
- package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.cts +3 -3
- package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.ts +3 -3
- package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.js +35 -9
- package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.cjs +13 -5
- package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.cts +3 -3
- package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.ts +3 -3
- package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.js +13 -5
- package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.cjs +32 -15
- package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.cts +3 -3
- package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.ts +3 -3
- package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.js +35 -18
- package/dist/widgets/FloatingChatWidget/FloatingChatWidget.cjs +36 -32
- package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.cts +2 -2
- package/dist/widgets/FloatingChatWidget/FloatingChatWidget.js +37 -33
- package/dist/widgets/FloatingChatWidget/hooks/useAutoPopup.cjs +2 -2
- package/dist/widgets/FloatingChatWidget/hooks/useAutoPopup.js +2 -2
- package/dist/widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget.cjs +7 -1
- package/dist/widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget.js +7 -1
- package/dist/widgets/ProductCardWidget/ProductCardWidget.cjs +31 -15
- package/dist/widgets/ProductCardWidget/ProductCardWidget.d.cts +2 -2
- package/dist/widgets/ProductCardWidget/ProductCardWidget.d.ts +2 -2
- package/dist/widgets/ProductCardWidget/ProductCardWidget.js +33 -17
- package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.cjs +33 -16
- package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.cts +3 -3
- package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.ts +3 -3
- package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.js +36 -19
- package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.cjs +3 -12
- package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.cts +2 -2
- package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.ts +2 -2
- package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.js +5 -14
- package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.cts +2 -2
- package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.ts +2 -2
- package/dist/widgets/SocialProofWidget/SocialProofWidget.cjs +34 -17
- package/dist/widgets/SocialProofWidget/SocialProofWidget.d.cts +3 -3
- package/dist/widgets/SocialProofWidget/SocialProofWidget.d.ts +3 -3
- package/dist/widgets/SocialProofWidget/SocialProofWidget.js +37 -20
- package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.cjs +2 -11
- package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.cts +2 -2
- package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.ts +2 -2
- package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.js +4 -13
- package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.cts +2 -2
- package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.ts +2 -2
- package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.cjs +31 -14
- package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.cts +3 -3
- package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.ts +3 -3
- package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.js +34 -17
- package/dist/widgets/dist/SearchResults/SearchResultsWidget.d.cts +2 -2
- package/dist/widgets/dist/SearchZeroState/SearchZeroStateWidget.d.cts +2 -2
- package/dist/widgets/dist/SuggestionBar/SuggestionBar.d.cts +2 -2
- package/dist/widgets/hooks/useGetWidgetStatus.cjs +1 -1
- package/dist/widgets/hooks/useGetWidgetStatus.js +1 -1
- package/dist/widgets/utils/functions.cjs +1 -1
- package/dist/widgets/utils/functions.js +1 -1
- package/package.json +1 -1
- package/src/hocs/withBaseWidget/__tests__/withBaseWidget.test.tsx +68 -302
- package/src/hocs/withBaseWidget/types.ts +6 -6
- package/src/hocs/withBaseWidget/withBaseWidget.tsx +46 -61
- package/src/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.tsx +33 -0
- package/src/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.tsx +9 -1
- package/src/widgets/ChatPreviewWidget/ChatPreviewWidget.tsx +34 -13
- package/src/widgets/ChatPreviewWidget/__tests__/ChatPreviewWidget.test.tsx +11 -5
- package/src/widgets/FloatingChatWidget/FloatingChatWidget.tsx +56 -38
- package/src/widgets/FloatingChatWidget/__tests__/FloatingChatWidget.test.tsx +11 -5
- package/src/widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget.tsx +8 -0
- package/src/widgets/ProductCardWidget/ProductCardWidget.tsx +37 -15
- package/src/widgets/ProductCardWidget/__tests__/ProductCardWidget.test.tsx +12 -8
- package/src/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.tsx +35 -13
- package/src/widgets/PromptButtonCarouselWithImageWidget/__tests__/PromptButtonCarouselWithImageWidget.test.tsx +19 -8
- package/src/widgets/PromptCarouselWidget/PromptCarouselWidget.tsx +1 -12
- package/src/widgets/PromptCarouselWidget/__tests__/PromptCarouselWidget.test.tsx +21 -8
- package/src/widgets/SocialProofWidget/SocialProofWidget.tsx +34 -12
- package/src/widgets/SocialProofWidget/__tests__/SocialProofWidget.test.tsx +19 -8
- package/src/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.tsx +1 -12
- package/src/widgets/TitledPromptCarouselWidget/__tests__/TitledPromptCarouselWidget.test.tsx +19 -8
- package/src/widgets/TypingAnimationWidget/TypingAnimationWidget.tsx +37 -14
- package/src/widgets/TypingAnimationWidget/__tests__/TypingAnimationWidget.test.tsx +19 -8
- package/src/widgets/__tests__/trackEventCanary.test.ts +1 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
|
-
import { SpiffyMetricsEventName } from '@envive-ai/react-hooks/contexts/amplitudeContext';
|
|
3
2
|
import { HardcopyResponse } from '@envive-ai/react-hooks/contexts/hardcopyContext';
|
|
4
3
|
import {
|
|
5
4
|
OrgUIConfigV3,
|
|
@@ -22,11 +21,6 @@ export interface BaseWidgetProps {
|
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
export interface WithBaseWidgetOptions {
|
|
25
|
-
/**
|
|
26
|
-
* The Amplitude event name to track when the widget becomes visible.
|
|
27
|
-
* Defaults to a generic component visible event if not provided.
|
|
28
|
-
*/
|
|
29
|
-
visibilityEventName?: SpiffyMetricsEventName;
|
|
30
24
|
/**
|
|
31
25
|
* Additional properties to include with the visibility event.
|
|
32
26
|
*/
|
|
@@ -36,4 +30,10 @@ export interface WithBaseWidgetOptions {
|
|
|
36
30
|
* Defaults to '0px'.
|
|
37
31
|
*/
|
|
38
32
|
rootMargin?: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Whether to defer loading of the widget until it is visible.
|
|
36
|
+
* Defaults to true
|
|
37
|
+
*/
|
|
38
|
+
deferLoading?: boolean;
|
|
39
39
|
}
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react';
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
useAmplitude,
|
|
6
|
-
} from '@envive-ai/react-hooks/contexts/amplitudeContext';
|
|
3
|
+
import { useAmplitude } from '@envive-ai/react-hooks/contexts/amplitudeContext';
|
|
4
|
+
import { useTrackComponentVisibleEvent } from '@envive-ai/react-hooks/hooks/TrackComponentVisibleEvent';
|
|
7
5
|
import { HardcopyResponse, useHardcopy } from '@envive-ai/react-hooks/contexts/hardcopyContext';
|
|
8
6
|
import { usePage } from '@envive-ai/react-hooks/contexts/pageContext';
|
|
9
7
|
import { useWidgetConfig } from '@envive-ai/react-hooks/contexts/widgetConfigContext';
|
|
10
8
|
import { OrgUIConfigV3, WidgetConfigV3 } from '@envive-ai/react-hooks/contexts/typesV3';
|
|
11
9
|
import { useUiConfig } from '@envive-ai/react-hooks/contexts/uiConfigContext';
|
|
12
|
-
|
|
13
10
|
import { BaseWidgetProps, WithBaseWidgetOptions } from './types';
|
|
14
11
|
|
|
15
12
|
export function withBaseWidget<P extends BaseWidgetProps>(
|
|
@@ -19,10 +16,10 @@ export function withBaseWidget<P extends BaseWidgetProps>(
|
|
|
19
16
|
const WrappedWidget = (props: P) => {
|
|
20
17
|
const { widgetConfigId, widgetType } = props;
|
|
21
18
|
const widgetRef = useRef<HTMLDivElement>(null);
|
|
22
|
-
const
|
|
23
|
-
const { trackEvent, isReady } = useAmplitude();
|
|
19
|
+
const { isReady } = useAmplitude();
|
|
24
20
|
|
|
25
21
|
const [hardcopyContent, setHardcopyContent] = useState<HardcopyResponse | undefined>(undefined);
|
|
22
|
+
const hardcopyFetchedRef = useRef(false);
|
|
26
23
|
const { getHardcopy, isLoading: isHardcopyLoading } = useHardcopy();
|
|
27
24
|
|
|
28
25
|
const { userEvent, isLoading: isPageLoading } = usePage();
|
|
@@ -36,87 +33,75 @@ export function withBaseWidget<P extends BaseWidgetProps>(
|
|
|
36
33
|
const isLoading =
|
|
37
34
|
isHardcopyLoading || isPageLoading || isWidgetConfigLoading || isUiConfigLoading;
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
const visibilityEventProps = {
|
|
37
|
+
widget_config_id: widgetConfigId,
|
|
38
|
+
widget_type: widgetType,
|
|
39
|
+
...options?.visibilityEventProps,
|
|
40
|
+
};
|
|
41
|
+
const { isVisible } = useTrackComponentVisibleEvent(
|
|
42
|
+
widgetRef,
|
|
43
|
+
visibilityEventProps,
|
|
44
|
+
options?.rootMargin || '0px',
|
|
45
|
+
isReady,
|
|
46
|
+
);
|
|
47
|
+
const startLoading: boolean = useMemo(() => {
|
|
48
|
+
if (options?.deferLoading === false) {
|
|
49
|
+
return true;
|
|
42
50
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (entry.isIntersecting && !hasTrackedEvent.current) {
|
|
47
|
-
const eventName =
|
|
48
|
-
options?.visibilityEventName || SpiffyMetricsEventName.ChatComponentVisible;
|
|
49
|
-
|
|
50
|
-
trackEvent({
|
|
51
|
-
eventName,
|
|
52
|
-
eventProps: {
|
|
53
|
-
widget_config_id: widgetConfigId,
|
|
54
|
-
widget_type: widgetType,
|
|
55
|
-
...options?.visibilityEventProps,
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
hasTrackedEvent.current = true;
|
|
60
|
-
observer.disconnect();
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
rootMargin: options?.rootMargin || '0px',
|
|
65
|
-
},
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
observer.observe(widgetRef.current);
|
|
69
|
-
|
|
70
|
-
return () => {
|
|
71
|
-
observer.disconnect();
|
|
72
|
-
};
|
|
73
|
-
}, [isReady, widgetConfigId, widgetType, trackEvent]);
|
|
51
|
+
// Defer loading by default until the widget is visible
|
|
52
|
+
return isVisible && !!widgetConfigId && !!widgetType;
|
|
53
|
+
}, [isVisible, widgetConfigId, widgetType]);
|
|
74
54
|
|
|
75
55
|
useEffect(() => {
|
|
76
|
-
if (!
|
|
56
|
+
if (!startLoading || hardcopyFetchedRef.current) {
|
|
77
57
|
return;
|
|
78
58
|
}
|
|
59
|
+
hardcopyFetchedRef.current = true;
|
|
79
60
|
getHardcopy({ widgetType, userEvent })
|
|
80
61
|
.then(setHardcopyContent)
|
|
81
62
|
.catch(() => {
|
|
82
63
|
// Silently handle errors - widget will render without hardcopy content
|
|
83
64
|
});
|
|
84
|
-
}, [
|
|
65
|
+
}, [startLoading, widgetType, getHardcopy, userEvent, widgetConfigId]);
|
|
85
66
|
|
|
86
67
|
useEffect(() => {
|
|
68
|
+
if (!startLoading) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
87
71
|
getUiConfig()
|
|
88
72
|
.then(setUiConfig)
|
|
89
73
|
.catch(() => {
|
|
90
74
|
// Silently handle errors - widget will render without ui config
|
|
91
75
|
});
|
|
92
|
-
}, [getUiConfig, widgetConfigId, widgetType]);
|
|
76
|
+
}, [startLoading, getUiConfig, widgetConfigId, widgetType]);
|
|
93
77
|
|
|
94
78
|
useEffect(() => {
|
|
95
|
-
if (!
|
|
79
|
+
if (!startLoading) {
|
|
96
80
|
return;
|
|
97
81
|
}
|
|
98
82
|
|
|
99
83
|
getWidgetConfig({ widgetConfigId, widgetType })
|
|
100
84
|
.then(setWidgetConfig)
|
|
101
|
-
.catch(
|
|
102
|
-
console.error('Error getting widget config', widgetConfigId, widgetType, e);
|
|
85
|
+
.catch(() => {
|
|
103
86
|
// Silently handle errors - widget will render without widget config
|
|
104
87
|
});
|
|
105
|
-
}, [widgetConfigId, widgetType, getWidgetConfig]);
|
|
88
|
+
}, [startLoading, widgetConfigId, widgetType, getWidgetConfig]);
|
|
106
89
|
|
|
107
90
|
return (
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
91
|
+
<span ref={widgetRef}>
|
|
92
|
+
<Widget
|
|
93
|
+
{...props}
|
|
94
|
+
isLoading={isLoading}
|
|
95
|
+
isHardcopyLoading={isHardcopyLoading}
|
|
96
|
+
isPageLoading={isPageLoading}
|
|
97
|
+
isWidgetConfigLoading={isWidgetConfigLoading}
|
|
98
|
+
isUiConfigLoading={isUiConfigLoading}
|
|
99
|
+
uiConfig={uiConfig}
|
|
100
|
+
widgetConfig={widgetConfig}
|
|
101
|
+
hardcopyContent={hardcopyContent}
|
|
102
|
+
observedWidget={widgetRef}
|
|
103
|
+
/>
|
|
104
|
+
</span>
|
|
120
105
|
);
|
|
121
106
|
};
|
|
122
107
|
|
|
@@ -6,9 +6,11 @@ import {
|
|
|
6
6
|
} from '@envive-ai/react-hooks/contexts/typesV3';
|
|
7
7
|
import { useChatToggle } from '@envive-ai/react-hooks/hooks/ChatToggle';
|
|
8
8
|
import { useAtomValue } from 'jotai';
|
|
9
|
+
import { useFeatureFlagService } from '@envive-ai/react-hooks/contexts/featureFlagServiceContext';
|
|
9
10
|
|
|
10
11
|
import {
|
|
11
12
|
ChatElementDisplayLocationV3,
|
|
13
|
+
FeatureGates,
|
|
12
14
|
Suggestion,
|
|
13
15
|
VariantTypeEnum,
|
|
14
16
|
} from '@envive-ai/react-hooks/application/models';
|
|
@@ -89,6 +91,11 @@ const ChatPreviewComparisonWidgetHandler = (props: BaseWidgetProps) => {
|
|
|
89
91
|
|
|
90
92
|
const hideLogo = uiConfig?.lookAndFeel?.hideWidgetLogo;
|
|
91
93
|
|
|
94
|
+
const voiceInputEnabled = uiConfig?.lookAndFeel?.voiceInputEnabled;
|
|
95
|
+
const { featureFlagService } = useFeatureFlagService();
|
|
96
|
+
const isVoiceInputEnabled =
|
|
97
|
+
featureFlagService?.isFeatureGateEnabled(FeatureGates.IsVoiceInputEnabled) || voiceInputEnabled;
|
|
98
|
+
|
|
92
99
|
const handlePromptButtonClick = useCallback(
|
|
93
100
|
(suggestion: Suggestion) => {
|
|
94
101
|
onSuggestionClicked(
|
|
@@ -112,6 +119,29 @@ const ChatPreviewComparisonWidgetHandler = (props: BaseWidgetProps) => {
|
|
|
112
119
|
});
|
|
113
120
|
}, [openChat]);
|
|
114
121
|
|
|
122
|
+
const handleTranscriptionStarted = useCallback(() => {
|
|
123
|
+
trackWidgetInteraction({
|
|
124
|
+
eventName: EnviveMetricsEventName.WidgetInteraction,
|
|
125
|
+
trigger: {
|
|
126
|
+
widget: WidgetInteractionComponent.POST_INTERACTION_COMPARISON,
|
|
127
|
+
widget_interaction: WidgetInteractionType.VOICE_TRANSCRIPTION_STARTED,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}, []);
|
|
131
|
+
|
|
132
|
+
const handleTranscriptionCompleted = useCallback((transcript: string) => {
|
|
133
|
+
trackWidgetInteraction({
|
|
134
|
+
eventName: EnviveMetricsEventName.WidgetInteraction,
|
|
135
|
+
trigger: {
|
|
136
|
+
widget: WidgetInteractionComponent.POST_INTERACTION_COMPARISON,
|
|
137
|
+
widget_interaction: WidgetInteractionType.VOICE_TRANSCRIPTION_COMPLETED,
|
|
138
|
+
widget_interaction_data: {
|
|
139
|
+
transcription: transcript,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}, []);
|
|
144
|
+
|
|
115
145
|
const handleLinkClick = (url: string) => {
|
|
116
146
|
trackWidgetInteraction({
|
|
117
147
|
eventName: EnviveMetricsEventName.WidgetInteraction,
|
|
@@ -156,6 +186,7 @@ const ChatPreviewComparisonWidgetHandler = (props: BaseWidgetProps) => {
|
|
|
156
186
|
textFieldPlaceholderText: hardCopyContent?.textFieldPlaceholderText,
|
|
157
187
|
images: images ?? [],
|
|
158
188
|
logoSrc: logoSrc ?? undefined,
|
|
189
|
+
voiceInputEnabled: isVoiceInputEnabled,
|
|
159
190
|
};
|
|
160
191
|
|
|
161
192
|
const widgetEventProps: ChatPreviewComparisonProps['widgetEventProps'] = {
|
|
@@ -168,6 +199,8 @@ const ChatPreviewComparisonWidgetHandler = (props: BaseWidgetProps) => {
|
|
|
168
199
|
handlePromptButtonTouchEnd: onTouchEnd,
|
|
169
200
|
handleLinkClick,
|
|
170
201
|
handleTextFieldClick,
|
|
202
|
+
onTranscriptionStarted: handleTranscriptionStarted,
|
|
203
|
+
onTranscriptionCompleted: handleTranscriptionCompleted,
|
|
171
204
|
};
|
|
172
205
|
|
|
173
206
|
return (
|
|
@@ -3,9 +3,11 @@ import {
|
|
|
3
3
|
WidgetTypeV3,
|
|
4
4
|
} from '@envive-ai/react-hooks/contexts/typesV3';
|
|
5
5
|
import { useAtomValue } from 'jotai';
|
|
6
|
+
import { useFeatureFlagService } from '@envive-ai/react-hooks/contexts/featureFlagServiceContext';
|
|
7
|
+
import { FeatureGates } from '@envive-ai/react-hooks/application/models';
|
|
6
8
|
|
|
7
|
-
import { ChatPreviewLoading } from '@envive-ai/react-toolkit-v3/ChatPreviewLoading';
|
|
8
9
|
import { chatPreviewLoadingDataAtom } from '@envive-ai/react-hooks/atoms/widget';
|
|
10
|
+
import { ChatPreviewLoading } from '@envive-ai/react-toolkit-v3/ChatPreviewLoading';
|
|
9
11
|
import { BaseWidgetProps } from '../../hocs/withBaseWidget/types';
|
|
10
12
|
import { withBaseWidget } from '../../hocs/withBaseWidget/withBaseWidget';
|
|
11
13
|
import { PromptCarouselWidget } from '../PromptCarouselWidget/PromptCarouselWidget';
|
|
@@ -19,6 +21,7 @@ const ChatPreviewLoadingWidgetHandler = (props: BaseWidgetProps) => {
|
|
|
19
21
|
|
|
20
22
|
const chatPreviewLoadingDataMap = useAtomValue(chatPreviewLoadingDataAtom);
|
|
21
23
|
const chatPreviewLoadingData = chatPreviewLoadingDataMap[widgetConfigId] ?? undefined;
|
|
24
|
+
const { featureFlagService } = useFeatureFlagService();
|
|
22
25
|
|
|
23
26
|
const isCacheEmpty =
|
|
24
27
|
!chatPreviewLoadingData ||
|
|
@@ -48,6 +51,10 @@ const ChatPreviewLoadingWidgetHandler = (props: BaseWidgetProps) => {
|
|
|
48
51
|
const hideTextField =
|
|
49
52
|
chatPreviewLoadingWidgetConfig?.hideTextField ?? chatPreviewLoadingData?.hideTextField;
|
|
50
53
|
|
|
54
|
+
const voiceInputEnabled = uiConfig?.lookAndFeel?.voiceInputEnabled;
|
|
55
|
+
const isVoiceInputEnabled =
|
|
56
|
+
featureFlagService?.isFeatureGateEnabled(FeatureGates.IsVoiceInputEnabled) || voiceInputEnabled;
|
|
57
|
+
|
|
51
58
|
return (
|
|
52
59
|
<ChatPreviewLoading
|
|
53
60
|
id={id}
|
|
@@ -58,6 +65,7 @@ const ChatPreviewLoadingWidgetHandler = (props: BaseWidgetProps) => {
|
|
|
58
65
|
hideLogo={hideLogo}
|
|
59
66
|
logoSrc={logoSrc}
|
|
60
67
|
titleLabel={titleLabel}
|
|
68
|
+
voiceInputEnabled={isVoiceInputEnabled}
|
|
61
69
|
/>
|
|
62
70
|
);
|
|
63
71
|
};
|
|
@@ -3,16 +3,17 @@ import { useSalesAgent } from '@envive-ai/react-hooks/contexts/salesAgentContext
|
|
|
3
3
|
import { ChatPreviewWidgetV3Config, WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
|
|
4
4
|
import { useChatToggle } from '@envive-ai/react-hooks/hooks/ChatToggle';
|
|
5
5
|
import { useAtomValue } from 'jotai';
|
|
6
|
+
import { useFeatureFlagService } from '@envive-ai/react-hooks/contexts/featureFlagServiceContext';
|
|
6
7
|
|
|
7
8
|
import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
|
|
8
9
|
|
|
9
10
|
import {
|
|
10
11
|
ChatElementDisplayLocationV3,
|
|
12
|
+
FeatureGates,
|
|
11
13
|
Suggestion,
|
|
12
14
|
} from '@envive-ai/react-hooks/application/models';
|
|
13
15
|
import {
|
|
14
16
|
EnviveMetricsEventName,
|
|
15
|
-
SpiffyMetricsEventName,
|
|
16
17
|
useAmplitude,
|
|
17
18
|
} from '@envive-ai/react-hooks/contexts/amplitudeContext';
|
|
18
19
|
import { useWidgetInteraction } from '@envive-ai/react-hooks/hooks/WidgetInteraction';
|
|
@@ -23,7 +24,7 @@ import {
|
|
|
23
24
|
import { ChatPreview } from '@envive-ai/react-toolkit-v3/ChatPreview';
|
|
24
25
|
import { ChatPreviewProps } from '@envive-ai/react-toolkit-v3/ChatPreview/types/types';
|
|
25
26
|
import { usePromptCarouselAnalytics } from '@envive-ai/react-toolkit-v3/PromptCarousel';
|
|
26
|
-
import { useCallback,
|
|
27
|
+
import { useCallback, useMemo } from 'react';
|
|
27
28
|
import { BaseWidgetProps } from '../../hocs/withBaseWidget/types';
|
|
28
29
|
import { withBaseWidget } from '../../hocs/withBaseWidget/withBaseWidget';
|
|
29
30
|
import { ChatPreviewLoadingWidgetWithBaseWidget } from '../ChatPreviewLoadingWidget/ChatPreviewLoadingWidget';
|
|
@@ -67,21 +68,15 @@ const ChatPreviewWidgetHandler = (props: BaseWidgetProps) => {
|
|
|
67
68
|
onTouchEnd,
|
|
68
69
|
} = usePromptCarouselAnalytics(WidgetInteractionComponent.CHAT_PREVIEW, text => text);
|
|
69
70
|
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
// TODO: -Add Widget Interaction here
|
|
72
|
-
trackEvent({
|
|
73
|
-
eventName: SpiffyMetricsEventName.ChatComponentVisible,
|
|
74
|
-
eventProps: {
|
|
75
|
-
widget_config_id: widgetConfigId,
|
|
76
|
-
widget_type: WidgetTypeV3.ChatPreviewV3,
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
}, [trackEvent, widgetConfigId]);
|
|
80
|
-
|
|
81
71
|
const logoSrc = uiConfig?.lookAndFeel?.widgetLogoSrc;
|
|
82
72
|
|
|
83
73
|
const hideLogo = uiConfig?.lookAndFeel?.hideWidgetLogo;
|
|
84
74
|
|
|
75
|
+
const voiceInputEnabled = uiConfig?.lookAndFeel?.voiceInputEnabled;
|
|
76
|
+
const { featureFlagService } = useFeatureFlagService();
|
|
77
|
+
const isVoiceInputEnabled =
|
|
78
|
+
featureFlagService?.isFeatureGateEnabled(FeatureGates.IsVoiceInputEnabled) || voiceInputEnabled;
|
|
79
|
+
|
|
85
80
|
const handlePromptButtonClick = useCallback(
|
|
86
81
|
(suggestion: Suggestion) => {
|
|
87
82
|
onSuggestionClicked(suggestion, ChatElementDisplayLocationV3.CHAT_PREVIEW_PROMPT_BUTTON);
|
|
@@ -102,6 +97,29 @@ const ChatPreviewWidgetHandler = (props: BaseWidgetProps) => {
|
|
|
102
97
|
});
|
|
103
98
|
}, [openChat]);
|
|
104
99
|
|
|
100
|
+
const handleTranscriptionStarted = useCallback(() => {
|
|
101
|
+
trackWidgetInteraction({
|
|
102
|
+
eventName: EnviveMetricsEventName.WidgetInteraction,
|
|
103
|
+
trigger: {
|
|
104
|
+
widget: WidgetInteractionComponent.CHAT_PREVIEW,
|
|
105
|
+
widget_interaction: WidgetInteractionType.VOICE_TRANSCRIPTION_STARTED,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}, []);
|
|
109
|
+
|
|
110
|
+
const handleTranscriptionCompleted = useCallback((transcript: string) => {
|
|
111
|
+
trackWidgetInteraction({
|
|
112
|
+
eventName: EnviveMetricsEventName.WidgetInteraction,
|
|
113
|
+
trigger: {
|
|
114
|
+
widget: WidgetInteractionComponent.CHAT_PREVIEW,
|
|
115
|
+
widget_interaction: WidgetInteractionType.VOICE_TRANSCRIPTION_COMPLETED,
|
|
116
|
+
widget_interaction_data: {
|
|
117
|
+
transcription: transcript,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}, []);
|
|
122
|
+
|
|
105
123
|
const handleLinkClick = (url: string) => {
|
|
106
124
|
trackWidgetInteraction({
|
|
107
125
|
eventName: EnviveMetricsEventName.WidgetInteraction,
|
|
@@ -140,6 +158,7 @@ const ChatPreviewWidgetHandler = (props: BaseWidgetProps) => {
|
|
|
140
158
|
promptButtons: hardCopyContent?.promptButtons,
|
|
141
159
|
textFieldPlaceholderText: hardCopyContent?.textFieldPlaceholderText,
|
|
142
160
|
logoSrc: logoSrc ?? undefined,
|
|
161
|
+
voiceInputEnabled: isVoiceInputEnabled,
|
|
143
162
|
};
|
|
144
163
|
|
|
145
164
|
const widgetEventProps: ChatPreviewProps['widgetEventProps'] = {
|
|
@@ -152,6 +171,8 @@ const ChatPreviewWidgetHandler = (props: BaseWidgetProps) => {
|
|
|
152
171
|
handlePromptButtonTouchEnd: onTouchEnd,
|
|
153
172
|
handleLinkClick,
|
|
154
173
|
handleTextFieldClick,
|
|
174
|
+
onTranscriptionStarted: handleTranscriptionStarted,
|
|
175
|
+
onTranscriptionCompleted: handleTranscriptionCompleted,
|
|
155
176
|
};
|
|
156
177
|
|
|
157
178
|
return (
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { render, waitFor } from '@testing-library/react';
|
|
2
|
-
import { SpiffyMetricsEventName } from '@envive-ai/react-hooks/contexts/amplitudeContext';
|
|
3
2
|
import { WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
|
|
4
3
|
import { UserEvent, UserEventCategory } from '@spiffy-ai/commerce-api-client';
|
|
5
4
|
import { ChatPreviewWidget } from '../ChatPreviewWidget';
|
|
6
5
|
|
|
7
6
|
const mockTrackEvent = vi.fn();
|
|
8
7
|
const mockGetHardcopy = vi.fn();
|
|
8
|
+
const mockUseTrackComponentVisibleEvent = vi.fn();
|
|
9
9
|
|
|
10
10
|
vi.mock('@envive-ai/react-toolkit-v3/ChatPreview', () => ({
|
|
11
11
|
ChatPreview: () => <div data-testid="chat-preview-mock">Chat Preview</div>,
|
|
@@ -22,6 +22,10 @@ vi.mock('@envive-ai/react-hooks/contexts/amplitudeContext', () => ({
|
|
|
22
22
|
},
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
|
+
vi.mock('@envive-ai/react-hooks/hooks/TrackComponentVisibleEvent', () => ({
|
|
26
|
+
useTrackComponentVisibleEvent: (...args: unknown[]) => mockUseTrackComponentVisibleEvent(...args),
|
|
27
|
+
}));
|
|
28
|
+
|
|
25
29
|
vi.mock('@envive-ai/react-hooks/contexts/hardcopyContext', () => ({
|
|
26
30
|
useHardcopy: () => ({
|
|
27
31
|
getHardcopy: mockGetHardcopy,
|
|
@@ -100,13 +104,15 @@ describe('ChatPreviewWidget analytics', () => {
|
|
|
100
104
|
|
|
101
105
|
await waitFor(
|
|
102
106
|
() => {
|
|
103
|
-
expect(
|
|
104
|
-
|
|
105
|
-
|
|
107
|
+
expect(mockUseTrackComponentVisibleEvent).toHaveBeenCalledWith(
|
|
108
|
+
expect.any(Object),
|
|
109
|
+
{
|
|
106
110
|
widget_config_id: 'test-config-1',
|
|
107
111
|
widget_type: WidgetTypeV3.ChatPreviewV3,
|
|
108
112
|
},
|
|
109
|
-
|
|
113
|
+
'0px',
|
|
114
|
+
true,
|
|
115
|
+
);
|
|
110
116
|
},
|
|
111
117
|
{ timeout: 3000 },
|
|
112
118
|
);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ChatElementDisplayLocationV3,
|
|
3
|
+
FeatureGates,
|
|
4
|
+
} from '@envive-ai/react-hooks/application/models';
|
|
2
5
|
import {
|
|
3
6
|
EnviveMetricsEventName,
|
|
4
|
-
SpiffyMetricsEventName,
|
|
5
7
|
useAmplitude,
|
|
6
8
|
} from '@envive-ai/react-hooks/contexts/amplitudeContext';
|
|
7
9
|
import { useSalesAgent } from '@envive-ai/react-hooks/contexts/salesAgentContext';
|
|
@@ -11,7 +13,9 @@ import {
|
|
|
11
13
|
WidgetTypeV3,
|
|
12
14
|
} from '@envive-ai/react-hooks/contexts/typesV3';
|
|
13
15
|
import { useChatToggle } from '@envive-ai/react-hooks/hooks/ChatToggle';
|
|
16
|
+
import { useTrackComponentVisibleEvent } from '@envive-ai/react-hooks/hooks/TrackComponentVisibleEvent';
|
|
14
17
|
import { useWidgetInteraction } from '@envive-ai/react-hooks/hooks/WidgetInteraction';
|
|
18
|
+
import { useFeatureFlagService } from '@envive-ai/react-hooks/contexts/featureFlagServiceContext';
|
|
15
19
|
import {
|
|
16
20
|
Collapse,
|
|
17
21
|
WidgetInteractionComponent,
|
|
@@ -92,6 +96,20 @@ const FloatingChatWidgetHandler = (props: FloatingChatWidgetHandlerProps) => {
|
|
|
92
96
|
(customerServiceIntegration?.provider as CustomerServiceType) ??
|
|
93
97
|
CustomerServiceType.unsupported;
|
|
94
98
|
|
|
99
|
+
const { featureFlagService } = useFeatureFlagService();
|
|
100
|
+
const originalVoiceInputEnabled = uiConfig?.lookAndFeel?.voiceInputEnabled;
|
|
101
|
+
const isVoiceInputEnabled =
|
|
102
|
+
featureFlagService?.isFeatureGateEnabled(FeatureGates.IsVoiceInputEnabled) ||
|
|
103
|
+
originalVoiceInputEnabled;
|
|
104
|
+
|
|
105
|
+
const modifiedLookAndFeelConfig = useMemo(
|
|
106
|
+
() => ({
|
|
107
|
+
...uiConfig?.lookAndFeel,
|
|
108
|
+
voiceInputEnabled: isVoiceInputEnabled,
|
|
109
|
+
}),
|
|
110
|
+
[uiConfig?.lookAndFeel, isVoiceInputEnabled],
|
|
111
|
+
);
|
|
112
|
+
|
|
95
113
|
// TODO: Get hardcopy content
|
|
96
114
|
const { isOpen, openChat, closeChat, onHover } = useChatToggle();
|
|
97
115
|
|
|
@@ -139,21 +157,17 @@ const FloatingChatWidgetHandler = (props: FloatingChatWidgetHandlerProps) => {
|
|
|
139
157
|
|
|
140
158
|
useAutoPopup({ autoPopupConfig: floatingChat?.autoPopupConfig });
|
|
141
159
|
|
|
142
|
-
const {
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
});
|
|
154
|
-
hasTrackedEvent.current = true;
|
|
155
|
-
}
|
|
156
|
-
}, [trackEvent, buttonShouldRender]);
|
|
160
|
+
const { isReady } = useAmplitude();
|
|
161
|
+
const floatingButtonVisibilityRef = useRef<HTMLDivElement>(null);
|
|
162
|
+
useTrackComponentVisibleEvent(
|
|
163
|
+
floatingButtonVisibilityRef,
|
|
164
|
+
{
|
|
165
|
+
widget_config_id: 'floating-button',
|
|
166
|
+
widget_type: WidgetTypeV3.FloatingButtonV3,
|
|
167
|
+
},
|
|
168
|
+
'0px',
|
|
169
|
+
isReady && buttonShouldRender,
|
|
170
|
+
);
|
|
157
171
|
|
|
158
172
|
return (
|
|
159
173
|
<CXButtonProvider
|
|
@@ -190,7 +204,7 @@ const FloatingChatWidgetHandler = (props: FloatingChatWidgetHandlerProps) => {
|
|
|
190
204
|
salesAgentData={salesAgentData}
|
|
191
205
|
hardcopyContent={hardcopyContent}
|
|
192
206
|
floatingChatConfig={uiConfig?.floatingChat ?? ({} as FloatingChatConfig)}
|
|
193
|
-
lookAndFeelConfig={
|
|
207
|
+
lookAndFeelConfig={modifiedLookAndFeelConfig ?? ({} as LookAndFeelConfig)}
|
|
194
208
|
isCXButtonSwitchEnabled={!!isSwitchEnabled?.()}
|
|
195
209
|
isFloatingChatOpen={effectiveIsOpen}
|
|
196
210
|
onToggleCXButton={toggle}
|
|
@@ -222,24 +236,26 @@ const FloatingChatWidgetHandler = (props: FloatingChatWidgetHandlerProps) => {
|
|
|
222
236
|
|
|
223
237
|
{/* Render button when preview button only OR when chat is closed */}
|
|
224
238
|
{buttonShouldRender && (
|
|
225
|
-
<
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
239
|
+
<div ref={floatingButtonVisibilityRef}>
|
|
240
|
+
<FloatingButton
|
|
241
|
+
id={FLOATING_BUTTON_ID}
|
|
242
|
+
variant={floatingButton?.style as FloatingButtonVariant}
|
|
243
|
+
mode={floatingButton?.mode as FloatingButtonMode}
|
|
244
|
+
backgroundColor={floatingButton?.backgroundColor as FloatingButtonBackgroundColor}
|
|
245
|
+
onClick={
|
|
246
|
+
previewButtonOnly
|
|
247
|
+
? () => {}
|
|
248
|
+
: () => openChat(ChatElementDisplayLocationV3.FLOATING_BUTTON)
|
|
249
|
+
}
|
|
250
|
+
onMouseOver={onHover}
|
|
251
|
+
customIcon={floatingButton?.iconSVGSrc}
|
|
252
|
+
show={floatingButton?.showOption as FloatingButtonShow}
|
|
253
|
+
location={floatingButton?.position as FloatingButtonLocation}
|
|
254
|
+
hasInteractionHappened={userHasInteractedValue}
|
|
255
|
+
ariaLabel="Open chat"
|
|
256
|
+
previewMode={!!previewButtonOnly}
|
|
257
|
+
/>
|
|
258
|
+
</div>
|
|
243
259
|
)}
|
|
244
260
|
</>
|
|
245
261
|
)}
|
|
@@ -247,8 +263,10 @@ const FloatingChatWidgetHandler = (props: FloatingChatWidgetHandlerProps) => {
|
|
|
247
263
|
);
|
|
248
264
|
};
|
|
249
265
|
|
|
250
|
-
const FloatingChatWidgetWithBaseWidget =
|
|
251
|
-
|
|
266
|
+
const FloatingChatWidgetWithBaseWidget = withBaseWidget<FloatingChatWidgetHandlerProps>(
|
|
267
|
+
FloatingChatWidgetHandler,
|
|
268
|
+
{ deferLoading: false },
|
|
269
|
+
);
|
|
252
270
|
|
|
253
271
|
export interface FloatingChatWidgetProps {
|
|
254
272
|
previewButtonOnly?: boolean;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { render, waitFor } from '@testing-library/react';
|
|
2
|
-
import { SpiffyMetricsEventName } from '@envive-ai/react-hooks/contexts/amplitudeContext';
|
|
3
2
|
import { WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
|
|
4
3
|
import { UserEvent, UserEventCategory } from '@spiffy-ai/commerce-api-client';
|
|
5
4
|
import { FloatingChatWidget } from '../FloatingChatWidget';
|
|
6
5
|
|
|
7
6
|
const mockTrackEvent = vi.fn();
|
|
8
7
|
const mockGetHardcopy = vi.fn();
|
|
8
|
+
const mockUseTrackComponentVisibleEvent = vi.fn();
|
|
9
9
|
|
|
10
10
|
vi.mock('@envive-ai/react-toolkit-v3/FloatingButton', () => ({
|
|
11
11
|
FloatingButton: () => <div data-testid="floating-button">Floating Button</div>,
|
|
@@ -22,6 +22,10 @@ vi.mock('@envive-ai/react-hooks/contexts/amplitudeContext', () => ({
|
|
|
22
22
|
},
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
|
+
vi.mock('@envive-ai/react-hooks/hooks/TrackComponentVisibleEvent', () => ({
|
|
26
|
+
useTrackComponentVisibleEvent: (...args: unknown[]) => mockUseTrackComponentVisibleEvent(...args),
|
|
27
|
+
}));
|
|
28
|
+
|
|
25
29
|
vi.mock('@envive-ai/react-hooks/contexts/hardcopyContext', () => ({
|
|
26
30
|
useHardcopy: () => ({
|
|
27
31
|
getHardcopy: mockGetHardcopy,
|
|
@@ -105,13 +109,15 @@ describe('FloatingChatWidget analytics', () => {
|
|
|
105
109
|
|
|
106
110
|
await waitFor(
|
|
107
111
|
() => {
|
|
108
|
-
expect(
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
expect(mockUseTrackComponentVisibleEvent).toHaveBeenCalledWith(
|
|
113
|
+
expect.any(Object),
|
|
114
|
+
{
|
|
111
115
|
widget_config_id: 'floating-button',
|
|
112
116
|
widget_type: WidgetTypeV3.FloatingButtonV3,
|
|
113
117
|
},
|
|
114
|
-
|
|
118
|
+
'0px',
|
|
119
|
+
true,
|
|
120
|
+
);
|
|
115
121
|
},
|
|
116
122
|
{ timeout: 3000 },
|
|
117
123
|
);
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
import { FullPageSalesAgent } from '@envive-ai/react-toolkit-v3/FullPageSalesAgent';
|
|
8
8
|
import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
|
|
9
9
|
import { BaseWidgetProps, withBaseWidget } from 'src/hocs/withBaseWidget';
|
|
10
|
+
import { useFeatureFlagService } from '@envive-ai/react-hooks/contexts/featureFlagServiceContext';
|
|
11
|
+
import { FeatureGates } from '@envive-ai/react-hooks/application/models';
|
|
10
12
|
|
|
11
13
|
const FullPageSalesAgentHandler = ({
|
|
12
14
|
uiConfig,
|
|
@@ -15,6 +17,11 @@ const FullPageSalesAgentHandler = ({
|
|
|
15
17
|
}: BaseWidgetProps) => {
|
|
16
18
|
const { headerContainer, autoHeight } = (widgetConfig as FullPageSalesAgentWidgetV3Config) || {};
|
|
17
19
|
|
|
20
|
+
const voiceInputEnabled = uiConfig?.lookAndFeel?.voiceInputEnabled;
|
|
21
|
+
const { featureFlagService } = useFeatureFlagService();
|
|
22
|
+
const isVoiceInputEnabled =
|
|
23
|
+
featureFlagService?.isFeatureGateEnabled(FeatureGates.IsVoiceInputEnabled) || voiceInputEnabled;
|
|
24
|
+
|
|
18
25
|
return (
|
|
19
26
|
<FullPageSalesAgent
|
|
20
27
|
theme={Theme.STANDARD}
|
|
@@ -23,6 +30,7 @@ const FullPageSalesAgentHandler = ({
|
|
|
23
30
|
hardcopyContent={hardcopyContent}
|
|
24
31
|
headerContainer={headerContainer}
|
|
25
32
|
autoHeight={autoHeight}
|
|
33
|
+
voiceInputEnabled={isVoiceInputEnabled}
|
|
26
34
|
/>
|
|
27
35
|
);
|
|
28
36
|
};
|