@@ -4,7 +4,7 @@ import { singleToArray } from 'botframework-webchat-core';
4
4
import classNames from 'classnames' ;
5
5
import MarkdownIt from 'markdown-it' ;
6
6
import 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' ;
8
8
import { Composer as SayComposer } from 'react-say' ;
9
9
import createStyleSet from './Styles/createStyleSet' ;
10
10
@@ -18,6 +18,7 @@ import {
18
18
import UITracker from './hooks/internal/UITracker' ;
19
19
import WebChatUIContext from './hooks/internal/WebChatUIContext' ;
20
20
import useStyleSet from './hooks/useStyleSet' ;
21
+ import useFocus from './hooks/useFocus' ;
21
22
import createDefaultActivityMiddleware from './Middleware/Activity/createCoreMiddleware' ;
22
23
import createDefaultActivityStatusMiddleware from './Middleware/ActivityStatus/createCoreMiddleware' ;
23
24
import createDefaultAttachmentForScreenReaderMiddleware from './Middleware/AttachmentForScreenReader/createCoreMiddleware' ;
@@ -40,8 +41,28 @@ import type { ContextOf } from './types/ContextOf';
40
41
import { type FocusSendBoxInit } from './types/internal/FocusSendBoxInit' ;
41
42
import { type FocusTranscriptInit } from './types/internal/FocusTranscriptInit' ;
42
43
44
+ export type ComposerRef = {
45
+ focusSendBoxInput : ( ) => Promise < void > ;
46
+ } ;
47
+
43
48
const { useGetActivityByKey, useReferenceGrammarID, useStyleOptions } = hooks ;
44
49
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
+
45
66
const node_env = process . env . node_env || process . env . NODE_ENV ;
46
67
47
68
const emotionPool = { } ;
@@ -282,109 +303,116 @@ ComposerCore.propTypes = {
282
303
283
304
type ComposerProps = APIComposerProps & ComposerCoreProps ;
284
305
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
+ ) ;
388
416
389
417
Composer . defaultProps = {
390
418
...APIComposer . defaultProps ,
0 commit comments