1- import React , { memo , useCallback , useMemo , type ReactNode } from 'react' ;
2- import { useReduceMemo } from 'use-reduce-memo' ;
3- import type { WebChatActivity } from 'botframework-webchat-core' ;
4- import { literal , object , safeParse } from 'valibot' ;
1+ import React , { memo , useCallback , useEffect , useMemo , useState , type ReactNode } from 'react' ;
52
6- import useActivities from '../../hooks/useActivities' ;
73import useWebChatAPIContext from '../../hooks/internal/useWebChatAPIContext' ;
84import CapabilitiesContext from './private/Context' ;
95import fetchCapabilitiesFromAdapter from './private/fetchCapabilitiesFromAdapter' ;
@@ -13,61 +9,39 @@ type Props = Readonly<{ children?: ReactNode | undefined }>;
139
1410const EMPTY_CAPABILITIES : Capabilities = Object . freeze ( { } ) ;
1511
16- // Synthetic marker to trigger initial fetch - must be a stable reference
17- const INIT_MARKER = Object . freeze ( { type : 'capabilities:init' as const } ) ;
18- type InitMarker = typeof INIT_MARKER ;
19- type ReducerInput = WebChatActivity | InitMarker ;
20-
21- const CapabilitiesChangedEventSchema = object ( {
22- type : literal ( 'event' ) ,
23- name : literal ( 'capabilitiesChanged' )
24- } ) ;
25-
26- const isInitMarker = ( item : ReducerInput ) : item is InitMarker => item === INIT_MARKER ;
27-
28- const isCapabilitiesChangedEvent = ( activity : ReducerInput ) : boolean =>
29- safeParse ( CapabilitiesChangedEventSchema , activity ) . success ;
30-
3112/**
32- * Composer that derives capabilities from the adapter using a pure derivation pattern.
13+ * Composer that provides capabilities from the adapter via EventTarget pattern.
3314 *
3415 * Design principles:
35- * 1. Initial fetch: Pulls capabilities from adapter on mount via synthetic init marker
36- * 2. Event-driven updates: Re-fetches only when 'capabilitiesChanged ' event is detected
16+ * 1. Initial fetch: Pulls capabilities from adapter on mount
17+ * 2. Event-driven updates: Re-fetches when adapter dispatches 'capabilitieschanged ' event
3718 * 3. Stable references: Individual capability objects maintain reference equality if unchanged
3819 * - This ensures consumers using selectors only re-render when their capability changes
3920 */
4021const CapabilitiesComposer = memo ( ( { children } : Props ) => {
41- const [ activities ] = useActivities ( ) ;
4222 const { directLine } = useWebChatAPIContext ( ) ;
4323
44- const activitiesWithInit = useMemo < readonly ReducerInput [ ] > (
45- ( ) => Object . freeze ( [ INIT_MARKER , ... activities ] ) ,
46- [ activities ]
24+ const getAllCapabilities = useCallback (
25+ ( ) => fetchCapabilitiesFromAdapter ( directLine , EMPTY_CAPABILITIES ) . capabilities ,
26+ [ directLine ]
4727 ) ;
4828
49- // TODO: [P1] update to use EventTarget than activity$.
50- const capabilities = useReduceMemo (
51- activitiesWithInit ,
52- useCallback (
53- ( prevCapabilities : Capabilities , item : ReducerInput ) : Capabilities => {
54- const shouldFetch = isInitMarker ( item ) || isCapabilitiesChangedEvent ( item ) ;
29+ const [ capabilities , setCapabilities ] = useState < Capabilities > ( ( ) => getAllCapabilities ( ) ) ;
5530
56- if ( ! shouldFetch ) {
57- return prevCapabilities ;
58- }
31+ useEffect ( ( ) => {
32+ const handleCapabilitiesChange = ( ) => {
33+ setCapabilities ( prevCapabilities => {
34+ const { capabilities, hasChanged } = fetchCapabilitiesFromAdapter ( directLine , prevCapabilities ) ;
35+ return hasChanged ? capabilities : prevCapabilities ;
36+ } ) ;
37+ } ;
5938
60- const { capabilities : newCapabilities , hasChanged } = fetchCapabilitiesFromAdapter (
61- directLine ,
62- prevCapabilities
63- ) ;
39+ if ( typeof directLine ?. addEventListener === 'function' ) {
40+ directLine . addEventListener ( 'capabilitieschanged' , handleCapabilitiesChange ) ;
6441
65- return hasChanged ? newCapabilities : prevCapabilities ;
66- } ,
67- [ directLine ]
68- ) ,
69- EMPTY_CAPABILITIES
70- ) ;
42+ return ( ) => directLine . removeEventListener ( 'capabilitieschanged' , handleCapabilitiesChange ) ;
43+ }
44+ } , [ directLine ] ) ;
7145
7246 const contextValue = useMemo ( ( ) => Object . freeze ( { capabilities } ) , [ capabilities ] ) ;
7347
0 commit comments