Skip to content

Commit 66b68ff

Browse files
authored
!fix: track DOM elements in useChatMessagesDomElements with pagination (#113)
* refactor: rename state in useChatMessageDomElements hook Renamed the state for clarity, as it stores an array of DOM elements. * fix: initial value of useChatMessageDomElements Set the initial value of the state `domElements` to an empty array for consistency with its declared type. This ensures the hook returns a more appropriate value(an empty array) when no message IDs are passed. * fix: track DOM elemnts in `useChatMessagesDomElements` with pagination The `useChatMessagesDomElements` hook was expecting all DOM elements from the rendered chat messages to be sent in a single event. However, due to the pagination mechanism in the bbb-core, only messages from the same page are sent together, which caused DOM elements from the last rendered page to overwrite those from previous pages. This commit adds support for pagination when storing DOM elements, ensuring that elements from different pages do not overwrite each other. It also includes a cleanup mechanism to remove stored DOM elements when a page is unmounted (due to pagination rolling or chat toggling). This ensures that only valid elements are stored and prevents the list from growing indefinitely. Additionally, the hook's internal state management has been updated and the filtering for the requested message IDs has been moved to the render function to ensure that the returned DOM elements are correctly updated when message IDs passed via props change. * fix: remove `messageIdsState` from `useChatMessageDomElements` Removes the `messageIdsState` as the hook already reacts to the message IDs passed via props, making the internal state redundant. * fix: page check and state update Checks whether the page is valid before updating state. Also, fixes a state update that was not using the proper set state function.
1 parent 2a2cdd7 commit 66b68ff

File tree

2 files changed

+73
-29
lines changed

2 files changed

+73
-29
lines changed

src/dom-element-manipulation/chat/message/hooks.ts

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,58 @@
1-
import { useEffect, useRef, useState } from 'react';
1+
import {
2+
useEffect, useState,
3+
useRef, useMemo,
4+
} from 'react';
25
import { DomElementManipulationHooks } from '../../enums';
36
import { HookEvents } from '../../../core/enum';
47
import {
58
HookEventWrapper, SubscribedEventDetails, UnsubscribedEventDetails, UpdatedEventDetails,
69
} from '../../../core/types';
7-
import { ChatMessageDomElementsArguments, UpdatedEventDetailsForChatMessageDomElements } from './types';
10+
import {
11+
ChatMessageDomElementsArguments,
12+
RenderedChatMessages,
13+
UpdatedEventDetailsForChatMessageDomElements,
14+
} from './types';
815
import { sortedStringify } from '../../../data-consumption/utils';
916

10-
export const useChatMessageDomElements = (messageIds: string[], pluginUuid: string) => {
11-
const [domElement, setDomElement] = useState<HTMLDivElement[]>();
12-
const [messageIdsState, setMessageIdsState] = useState<string[]>((messageIds) || []);
17+
const messageIdFromDomElement = (element: HTMLDivElement) => (element?.getAttribute('data-chat-message-id') || '');
1318

14-
const previousNeededIds = useRef<string[]>();
19+
export const useChatMessageDomElements = (messageIds: string[], pluginUuid: string) => {
20+
const [domElements, setDomElements] = useState<RenderedChatMessages>([]);
21+
const previousMessageIds = useRef<string[]>([]);
1522

1623
const handleDomElementUpdateEvent: EventListener = (
1724
(event: HookEventWrapper<
18-
UpdatedEventDetails<UpdatedEventDetailsForChatMessageDomElements[]>>) => {
25+
UpdatedEventDetails<UpdatedEventDetailsForChatMessageDomElements>>) => {
1926
const detail = event.detail as UpdatedEventDetails<
20-
UpdatedEventDetailsForChatMessageDomElements[]>;
27+
UpdatedEventDetailsForChatMessageDomElements>;
2128
if (detail.hook === DomElementManipulationHooks.CHAT_MESSAGE) {
22-
const filteredDataFromBbbCore = detail.data?.filter(
23-
(item) => messageIdsState.includes(item.messageId),
24-
) || [];
25-
const filteredStreamIdsFromBbbCore = filteredDataFromBbbCore.map(
26-
(item) => item.messageId,
29+
const pageToUpdate = detail.data?.page;
30+
if (pageToUpdate === undefined) return;
31+
if (detail.data?.messages.length === 0) {
32+
// indicates the page was unmounted in the client
33+
// so we remove all stored elements for that page,
34+
// since they might be invalid.
35+
setDomElements((domElementsState) => {
36+
const newDomElements = {
37+
...domElementsState,
38+
};
39+
delete newDomElements[pageToUpdate];
40+
return newDomElements;
41+
});
42+
}
43+
44+
const pageDomElementsFromBbbCore = detail.data?.messages.map(
45+
(item) => item.message,
46+
);
47+
const receivedAnythingNew = pageDomElementsFromBbbCore.some(
48+
(pageDomElement) => (
49+
!domElements[pageToUpdate]?.includes(pageDomElement)),
2750
);
28-
if (sortedStringify(filteredStreamIdsFromBbbCore)
29-
!== sortedStringify(previousNeededIds.current)) {
30-
previousNeededIds.current = [...filteredStreamIdsFromBbbCore];
31-
setDomElement(
32-
filteredDataFromBbbCore.map((messageItemFromCore) => messageItemFromCore.message),
33-
);
51+
if (receivedAnythingNew) {
52+
setDomElements((domElementsState) => ({
53+
...domElementsState,
54+
[pageToUpdate]: pageDomElementsFromBbbCore,
55+
}));
3456
}
3557
}
3658
}) as EventListener;
@@ -61,8 +83,19 @@ export const useChatMessageDomElements = (messageIds: string[], pluginUuid: stri
6183
);
6284
};
6385
}, []);
86+
6487
useEffect(() => {
6588
window.addEventListener(HookEvents.BBB_CORE_SENT_NEW_DATA, handleDomElementUpdateEvent);
89+
// Runs on code cleanup
90+
return () => {
91+
// On every `domElements` update, the event listener is removed and re-added to ensure
92+
// the handler has access to the latest `domElements` state value.
93+
window.removeEventListener(HookEvents.BBB_CORE_SENT_NEW_DATA, handleDomElementUpdateEvent);
94+
};
95+
}, [domElements]);
96+
97+
const updateRequestedIds = () => {
98+
previousMessageIds.current = messageIds;
6699
window.dispatchEvent(
67100
new CustomEvent<
68101
UpdatedEventDetails<void>>(HookEvents.PLUGIN_SENT_CHANGES_TO_BBB_CORE, {
@@ -76,14 +109,16 @@ export const useChatMessageDomElements = (messageIds: string[], pluginUuid: stri
76109
},
77110
}),
78111
);
79-
// Runs on code cleanup
80-
return () => {
81-
// Everytime the state update, we remove the eventListener and then we re-add it.
82-
window.removeEventListener(HookEvents.BBB_CORE_SENT_NEW_DATA, handleDomElementUpdateEvent);
83-
};
84-
}, [messageIdsState]);
85-
if (sortedStringify((messageIds) || []) !== sortedStringify(messageIdsState)) {
86-
setMessageIdsState((messageIds) || []);
112+
};
113+
114+
if (sortedStringify((messageIds) || []) !== sortedStringify(previousMessageIds.current)) {
115+
updateRequestedIds();
87116
}
88-
return domElement;
117+
118+
const flattenDomElements = useMemo(() => (
119+
Object.values(domElements).filter((i) => Array.isArray(i)).flat()
120+
), [domElements]);
121+
return flattenDomElements.filter((domElement) => (
122+
messageIds.includes(messageIdFromDomElement(domElement))
123+
));
89124
};

src/dom-element-manipulation/chat/message/types.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,16 @@ export interface ChatMessageDomElementsArguments {
77
pluginUuid: string;
88
}
99

10-
export interface UpdatedEventDetailsForChatMessageDomElements {
10+
export interface MessageDetails {
1111
messageId: string;
1212
message: HTMLDivElement;
1313
}
14+
15+
export interface UpdatedEventDetailsForChatMessageDomElements {
16+
page: number;
17+
messages: MessageDetails[];
18+
}
19+
20+
export interface RenderedChatMessages {
21+
[pageNumber: number]: HTMLDivElement[];
22+
}

0 commit comments

Comments
 (0)