1- import { useMemoWithPrevious } from '@msinternal/botframework-webchat-react-hooks' ;
2- import { memo , createElement , useCallback , useMemo , useState , type ReactNode } from 'react' ;
3- import { useRefFrom } from 'use-ref-from' ;
1+ import { memo , createElement , useCallback , useMemo , type ReactNode , useRef } from 'react' ;
42
53import ActivityLogicalGroupingContext , {
64 type ActivityLogicalGroupingContextType ,
@@ -13,85 +11,42 @@ type ActivityLogicalGroupingComposerProps = Readonly<{
1311} > ;
1412
1513const ActivityLogicalGroupingComposer = ( { children } : ActivityLogicalGroupingComposerProps ) => {
16- const [ logicalGroupings , setLogicalGroupings ] = useState < readonly LogicalGrouping [ ] > ( [ ] ) ;
17- const logicalGroupingsRef = useRefFrom ( logicalGroupings ) ;
14+ const logicalGroupingsRef = useRef < Map < string , LogicalGrouping > > ( new Map ( ) ) ;
15+ const activityToGroupMapRef = useRef < ReadonlyMap < string , string | undefined > > ( new Map ( ) ) ;
1816
19- // Create a map from activity key to group key for quick lookups with reference stability
20- const activityToGroupMap = useMemoWithPrevious (
21- ( prevMap ?: ReadonlyMap < string , string > ) => {
22- const newMap = new Map < string , string > ( ) ;
17+ const addLogicalGrouping = useCallback (
18+ ( grouping : LogicalGrouping ) => {
19+ const prevGroupingKeys = logicalGroupingsRef . current . get ( grouping . id ) ?. activityKeys ?? [ ] ;
2320
24- logicalGroupings . forEach ( group => {
25- group . activityKeys . forEach ( activityKey => {
26- activityKey && newMap . set ( activityKey , group . id ) ;
27- } ) ;
28- } ) ;
21+ logicalGroupingsRef . current . set ( grouping . id , grouping ) ;
2922
30- // Check if the map content has actually changed
31- if ( prevMap && prevMap . size === newMap . size ) {
32- let hasChanged = false ;
33- for ( const [ key , value ] of newMap ) {
34- if ( prevMap . get ( key ) !== value ) {
35- hasChanged = true ;
36- break ;
37- }
38- }
23+ const toRemoveSet = new Set ( prevGroupingKeys ) . difference ( new Set ( grouping . activityKeys ) ) ;
24+ const toAddSet = new Set ( grouping . activityKeys ) . difference ( new Set ( prevGroupingKeys ) ) ;
3925
40- if ( ! hasChanged ) {
41- // Return the previous map to maintain reference stability
42- return prevMap ;
43- }
44- }
45-
46- return newMap ;
26+ activityToGroupMapRef . current = new Map ( [
27+ ...[ ...activityToGroupMapRef . current ] . filter ( ( [ , value ] ) => ! ! value && ! toRemoveSet . has ( value ) ) ,
28+ ...[ ...toAddSet ] . map ( activityKey => [ activityKey , grouping . id ] as const )
29+ ] ) ;
4730 } ,
48- [ logicalGroupings ]
31+ [ logicalGroupingsRef ]
4932 ) ;
5033
51- const activityToGroupMapRef = useRefFrom ( activityToGroupMap ) ;
52-
53- const addLogicalGrouping = useCallback ( ( grouping : LogicalGrouping ) => {
54- setLogicalGroupings ( prev => {
55- // Remove any existing grouping with the same ID and add the new one
56- const filtered = prev . filter ( existing => existing . id !== grouping . id ) ;
57- return Object . freeze ( [ ...filtered , grouping ] ) ;
58- } ) ;
59- } , [ ] ) ;
60-
6134 const getLogicalGroupKey = useCallback (
6235 ( activityKey : string ) : string | undefined => activityToGroupMapRef . current . get ( activityKey ) ,
6336 [ activityToGroupMapRef ]
6437 ) ;
6538
66- const shouldFocusLogicalGroup = useCallback (
67- ( activityKey : string ) : boolean => {
68- const groupKey = activityToGroupMapRef . current . get ( activityKey ) ;
69- if ( ! groupKey ) {
70- return false ;
71- }
72-
73- const group = logicalGroupingsRef . current . find ( g => g . id === groupKey ) ;
74- if ( ! group || ! group . getGroupState ) {
75- return false ;
76- }
77-
78- const groupState = group . getGroupState ( ) ;
79- return ! groupState . isCollapsed ;
80- } ,
81- [ activityToGroupMapRef , logicalGroupingsRef ]
82- ) ;
83-
8439 const getGroupState = useCallback (
8540 ( groupKey : string ) : GroupState | undefined => {
86- const group = logicalGroupingsRef . current . find ( g => g . id === groupKey ) ;
41+ const group = logicalGroupingsRef . current . get ( groupKey ) ;
8742 return group ?. getGroupState ?.( ) ;
8843 } ,
8944 [ logicalGroupingsRef ]
9045 ) ;
9146
9247 const getGroupBoundaries = useCallback (
9348 ( groupKey : string ) : [ string , string ] => {
94- const group = logicalGroupingsRef . current . find ( g => g . id === groupKey ) ;
49+ const group = logicalGroupingsRef . current . get ( groupKey ) ;
9550 if ( ! group || ! group . activityKeys . length ) {
9651 return [ undefined , undefined ] ;
9752 }
@@ -101,28 +56,14 @@ const ActivityLogicalGroupingComposer = ({ children }: ActivityLogicalGroupingCo
10156 [ logicalGroupingsRef ]
10257 ) ;
10358
104- const activityToGroupMapState = useMemo < readonly [ ReadonlyMap < string , string > ] > (
105- ( ) => Object . freeze ( [ activityToGroupMap ] as const ) ,
106- [ activityToGroupMap ]
107- ) ;
108-
10959 const contextValue : ActivityLogicalGroupingContextType = useMemo (
11060 ( ) => ( {
11161 addLogicalGrouping,
11262 getLogicalGroupKey,
113- shouldFocusLogicalGroup,
11463 getGroupState,
115- getGroupBoundaries,
116- activityToGroupMapState
64+ getGroupBoundaries
11765 } ) ,
118- [
119- addLogicalGrouping ,
120- getLogicalGroupKey ,
121- shouldFocusLogicalGroup ,
122- getGroupState ,
123- getGroupBoundaries ,
124- activityToGroupMapState
125- ]
66+ [ addLogicalGrouping , getLogicalGroupKey , getGroupState , getGroupBoundaries ]
12667 ) ;
12768
12869 return createElement ( ActivityLogicalGroupingContext . Provider , { value : contextValue } , children ) ;
0 commit comments