@@ -4,7 +4,7 @@ import { singleToArray } from 'botframework-webchat-core';
44import classNames from 'classnames' ;
55import MarkdownIt from 'markdown-it' ;
66import PropTypes from 'prop-types' ;
7- import React , { memo , useCallback , useMemo , useRef , useState } from 'react' ;
7+ import React , { forwardRef , Fragment , memo , useCallback , useImperativeHandle , useMemo , useRef , useState } from 'react' ;
88import { Composer as SayComposer } from 'react-say' ;
99import createStyleSet from './Styles/createStyleSet' ;
1010
@@ -18,6 +18,7 @@ import {
1818import UITracker from './hooks/internal/UITracker' ;
1919import WebChatUIContext from './hooks/internal/WebChatUIContext' ;
2020import useStyleSet from './hooks/useStyleSet' ;
21+ import useFocus from './hooks/useFocus' ;
2122import createDefaultActivityMiddleware from './Middleware/Activity/createCoreMiddleware' ;
2223import createDefaultActivityStatusMiddleware from './Middleware/ActivityStatus/createCoreMiddleware' ;
2324import createDefaultAttachmentForScreenReaderMiddleware from './Middleware/AttachmentForScreenReader/createCoreMiddleware' ;
@@ -40,8 +41,28 @@ import type { ContextOf } from './types/ContextOf';
4041import { type FocusSendBoxInit } from './types/internal/FocusSendBoxInit' ;
4142import { type FocusTranscriptInit } from './types/internal/FocusTranscriptInit' ;
4243
44+ export type ComposerRef = {
45+ focusSendBoxInput : ( ) => Promise < void > ;
46+ } ;
47+
4348const { useGetActivityByKey, useReferenceGrammarID, useStyleOptions } = hooks ;
4449
50+ const ComposerWithRef = forwardRef < ComposerRef , { readonly children : ReactNode } > ( ( { children } , ref ) => {
51+ const focus = useFocus ( ) ;
52+
53+ useImperativeHandle (
54+ ref ,
55+ ( ) => ( {
56+ focusSendBoxInput : async ( ) => {
57+ await focus ( 'sendBox' ) ;
58+ }
59+ } ) ,
60+ [ focus ]
61+ ) ;
62+
63+ return < Fragment > { children } </ Fragment > ;
64+ } ) ;
65+
4566const node_env = process . env . node_env || process . env . NODE_ENV ;
4667
4768const emotionPool = { } ;
@@ -282,109 +303,116 @@ ComposerCore.propTypes = {
282303
283304type ComposerProps = APIComposerProps & ComposerCoreProps ;
284305
285- const Composer : FC < ComposerProps > = ( {
286- activityMiddleware,
287- activityStatusMiddleware,
288- attachmentForScreenReaderMiddleware,
289- attachmentMiddleware,
290- avatarMiddleware,
291- cardActionMiddleware,
292- children,
293- extraStyleSet,
294- renderMarkdown,
295- scrollToEndButtonMiddleware,
296- styleSet,
297- suggestedActionsAccessKey,
298- toastMiddleware,
299- typingIndicatorMiddleware,
300- webSpeechPonyfillFactory,
301- ...composerProps
302- } ) => {
303- const { nonce, onTelemetry } = composerProps ;
304-
305- const patchedActivityMiddleware = useMemo (
306- ( ) => [ ...singleToArray ( activityMiddleware ) , ...createDefaultActivityMiddleware ( ) ] ,
307- [ activityMiddleware ]
308- ) ;
309-
310- const patchedActivityStatusMiddleware = useMemo (
311- ( ) => [ ...singleToArray ( activityStatusMiddleware ) , ...createDefaultActivityStatusMiddleware ( ) ] ,
312- [ activityStatusMiddleware ]
313- ) ;
314-
315- const patchedAttachmentForScreenReaderMiddleware = useMemo (
316- ( ) => [
317- ...singleToArray ( attachmentForScreenReaderMiddleware ) ,
318- ...createDefaultAttachmentForScreenReaderMiddleware ( )
319- ] ,
320- [ attachmentForScreenReaderMiddleware ]
321- ) ;
322-
323- const patchedAttachmentMiddleware = useMemo (
324- ( ) => [ ...singleToArray ( attachmentMiddleware ) , ...createDefaultAttachmentMiddleware ( ) ] ,
325- [ attachmentMiddleware ]
326- ) ;
327-
328- const patchedAvatarMiddleware = useMemo (
329- ( ) => [ ...singleToArray ( avatarMiddleware ) , ...createDefaultAvatarMiddleware ( ) ] ,
330- [ avatarMiddleware ]
331- ) ;
332-
333- const patchedCardActionMiddleware = useMemo (
334- ( ) => [ ...singleToArray ( cardActionMiddleware ) , ...createDefaultCardActionMiddleware ( ) ] ,
335- [ cardActionMiddleware ]
336- ) ;
337-
338- const patchedToastMiddleware = useMemo (
339- ( ) => [ ...singleToArray ( toastMiddleware ) , ...createDefaultToastMiddleware ( ) ] ,
340- [ toastMiddleware ]
341- ) ;
342-
343- const patchedTypingIndicatorMiddleware = useMemo (
344- ( ) => [ ...singleToArray ( typingIndicatorMiddleware ) , ...createDefaultTypingIndicatorMiddleware ( ) ] ,
345- [ typingIndicatorMiddleware ]
346- ) ;
347-
348- const defaultScrollToEndButtonMiddleware = useMemo ( ( ) => createDefaultScrollToEndButtonMiddleware ( ) , [ ] ) ;
349-
350- const patchedScrollToEndButtonMiddleware = useMemo (
351- ( ) => [ ...singleToArray ( scrollToEndButtonMiddleware ) , ...defaultScrollToEndButtonMiddleware ] ,
352- [ defaultScrollToEndButtonMiddleware , scrollToEndButtonMiddleware ]
353- ) ;
354-
355- return (
356- < APIComposer
357- activityMiddleware = { patchedActivityMiddleware }
358- activityStatusMiddleware = { patchedActivityStatusMiddleware }
359- attachmentForScreenReaderMiddleware = { patchedAttachmentForScreenReaderMiddleware }
360- attachmentMiddleware = { patchedAttachmentMiddleware }
361- avatarMiddleware = { patchedAvatarMiddleware }
362- cardActionMiddleware = { patchedCardActionMiddleware }
363- downscaleImageToDataURL = { downscaleImageToDataURL }
364- // Under dev server of create-react-app, "NODE_ENV" will be set to "development".
365- internalErrorBoxClass = { node_env === 'development' ? ErrorBox : undefined }
366- nonce = { nonce }
367- scrollToEndButtonMiddleware = { patchedScrollToEndButtonMiddleware }
368- toastMiddleware = { patchedToastMiddleware }
369- typingIndicatorMiddleware = { patchedTypingIndicatorMiddleware }
370- { ...composerProps }
371- >
372- < ActivityTreeComposer >
373- < ComposerCore
374- extraStyleSet = { extraStyleSet }
375- nonce = { nonce }
376- renderMarkdown = { renderMarkdown }
377- styleSet = { styleSet }
378- suggestedActionsAccessKey = { suggestedActionsAccessKey }
379- webSpeechPonyfillFactory = { webSpeechPonyfillFactory }
380- >
381- { children }
382- { onTelemetry && < UITracker /> }
383- </ ComposerCore >
384- </ ActivityTreeComposer >
385- </ APIComposer >
386- ) ;
387- } ;
306+ const Composer = forwardRef < ComposerRef , ComposerProps > (
307+ (
308+ {
309+ activityMiddleware,
310+ activityStatusMiddleware,
311+ attachmentForScreenReaderMiddleware,
312+ attachmentMiddleware,
313+ avatarMiddleware,
314+ cardActionMiddleware,
315+ children,
316+ extraStyleSet,
317+ renderMarkdown,
318+ scrollToEndButtonMiddleware,
319+ styleSet,
320+ suggestedActionsAccessKey,
321+ toastMiddleware,
322+ typingIndicatorMiddleware,
323+ webSpeechPonyfillFactory,
324+ ...composerProps
325+ } ,
326+ ref
327+ ) => {
328+ const { nonce, onTelemetry } = composerProps ;
329+
330+ const patchedActivityMiddleware = useMemo (
331+ ( ) => [ ...singleToArray ( activityMiddleware ) , ...createDefaultActivityMiddleware ( ) ] ,
332+ [ activityMiddleware ]
333+ ) ;
334+
335+ const patchedActivityStatusMiddleware = useMemo (
336+ ( ) => [ ...singleToArray ( activityStatusMiddleware ) , ...createDefaultActivityStatusMiddleware ( ) ] ,
337+ [ activityStatusMiddleware ]
338+ ) ;
339+
340+ const patchedAttachmentForScreenReaderMiddleware = useMemo (
341+ ( ) => [
342+ ...singleToArray ( attachmentForScreenReaderMiddleware ) ,
343+ ...createDefaultAttachmentForScreenReaderMiddleware ( )
344+ ] ,
345+ [ attachmentForScreenReaderMiddleware ]
346+ ) ;
347+
348+ const patchedAttachmentMiddleware = useMemo (
349+ ( ) => [ ...singleToArray ( attachmentMiddleware ) , ...createDefaultAttachmentMiddleware ( ) ] ,
350+ [ attachmentMiddleware ]
351+ ) ;
352+
353+ const patchedAvatarMiddleware = useMemo (
354+ ( ) => [ ...singleToArray ( avatarMiddleware ) , ...createDefaultAvatarMiddleware ( ) ] ,
355+ [ avatarMiddleware ]
356+ ) ;
357+
358+ const patchedCardActionMiddleware = useMemo (
359+ ( ) => [ ...singleToArray ( cardActionMiddleware ) , ...createDefaultCardActionMiddleware ( ) ] ,
360+ [ cardActionMiddleware ]
361+ ) ;
362+
363+ const patchedToastMiddleware = useMemo (
364+ ( ) => [ ...singleToArray ( toastMiddleware ) , ...createDefaultToastMiddleware ( ) ] ,
365+ [ toastMiddleware ]
366+ ) ;
367+
368+ const patchedTypingIndicatorMiddleware = useMemo (
369+ ( ) => [ ...singleToArray ( typingIndicatorMiddleware ) , ...createDefaultTypingIndicatorMiddleware ( ) ] ,
370+ [ typingIndicatorMiddleware ]
371+ ) ;
372+
373+ const defaultScrollToEndButtonMiddleware = useMemo ( ( ) => createDefaultScrollToEndButtonMiddleware ( ) , [ ] ) ;
374+
375+ const patchedScrollToEndButtonMiddleware = useMemo (
376+ ( ) => [ ...singleToArray ( scrollToEndButtonMiddleware ) , ...defaultScrollToEndButtonMiddleware ] ,
377+ [ defaultScrollToEndButtonMiddleware , scrollToEndButtonMiddleware ]
378+ ) ;
379+
380+ return (
381+ < APIComposer
382+ activityMiddleware = { patchedActivityMiddleware }
383+ activityStatusMiddleware = { patchedActivityStatusMiddleware }
384+ attachmentForScreenReaderMiddleware = { patchedAttachmentForScreenReaderMiddleware }
385+ attachmentMiddleware = { patchedAttachmentMiddleware }
386+ avatarMiddleware = { patchedAvatarMiddleware }
387+ cardActionMiddleware = { patchedCardActionMiddleware }
388+ downscaleImageToDataURL = { downscaleImageToDataURL }
389+ // Under dev server of create-react-app, "NODE_ENV" will be set to "development".
390+ internalErrorBoxClass = { node_env === 'development' ? ErrorBox : undefined }
391+ nonce = { nonce }
392+ scrollToEndButtonMiddleware = { patchedScrollToEndButtonMiddleware }
393+ toastMiddleware = { patchedToastMiddleware }
394+ typingIndicatorMiddleware = { patchedTypingIndicatorMiddleware }
395+ { ...composerProps }
396+ >
397+ < ActivityTreeComposer >
398+ < ComposerCore
399+ extraStyleSet = { extraStyleSet }
400+ nonce = { nonce }
401+ renderMarkdown = { renderMarkdown }
402+ styleSet = { styleSet }
403+ suggestedActionsAccessKey = { suggestedActionsAccessKey }
404+ webSpeechPonyfillFactory = { webSpeechPonyfillFactory }
405+ >
406+ < ComposerWithRef ref = { ref } >
407+ { children }
408+ { onTelemetry && < UITracker /> }
409+ </ ComposerWithRef >
410+ </ ComposerCore >
411+ </ ActivityTreeComposer >
412+ </ APIComposer >
413+ ) ;
414+ }
415+ ) ;
388416
389417Composer . defaultProps = {
390418 ...APIComposer . defaultProps ,
0 commit comments