forked from microsoft/BotFramework-WebChat
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathActivityTreeComposer.tsx
More file actions
112 lines (88 loc) · 4.27 KB
/
ActivityTreeComposer.tsx
File metadata and controls
112 lines (88 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import { hooks, type ActivityComponentFactory } from 'botframework-webchat-api';
import type { WebChatActivity } from 'botframework-webchat-core';
import React, { useMemo, useRef, type ReactNode } from 'react';
import useMemoWithPrevious from '../../hooks/internal/useMemoWithPrevious';
import ActivityTreeContext from './private/Context';
import { ActivityWithRenderer, ReadonlyActivityTree } from './private/types';
import useActivitiesWithRenderer from './private/useActivitiesWithRenderer';
import useActivityTreeWithRenderer from './private/useActivityTreeWithRenderer';
import useActivityTreeContext from './private/useContext';
import type { ActivityTreeContextType } from './private/Context';
type ActivityTreeComposerProps = Readonly<{ children?: ReactNode | undefined }>;
const {
useActivities,
useActivityKeys,
useCreateActivityRenderer,
useGetActivitiesByKey,
useGetKeyByActivity,
useReduceActivities
} = hooks;
const ActivityTreeComposer = ({ children }: ActivityTreeComposerProps) => {
const existingContext = useActivityTreeContext(false);
if (existingContext) {
throw new Error('botframework-webchat internal: <ActivityTreeComposer> should not be nested.');
}
const [rawActivities] = useActivities();
const getActivitiesByKey = useGetActivitiesByKey();
const getKeyByActivity = useGetKeyByActivity();
const activityKeys = useActivityKeys();
// Persistent Map to store activities by their keys
const activityMapRef = useRef<Readonly<Map<string, WebChatActivity>>>(
Object.freeze(new Map<string, WebChatActivity>())
);
const activities =
useReduceActivities<readonly WebChatActivity[]>((prevActivities = [], activity) => {
if (!activityKeys) {
return rawActivities;
}
const activityKey = getKeyByActivity(activity);
// "Activities with same key" means "multiple revisions of same activity."
const activitiesWithSameKey = getActivitiesByKey(activityKey);
// TODO: We may want to send all revisions of activity to the middleware so they can render UI to see previous revisions.
if (activitiesWithSameKey?.[activitiesWithSameKey.length - 1] === 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 prevActivities;
}) || [];
const createActivityRenderer: ActivityComponentFactory = useCreateActivityRenderer();
const activitiesWithRenderer = useActivitiesWithRenderer(activities, createActivityRenderer);
const activityTreeWithRenderer = useActivityTreeWithRenderer(activitiesWithRenderer);
const flattenedActivityTreeWithRenderer = useMemoWithPrevious<Readonly<ActivityWithRenderer[]>>(
prevFlattenedActivityTree => {
const nextFlattenedActivityTree = Object.freeze(
activityTreeWithRenderer.reduce<ActivityWithRenderer[]>(
(intermediate, entriesWithSameSender) =>
entriesWithSameSender.reduce<ActivityWithRenderer[]>(
(intermediate, entriesWithSameSenderAndStatus) =>
entriesWithSameSenderAndStatus.reduce<ActivityWithRenderer[]>((intermediate, entry) => {
intermediate.push(entry);
return intermediate;
}, intermediate),
intermediate
),
[]
)
);
return nextFlattenedActivityTree.length === prevFlattenedActivityTree?.length &&
nextFlattenedActivityTree.every((item, index) => item === prevFlattenedActivityTree[+index])
? prevFlattenedActivityTree
: nextFlattenedActivityTree;
},
[activityTreeWithRenderer]
);
const contextValue: ActivityTreeContextType = useMemo(
() => ({
activityTreeWithRendererState: Object.freeze([activityTreeWithRenderer]) as readonly [ReadonlyActivityTree],
flattenedActivityTreeWithRendererState: Object.freeze([flattenedActivityTreeWithRenderer]) as readonly [
readonly ActivityWithRenderer[]
]
}),
[activityTreeWithRenderer, flattenedActivityTreeWithRenderer]
);
return <ActivityTreeContext.Provider value={contextValue}>{children}</ActivityTreeContext.Provider>;
};
export default ActivityTreeComposer;