From 865187d58e0c459263b2fa8b02a29de241037c3b Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 9 Dec 2024 14:36:25 +0000 Subject: [PATCH] Add support for using CallViewModel for reactions sounds. --- src/room/GroupCallView.tsx | 27 +++++++++++++++------------ src/room/InCallView.tsx | 7 ++++++- src/room/ReactionsOverlay.tsx | 27 ++++++++++----------------- src/state/CallViewModel.ts | 34 +++++++++++++++++++++++++++++++++- 4 files changed, 64 insertions(+), 31 deletions(-) diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 9336ffdd..f0a74b8b 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -41,6 +41,7 @@ import { InviteModal } from "./InviteModal"; import { useUrlParams } from "../UrlParams"; import { E2eeType } from "../e2ee/e2eeType"; import { Link } from "../button/Link"; +import { ReactionsProvider } from "../useReactions"; declare global { interface Window { @@ -328,18 +329,20 @@ export const GroupCallView: FC = ({ return ( <> {shareModal} - + + + ); } else if (left && widget === null) { diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 8710d3e8..b1382047 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -107,6 +107,7 @@ export const ActiveCall: FC = (props) => { [connState], ); const [vm, setVm] = useState(null); + const reactions = useReactions(); useEffect(() => { return (): void => { @@ -117,6 +118,10 @@ export const ActiveCall: FC = (props) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + vm?.updateReactions(reactions); + }, [vm, reactions]); + useEffect(() => { if (livekitRoom !== undefined) { const vm = new CallViewModel( @@ -638,7 +643,7 @@ export const InCallView: FC = ({ {renderContent()} - + {footer} {layout.type !== "pip" && ( <> diff --git a/src/room/ReactionsOverlay.tsx b/src/room/ReactionsOverlay.tsx index 7cdf7568..edc967ea 100644 --- a/src/room/ReactionsOverlay.tsx +++ b/src/room/ReactionsOverlay.tsx @@ -5,33 +5,26 @@ SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ -import { ReactNode, useMemo } from "react"; - -import { useReactions } from "../useReactions"; +import { ReactNode } from "react"; import { showReactions as showReactionsSetting, useSetting, } from "../settings/settings"; import styles from "./ReactionsOverlay.module.css"; +import { CallViewModel } from "../state/CallViewModel"; +import { useObservableState } from "observable-hooks"; -export function ReactionsOverlay(): ReactNode { - const { reactions } = useReactions(); +export function ReactionsOverlay({ vm }: { vm: CallViewModel }): ReactNode { const [showReactions] = useSetting(showReactionsSetting); - const reactionsIcons = useMemo( - () => - showReactions - ? Object.entries(reactions).map(([sender, { emoji }]) => ({ - sender, - emoji, - startX: Math.ceil(Math.random() * 80) + 10, - })) - : [], - [showReactions, reactions], - ); + const reactionsIcons = useObservableState(vm.visibleReactions); + + if (!showReactions) { + return; + } return (
- {reactionsIcons.map(({ sender, emoji, startX }) => ( + {reactionsIcons?.map(({ sender, emoji, startX }) => ( >(); + public readonly reactions = new Subject>(); + + public updateReactions(data: ReturnType) { + this.handsRaised.next(data.raisedHands); + this.reactions.next(data.reactions); + } + + public readonly visibleReactions = combineLatest([ + this.reactions, + showReactions.value, + ]) + .pipe( + map(([reactions, setting]) => (setting ? reactions : {})), + scan< + Record, + { sender: string; emoji: string; startX: number }[] + >((acc, latest) => { + const newSet: { sender: string; emoji: string; startX: number }[] = []; + for (const [sender, reaction] of Object.entries(latest)) { + const startX = + acc.find((v) => v.sender === sender && v.emoji)?.startX ?? + Math.ceil(Math.random() * 80) + 10; + newSet.push({ sender, emoji: reaction.emoji, startX }); + } + return newSet; + }, []), + ) + .pipe(this.scope.state()); + public constructor( // A call is permanently tied to a single Matrix room and LiveKit room private readonly matrixRTCSession: MatrixRTCSession,