diff --git a/src/useReactions.test.tsx b/src/useReactions.test.tsx
index 99db2619..79caeb0a 100644
--- a/src/useReactions.test.tsx
+++ b/src/useReactions.test.tsx
@@ -99,9 +99,12 @@ export class MockRTCSession extends EventEmitter {
}
}
-function createReaction(parentMemberEvent: string): MatrixEvent {
+function createReaction(
+ parentMemberEvent: string,
+ overridenSender?: string,
+): MatrixEvent {
return new MatrixEvent({
- sender: membership[parentMemberEvent],
+ sender: overridenSender ?? membership[parentMemberEvent],
type: EventType.Reaction,
origin_server_ts: new Date().getTime(),
content: {
@@ -140,15 +143,20 @@ export class MockRoom extends EventEmitter {
return {
getChildEventsForEvent: (membershipEventId: string) => ({
getRelations: (): MatrixEvent[] => {
- const sender = membership[membershipEventId];
- return this.existingRelations.filter((r) => r.getSender() === sender);
+ return this.existingRelations.filter(
+ (r) =>
+ r.getContent()["m.relates_to"]?.event_id === membershipEventId,
+ );
},
}),
} as unknown as Room["relations"];
}
- public testSendReaction(parentMemberEvent: string): string {
- const evt = createReaction(parentMemberEvent);
+ public testSendReaction(
+ parentMemberEvent: string,
+ overridenSender?: string,
+ ): string {
+ const evt = createReaction(parentMemberEvent, overridenSender);
this.emit(RoomEvent.Timeline, evt, this, undefined, false, {
timeline: new EventTimeline(new EventTimelineSet(undefined)),
});
@@ -238,4 +246,23 @@ describe("useReactions", () => {
});
expect(queryByRole("list")?.children).to.have.lengthOf(0);
});
+ test("ignores invalid sender for historic event", () => {
+ const room = new MockRoom([
+ createReaction(memberEventAlice, memberUserIdBob),
+ ]);
+ const rtcSession = new MockRTCSession(room);
+ const { queryByRole } = render(
+ ,
+ );
+ expect(queryByRole("list")?.children).to.have.lengthOf(0);
+ });
+ test("ignores invalid sender for new event", async () => {
+ const room = new MockRoom([]);
+ const rtcSession = new MockRTCSession(room);
+ const { queryByRole } = render(
+ ,
+ );
+ await act(() => room.testSendReaction(memberEventAlice, memberUserIdBob));
+ expect(queryByRole("list")?.children).to.have.lengthOf(0);
+ });
});
diff --git a/src/useReactions.tsx b/src/useReactions.tsx
index 6aac4043..342c43e1 100644
--- a/src/useReactions.tsx
+++ b/src/useReactions.tsx
@@ -113,14 +113,17 @@ export const ReactionsProvider = ({
useEffect(() => {
// Fetches the first reaction for a given event. We assume no more than
// one reaction on an event here.
- const getLastReactionEvent = (eventId: string): MatrixEvent | undefined => {
+ const getLastReactionEvent = (
+ eventId: string,
+ expectedSender: string,
+ ): MatrixEvent | undefined => {
const relations = room.relations.getChildEventsForEvent(
eventId,
RelationType.Annotation,
EventType.Reaction,
);
const allEvents = relations?.getRelations() ?? [];
- return allEvents.find((u) => u.sender === myUserId);
+ return allEvents.find((u) => u.event.sender === expectedSender);
};
// Remove any raised hands for users no longer joined to the call.
@@ -144,7 +147,7 @@ export const ReactionsProvider = ({
// was raised, reset.
removeRaisedHand(m.sender);
}
- const reaction = getLastReactionEvent(m.eventId);
+ const reaction = getLastReactionEvent(m.eventId, m.sender);
const eventId = reaction?.getId();
if (!eventId) {
continue;
@@ -160,14 +163,10 @@ export const ReactionsProvider = ({
}
}
}
- }, [
- room,
- memberships,
- myUserId,
- raisedHands,
- addRaisedHand,
- removeRaisedHand,
- ]);
+ // Ignoring raisedHands here because we don't want to trigger each time the raised
+ // hands set is updated.
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [room, memberships, myUserId, addRaisedHand, removeRaisedHand]);
// This effect handles any *live* reaction/redactions in the room.
useEffect(() => {