diff --git a/src/button/RaisedHandToggleButton.tsx b/src/button/RaisedHandToggleButton.tsx index 502f6240..72f15a7b 100644 --- a/src/button/RaisedHandToggleButton.tsx +++ b/src/button/RaisedHandToggleButton.tsx @@ -99,19 +99,20 @@ export function RaiseHandToggleButton({ logger.error("Cannot find own membership event"); return; } + const parentEventId = myMembership.eventId; setBusy(true); client .sendEvent(rtcSession.room.roomId, EventType.Reaction, { "m.relates_to": { rel_type: RelationType.Annotation, - event_id: myMembership.eventId, + event_id: parentEventId, key: "🖐️", }, }) .then((reaction) => { logger.debug("Sent raise hand event", reaction.event_id); setMyReactionId(reaction.event_id); - addRaisedHand(userId, new Date()); + addRaisedHand(userId, parentEventId, new Date()); }) .catch((e) => { logger.error("Failed to send reaction event", e); diff --git a/src/useReactions.tsx b/src/useReactions.tsx index 2aa8727a..5db015a4 100644 --- a/src/useReactions.tsx +++ b/src/useReactions.tsx @@ -19,6 +19,7 @@ import { ReactNode, useCallback, useEffect, + useMemo, } from "react"; import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession"; @@ -28,7 +29,7 @@ import { useClientState } from "./ClientContext"; interface ReactionsContextType { raisedHands: Record; raisedHandCount: number; - addRaisedHand: (userId: string, date: Date) => void; + addRaisedHand: (userId: string, parentEventId: string, date: Date) => void; removeRaisedHand: (userId: string) => void; supportsReactions: boolean; myReactionId: string | null; @@ -54,7 +55,15 @@ export const ReactionsProvider = ({ children: ReactNode; rtcSession: MatrixRTCSession; }): JSX.Element => { - const [raisedHands, setRaisedHands] = useState>({}); + const [raisedHands, setRaisedHands] = useState< + Record< + string, + { + time: Date; + parentEventId: string; + } + > + >({}); const [myReactionId, setMyReactionId] = useState(null); const [raisedHandCount, setRaisedHandCount] = useState(0); const memberships = useMatrixRTCSessionMemberships(rtcSession); @@ -64,10 +73,13 @@ export const ReactionsProvider = ({ const room = rtcSession.room; const addRaisedHand = useCallback( - (userId: string, time: Date) => { + (userId: string, parentEventId: string, time: Date) => { setRaisedHands({ ...raisedHands, - [userId]: time, + [userId]: { + time, + parentEventId, + }, }); setRaisedHandCount(Object.keys(raisedHands).length + 1); }, @@ -77,6 +89,9 @@ export const ReactionsProvider = ({ const removeRaisedHand = useCallback( (userId: string) => { delete raisedHands[userId]; + if (userId) { + setMyReactionId(null); + } setRaisedHands(raisedHands); setRaisedHandCount(Object.keys(raisedHands).length); }, @@ -99,6 +114,10 @@ export const ReactionsProvider = ({ if (!m.sender || !m.eventId) { continue; } + if (raisedHands[m.sender].parentEventId !== m.eventId) { + // Membership event for sender has changed. + removeRaisedHand(m.sender); + } const reaction = getLastReactionEvent(m.eventId); const eventId = reaction?.getId(); if (!eventId) { @@ -107,7 +126,7 @@ export const ReactionsProvider = ({ if (reaction && reaction.getType() === EventType.Reaction) { const content = reaction.getContent() as ReactionEventContent; if (content?.["m.relates_to"]?.key === "🖐️") { - addRaisedHand(m.sender, new Date(reaction.localTimestamp)); + addRaisedHand(m.sender, m.eventId, new Date(reaction.localTimestamp)); if (m.sender === room.client.getUserId()) { setMyReactionId(eventId); } @@ -116,7 +135,7 @@ export const ReactionsProvider = ({ } // Deliberately ignoring addRaisedHand which was causing looping. // eslint-disable-next-line react-hooks/exhaustive-deps - }, [room, memberships]); + }, [raisedHands, room, memberships]); useEffect(() => { const handleReactionEvent = (event: MatrixEvent): void => { @@ -129,7 +148,11 @@ export const ReactionsProvider = ({ // TODO: check if target of reaction is a call membership event const content = event.getContent() as ReactionEventContent; if (content?.["m.relates_to"].key === "🖐️") { - addRaisedHand(sender, new Date(event.localTimestamp)); + addRaisedHand( + sender, + content["m.relates_to"].event_id, + new Date(event.localTimestamp), + ); } } else if (event.getType() === EventType.RoomRedaction) { // TODO: check target of redaction event @@ -146,10 +169,18 @@ export const ReactionsProvider = ({ }; }, [room, addRaisedHand, removeRaisedHand]); + const resultRaisedHands = useMemo( + () => + Object.fromEntries( + Object.entries(raisedHands).map(([uid, data]) => [uid, data.time]), + ), + [raisedHands], + ); + return (