From 8e29e536e74653ff4dce84b85b3fe9361f65783a Mon Sep 17 00:00:00 2001 From: Pranav Joshi Date: Fri, 25 Apr 2025 16:21:55 +0530 Subject: [PATCH 1/2] improve activity tree composer --- packages/api/src/hooks/index.ts | 2 + packages/api/src/hooks/useReduceActivities.ts | 3 + .../ActivityTree/ActivityTreeComposer.tsx | 59 +++++++++++++------ 3 files changed, 47 insertions(+), 17 deletions(-) create mode 100644 packages/api/src/hooks/useReduceActivities.ts diff --git a/packages/api/src/hooks/index.ts b/packages/api/src/hooks/index.ts index 2768ef5b4b..f1cfe8e3d1 100644 --- a/packages/api/src/hooks/index.ts +++ b/packages/api/src/hooks/index.ts @@ -39,6 +39,7 @@ import useNotifications from './useNotifications'; import usePerformCardAction from './usePerformCardAction'; import usePonyfill from './usePonyfill'; import usePostActivity from './usePostActivity'; +import useReduceActivities from './useReduceActivities'; import useReferenceGrammarID from './useReferenceGrammarID'; import useRelativeTimeFormatter from './useRelativeTimeFormatter'; import useRenderAttachment from './useRenderAttachment'; @@ -113,6 +114,7 @@ export { usePerformCardAction, usePonyfill, usePostActivity, + useReduceActivities, useReferenceGrammarID, useRelativeTimeFormatter, useRenderAttachment, diff --git a/packages/api/src/hooks/useReduceActivities.ts b/packages/api/src/hooks/useReduceActivities.ts new file mode 100644 index 0000000000..00d98131e7 --- /dev/null +++ b/packages/api/src/hooks/useReduceActivities.ts @@ -0,0 +1,3 @@ +import useReduceActivities from '../providers/ActivityTyping/private/useReduceActivities'; + +export default useReduceActivities; diff --git a/packages/component/src/providers/ActivityTree/ActivityTreeComposer.tsx b/packages/component/src/providers/ActivityTree/ActivityTreeComposer.tsx index d5569b92ce..2a8b9c6d91 100644 --- a/packages/component/src/providers/ActivityTree/ActivityTreeComposer.tsx +++ b/packages/component/src/providers/ActivityTree/ActivityTreeComposer.tsx @@ -1,6 +1,6 @@ import { hooks, type ActivityComponentFactory } from 'botframework-webchat-api'; import type { WebChatActivity } from 'botframework-webchat-core'; -import React, { useMemo, type ReactNode } from 'react'; +import React, { useMemo, useRef, type ReactNode } from 'react'; import useMemoWithPrevious from '../../hooks/internal/useMemoWithPrevious'; import ActivityTreeContext from './private/Context'; @@ -13,7 +13,14 @@ import type { ActivityTreeContextType } from './private/Context'; type ActivityTreeComposerProps = Readonly<{ children?: ReactNode | undefined }>; -const { useActivities, useActivityKeys, useCreateActivityRenderer, useGetActivitiesByKey, useGetKeyByActivity } = hooks; +const { + useActivities, + useActivityKeys, + useCreateActivityRenderer, + useGetKeyByActivity, + useReduceActivities, + useGetActivityByKey +} = hooks; const ActivityTreeComposer = ({ children }: ActivityTreeComposerProps) => { const existingContext = useActivityTreeContext(false); @@ -23,30 +30,48 @@ const ActivityTreeComposer = ({ children }: ActivityTreeComposerProps) => { } const [rawActivities] = useActivities(); - const getActivitiesByKey = useGetActivitiesByKey(); const getKeyByActivity = useGetKeyByActivity(); const activityKeys = useActivityKeys(); + const getActivityByKey = useGetActivityByKey(); + const activityKeysSet = new Set(activityKeys[0]); - const activities = useMemo(() => { - const activities: WebChatActivity[] = []; + // Persistent Map to store activities by their keys + const activityMapRef = useRef>>( + Object.freeze(new Map()) + ); + + const activities = + useReduceActivities((prevActivities = [], activity) => { + if (!activityKeys) { + return rawActivities; + } - if (!activityKeys) { - return rawActivities; - } + // This is better than looping through all activities as bunch of stream activities will be clubbed + // under single key and number of iteration will be significantly less. + for (const key of activityMapRef.current.keys()) { + if (!activityKeysSet.has(key)) { + activityMapRef.current.delete(key); + } + } - for (const activity of rawActivities) { - // If an activity has multiple revisions, display the latest revision only at the position of the first revision. + const activityKey = getKeyByActivity(activity); - // "Activities with same key" means "multiple revisions of same activity." - const activitiesWithSameKey = getActivitiesByKey(getKeyByActivity(activity)); + // we always want last revision of activity whether it is single or multiple." + const lastActivity = getActivityByKey(activityKey); // TODO: We may want to send all revisions of activity to the middleware so they can render UI to see previous revisions. - activitiesWithSameKey?.[0] === activity && - activities.push(activitiesWithSameKey[activitiesWithSameKey.length - 1]); - } + if (lastActivity === activity) { + const activityMap = activityMapRef.current; + + // Update or add the activity in the persistent Map + activityMap.set(activityKey, activity); + + // Return the updated activities as an array + return Array.from(activityMap.values()); + } - return Object.freeze(activities); - }, [activityKeys, getActivitiesByKey, getKeyByActivity, rawActivities]); + return prevActivities; + }) || []; const createActivityRenderer: ActivityComponentFactory = useCreateActivityRenderer(); From ce4b813ef96dfabe3970e22084d4018106081b7f Mon Sep 17 00:00:00 2001 From: Pranav Joshi Date: Fri, 25 Apr 2025 16:27:05 +0530 Subject: [PATCH 2/2] changelog updated --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e91d22dd3..04a99bc327 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -184,6 +184,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/ - [`webpack-cli@6.0.1`](https://npmjs.com/package/webpack-cli/) - [`webpack@5.98.0`](https://npmjs.com/package/webpack/) - Fixed [#5446](https://github.com/microsoft/BotFramework-WebChat/issues/5446). Embedded `uuid` so `microsoft-cognitiveservices-speech-sdk` do not need to use dynamic loading, as this could fail in Webpack 4 environment, in PR [#5445](https://github.com/microsoft/BotFramework-WebChat/pull/5445), by [@compulim](https://github.com/compulim) +- - Improved latency of `ActivityTreeComposer` in PR [#5468](https://github.com/microsoft/BotFramework-WebChat/pull/5468) by [@pranavjoshi001](https://github.com/pranavjoshi001) ### Fixed