botframework-webchat-component
Version:
React component of botframework-webchat
66 lines (52 loc) • 3.44 kB
JavaScript
import { hooks } from 'botframework-webchat-api';
import { useMemo, useRef } from 'react';
import { useSticky } from 'react-scroll-to-bottom';
import findLastIndex from '../../Utils/findLastIndex';
import getActivityUniqueId from '../../Utils/getActivityUniqueId';
import useChanged from './useChanged';
const { useActivities } = hooks;
// Acknowledged means either:
// 1. The user sent a message
// - We don't need a condition here. When Web Chat sends the user's message, it will scroll to bottom, and it will trigger condition 2 below.
// 2. The user scroll to the bottom of the transcript, from a non-bottom scroll position
// - If the transcript is already at the bottom, the user needs to scroll up and then go back down
// - What happens if we are relaxing "scrolled from a non-bottom scroll position":
// 1. The condition will become solely "at the bottom of the transcript"
// 2. Auto-scroll will always scroll the transcript to the bottom
// 3. Web Chat will always acknowledge all activities as it is at the bottom
// 4. Acknowledge flag become useless
// 5. Therefore, even the developer set "pause after 3 activities", if activities are coming in at a slow pace (not batched in a single render)
// Web Chat will keep scrolling and not snapped/paused
// Note: When Web Chat is loaded, there are no activities acknowledged. We need to assume all arriving activities are acknowledged until end-user sends their first activity.
// Activities loaded initially could be from conversation history. Without assuming acknowledgement, Web Chat will not scroll initially (as everything is not acknowledged).
// It would be better if the chat adapter should let Web Chat know if the activity is loaded from history or not.
// TODO: [P2] #3670 Move the "conversation history acknowledgement" logic mentioned above to polyfill of chat adapters.
// 1. Chat adapter should send "acknowledged" as part of "channelData"
// 2. If "acknowledged" is "undefined", we set it to:
// a. true, if there are no egress activities yet
// b. Otherwise, false
export default function useAcknowledgedActivity() {
const [activities] = useActivities();
const [sticky] = useSticky();
const lastStickyActivityIDRef = useRef();
const stickyChanged = useChanged(sticky);
const stickyChangedToSticky = stickyChanged && sticky;
const lastStickyActivityID = useMemo(() => {
if (stickyChangedToSticky) {
lastStickyActivityIDRef.current = getActivityUniqueId(activities[activities.length - 1] || {});
}
return lastStickyActivityIDRef.current;
}, [activities, lastStickyActivityIDRef, stickyChangedToSticky]);
return useMemo(() => {
const lastStickyActivityIndex = activities.findIndex(
activity => getActivityUniqueId(activity) === lastStickyActivityID
);
const lastEgressActivityIndex = findLastIndex(activities, ({ from: { role } = {} }) => role === 'user');
// As described above, if no activities were acknowledged through egress activity, we will assume everything is acknowledged.
const lastAcknowledgedActivityIndex = !~lastEgressActivityIndex
? activities.length - 1
: Math.max(lastStickyActivityIndex, lastEgressActivityIndex);
const lastAcknowledgedActivity = activities[lastAcknowledgedActivityIndex];
return [lastAcknowledgedActivity];
}, [activities, lastStickyActivityID]);
}