diff --git a/src/useReactions.test.tsx b/src/useReactions.test.tsx
index 6140793fc..3fb432475 100644
--- a/src/useReactions.test.tsx
+++ b/src/useReactions.test.tsx
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
-import { render } from "@testing-library/react";
+import { findByRole, getByRole, render } from "@testing-library/react";
import { act, FC } from "react";
import { describe, expect, test } from "vitest";
import { RoomEvent } from "matrix-js-sdk/src/matrix";
@@ -43,7 +43,7 @@ const TestComponent: FC = () => {
{Object.entries(raisedHands).map(([userId, date]) => (
- -
+
-
{userId}
@@ -106,12 +106,12 @@ describe("useReactions", () => {
createHandRaisedReaction(memberEventAlice, membership),
]);
const rtcSession = new MockRTCSession(room, membership);
- const { queryByRole } = render(
+ const { findByRole } = render(
,
);
- expect(queryByRole("list")?.children).to.have.lengthOf(1);
+ expect(findByRole("listitem")).toBeTruthy();
});
// If the membership event changes for a user, we want to remove
// the raised hand event.
@@ -120,12 +120,12 @@ describe("useReactions", () => {
createHandRaisedReaction(memberEventAlice, membership),
]);
const rtcSession = new MockRTCSession(room, membership);
- const { queryByRole } = render(
+ const { findByRole, queryByRole } = render(
,
);
- expect(queryByRole("list")?.children).to.have.lengthOf(1);
+ expect(findByRole("listitem")).toBeTruthy();
act(() => rtcSession.testRemoveMember(memberUserIdAlice));
expect(queryByRole("list")?.children).to.have.lengthOf(0);
});
@@ -134,12 +134,12 @@ describe("useReactions", () => {
createHandRaisedReaction(memberEventAlice, membership),
]);
const rtcSession = new MockRTCSession(room, membership);
- const { queryByRole } = render(
+ const { queryByRole, findByRole } = render(
,
);
- expect(queryByRole("list")?.children).to.have.lengthOf(1);
+ expect(findByRole("listitem")).toBeTruthy();
// Simulate leaving and rejoining
act(() => {
rtcSession.testRemoveMember(memberUserIdAlice);
diff --git a/src/useReactions.tsx b/src/useReactions.tsx
index c8d3c3b59..ce527eab9 100644
--- a/src/useReactions.tsx
+++ b/src/useReactions.tsx
@@ -106,6 +106,7 @@ export const ReactionsProvider = ({
);
const addRaisedHand = useCallback((userId: string, info: RaisedHandInfo) => {
+ logger.info(`Adding raised hand for ${userId}`);
setRaisedHands((prevRaisedHands) => ({
...prevRaisedHands,
[userId]: info,
@@ -113,6 +114,7 @@ export const ReactionsProvider = ({
}, []);
const removeRaisedHand = useCallback((userId: string) => {
+ logger.info(`Removing raised hand for ${userId}`);
setRaisedHands(
({ [userId]: _removed, ...remainingRaisedHands }) => remainingRaisedHands,
);
@@ -121,16 +123,26 @@ export const ReactionsProvider = ({
// This effect will check the state whenever the membership of the session changes.
useEffect(() => {
// Fetches the first reaction for a given event.
- const getLastReactionEvent = (
+ const getLastReactionEvent = async (
eventId: string,
expectedSender: string,
- ): MatrixEvent | undefined => {
+ ): Promise => {
const relations = room.relations.getChildEventsForEvent(
eventId,
RelationType.Annotation,
EventType.Reaction,
);
- const allEvents = relations?.getRelations() ?? [];
+ let allEvents = relations?.getRelations() ?? [];
+ // If we found no relations for this event, fetch via fetchRelations.
+ if (allEvents.length === 0) {
+ const res = await room.client.fetchRelations(
+ room.roomId,
+ eventId,
+ RelationType.Annotation,
+ EventType.Reaction,
+ );
+ allEvents = res.chunk.map((e) => new MatrixEvent(e));
+ }
return allEvents.find(
(reaction) =>
reaction.event.sender === expectedSender &&
@@ -160,18 +172,26 @@ export const ReactionsProvider = ({
// was raised, reset.
removeRaisedHand(m.sender);
}
- const reaction = getLastReactionEvent(m.eventId, m.sender);
- if (reaction) {
- const eventId = reaction?.getId();
- if (!eventId) {
- continue;
- }
- addRaisedHand(m.sender, {
- membershipEventId: m.eventId,
- reactionEventId: eventId,
- time: new Date(reaction.localTimestamp),
+ getLastReactionEvent(m.eventId, m.sender)
+ .then((reaction) => {
+ if (reaction) {
+ const eventId = reaction.getId();
+ if (!eventId) {
+ return;
+ }
+ addRaisedHand(m.sender!, {
+ membershipEventId: m.eventId!,
+ reactionEventId: eventId,
+ time: new Date(reaction.localTimestamp),
+ });
+ }
+ })
+ .catch((ex) => {
+ logger.warn(
+ `Failed to fetch reaction for member ${m.sender} (${m.eventId})`,
+ ex,
+ );
});
- }
}
// Ignoring raisedHands here because we don't want to trigger each time the raised
// hands set is updated.
@@ -307,8 +327,8 @@ export const ReactionsProvider = ({
return (): void => {
room.off(MatrixRoomEvent.Timeline, handleReactionEvent);
room.off(MatrixRoomEvent.Redaction, handleReactionEvent);
- room.client.off(MatrixEventEvent.Decrypted, handleReactionEvent);
room.off(MatrixRoomEvent.LocalEchoUpdated, handleReactionEvent);
+ room.client.off(MatrixEventEvent.Decrypted, handleReactionEvent);
reactionTimeouts.forEach((t) => clearTimeout(t));
// If we're clearing timeouts, we also clear all reactions.
setReactions({});
diff --git a/src/utils/testReactions.tsx b/src/utils/testReactions.tsx
index 84ff217bd..e9ced62a6 100644
--- a/src/utils/testReactions.tsx
+++ b/src/utils/testReactions.tsx
@@ -139,6 +139,7 @@ export class MockRoom extends EventEmitter {
return Promise.resolve({ event_id: randomUUID() });
},
decryptEventIfNeeded: async () => {},
+ fetchRelations: async () => Promise.resolve({ chunk: [] }),
on() {
return this;
},