-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Expand file tree
/
Copy pathFluentThemeProvider.tsx
More file actions
143 lines (128 loc) · 5.21 KB
/
FluentThemeProvider.tsx
File metadata and controls
143 lines (128 loc) · 5.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* eslint-disable prefer-arrow-callback */
import { reactNode, validateProps } from '@msinternal/botframework-webchat-react-valibot';
import { ThemeProvider } from 'botframework-webchat/component';
import {
createActivityBorderMiddleware,
createActivityGroupingMiddleware,
DecoratorComposer,
WebChatDecorator,
type DecoratorMiddleware
} from 'botframework-webchat/decorator';
import {
isVoiceTranscriptActivity,
type ActivityMiddleware,
type ActivityStatusMiddleware,
type TypingIndicatorMiddleware
} from 'botframework-webchat/internal';
import React, { memo, useMemo } from 'react';
import { custom, object, optional, pipe, readonly, string, type InferInput } from 'valibot';
import VoiceTranscriptActivityStatus from '../components/activityStatus/VoiceTranscriptActivityStatus';
import ActivityLoader from '../components/activity/ActivityLoader';
import PartGroupDecorator from '../components/activity/PartGroupingDecorator';
import AssetComposer from '../components/assets/AssetComposer';
import { isLinerMessageActivity, LinerMessageActivity } from '../components/linerActivity';
import { isPreChatMessageActivity, PreChatMessageActivity } from '../components/preChatActivity';
import { PrimarySendBox } from '../components/sendBox';
import { TelephoneKeypadProvider } from '../components/telephoneKeypad';
import { WebChatTheme } from '../components/theme';
import SlidingDotsTypingIndicator from '../components/typingIndicator/SlidingDotsTypingIndicator';
import FluentThemeStylesheet from '../stylesheet/FluentThemeStylesheet';
import VariantComposer, { variantNameSchema } from './VariantComposer';
const fluentThemeProviderPropsSchema = pipe(
object({
children: optional(reactNode()),
nonce: optional(string()),
stylesRoot: optional(custom<Node>(value => value instanceof Node)),
variant: optional(variantNameSchema)
}),
readonly()
);
type FluentThemeProviderProps = InferInput<typeof fluentThemeProviderPropsSchema>;
const activityMiddleware: readonly ActivityMiddleware[] = Object.freeze([
() =>
next =>
(...args) => {
const activity = args[0]?.activity;
// TODO: Should show pre-chat only when it is the very first message in the chat history.
if (isPreChatMessageActivity(activity)) {
return () => <PreChatMessageActivity activity={activity} />;
}
if (isLinerMessageActivity(activity)) {
return () => <LinerMessageActivity activity={activity} />;
}
return next(...args);
}
]);
const sendBoxMiddleware = [() => () => () => PrimarySendBox];
const decoratorMiddleware: readonly DecoratorMiddleware[] = Object.freeze([
createActivityGroupingMiddleware(next => request => {
if (request.groupingName === 'part') {
return PartGroupDecorator;
}
return next(request);
}),
createActivityBorderMiddleware(function FluentBorderLoader({ request, Next, ...props }) {
return (
<ActivityLoader showLoader={props.showLoader ?? request.livestreamingState === 'preparing'}>
<Next {...props} showLoader={false} />
</ActivityLoader>
);
})
]);
const activityStatusMiddleware: readonly ActivityStatusMiddleware[] = Object.freeze([
() =>
next =>
({ activity, ...args }) => {
if (isVoiceTranscriptActivity(activity)) {
return <VoiceTranscriptActivityStatus activity={activity} />;
}
return next({ activity, ...args });
}
]);
const typingIndicatorMiddleware: readonly TypingIndicatorMiddleware[] = Object.freeze([
() =>
next =>
(...args) =>
args[0].visible ? <SlidingDotsTypingIndicator /> : next(...args)
] satisfies TypingIndicatorMiddleware[]);
function FluentThemeProvider(props: FluentThemeProviderProps) {
// validateProps() does not fill in optional in production mode.
const { children, nonce, stylesRoot, variant = 'fluent' } = validateProps(fluentThemeProviderPropsSchema, props);
const fluentStyleOptions = useMemo(
() =>
Object.freeze({
...(variant === 'copilot' && { partGroupDefaultOpen: false, referenceListDefaultOpen: false }),
feedbackActionsPlacement: 'activity-actions',
stylesRoot
}),
[stylesRoot, variant]
);
return (
<VariantComposer variant={variant}>
<WebChatTheme>
<TelephoneKeypadProvider>
<ThemeProvider
activityMiddleware={activityMiddleware}
activityStatusMiddleware={activityStatusMiddleware}
sendBoxMiddleware={sendBoxMiddleware}
styleOptions={fluentStyleOptions}
typingIndicatorMiddleware={typingIndicatorMiddleware}
>
<FluentThemeStylesheet nonce={nonce} />
<AssetComposer>
{/*
<Composer> is not set up yet, we have no place to send nonce.
This is temporal, until we decided to fold <WebChatDecorator> back into <Composer>.
*/}
<WebChatDecorator nonce={nonce}>
<DecoratorComposer middleware={decoratorMiddleware}>{children}</DecoratorComposer>
</WebChatDecorator>
</AssetComposer>
</ThemeProvider>
</TelephoneKeypadProvider>
</WebChatTheme>
</VariantComposer>
);
}
export default memo(FluentThemeProvider);
export { type FluentThemeProviderProps };