/* eslint-disable prefer-arrow-callback */ import { reactNode, validateProps } from '@msinternal/botframework-webchat-react-valibot'; import { ThemeProvider } from 'botframework-webchat/component.js'; import { createActivityBorderMiddleware, createActivityGroupingMiddleware, DecoratorComposer, WebChatDecorator, type DecoratorMiddleware } from 'botframework-webchat/decorator.js'; import { isVoiceTranscriptActivity, type ActivityMiddleware, type ActivityStatusMiddleware, type TypingIndicatorMiddleware } from 'botframework-webchat/internal.js'; 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(value => value instanceof Node)), variant: optional(variantNameSchema) }), readonly() ); type FluentThemeProviderProps = InferInput; 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 () => ; } if (isLinerMessageActivity(activity)) { return () => ; } 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 ( ); }) ]); const activityStatusMiddleware: readonly ActivityStatusMiddleware[] = Object.freeze([ () => next => ({ activity, ...args }) => { if (isVoiceTranscriptActivity(activity)) { return ; } return next({ activity, ...args }); } ]); const typingIndicatorMiddleware: readonly TypingIndicatorMiddleware[] = Object.freeze([ () => next => (...args) => args[0].visible ? : 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 ( {/* is not set up yet, we have no place to send nonce. This is temporal, until we decided to fold back into . */} {children} ); } export default memo(FluentThemeProvider); export { type FluentThemeProviderProps };