mirror of
https://github.com/vector-im/element-call.git
synced 2026-04-03 07:10:26 +00:00
Initial support for Hand Raise feature
Signed-off-by: Milton Moura <miltonmoura@gmail.com>
This commit is contained in:
@@ -10,7 +10,14 @@ import {
|
||||
RoomContext,
|
||||
useLocalParticipant,
|
||||
} from "@livekit/components-react";
|
||||
import { ConnectionState, Room } from "livekit-client";
|
||||
import {
|
||||
ConnectionState,
|
||||
// eslint-disable-next-line camelcase
|
||||
DataPacket_Kind,
|
||||
Participant,
|
||||
Room,
|
||||
RoomEvent,
|
||||
} from "livekit-client";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import {
|
||||
FC,
|
||||
@@ -39,6 +46,7 @@ import {
|
||||
MicButton,
|
||||
VideoButton,
|
||||
ShareScreenButton,
|
||||
RaiseHandButton,
|
||||
SettingsButton,
|
||||
} from "../button";
|
||||
import { Header, LeftNav, RightNav, RoomHeaderInfo } from "../Header";
|
||||
@@ -78,6 +86,7 @@ import { makeOneOnOneLayout } from "../grid/OneOnOneLayout";
|
||||
import { makeSpotlightExpandedLayout } from "../grid/SpotlightExpandedLayout";
|
||||
import { makeSpotlightLandscapeLayout } from "../grid/SpotlightLandscapeLayout";
|
||||
import { makeSpotlightPortraitLayout } from "../grid/SpotlightPortraitLayout";
|
||||
import { RaisedHandsProvider, useRaisedHands } from "./useRaisedHands";
|
||||
|
||||
const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {});
|
||||
|
||||
@@ -130,12 +139,14 @@ export const ActiveCall: FC<ActiveCallProps> = (props) => {
|
||||
|
||||
return (
|
||||
<RoomContext.Provider value={livekitRoom}>
|
||||
<InCallView
|
||||
{...props}
|
||||
vm={vm}
|
||||
livekitRoom={livekitRoom}
|
||||
connState={connState}
|
||||
/>
|
||||
<RaisedHandsProvider>
|
||||
<InCallView
|
||||
{...props}
|
||||
vm={vm}
|
||||
livekitRoom={livekitRoom}
|
||||
connState={connState}
|
||||
/>
|
||||
</RaisedHandsProvider>
|
||||
</RoomContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -298,6 +309,34 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
[vm],
|
||||
);
|
||||
|
||||
const { raisedHands, setRaisedHands } = useRaisedHands();
|
||||
const isHandRaised = raisedHands.includes(
|
||||
localParticipant.identity.split(":")[0] +
|
||||
":" +
|
||||
localParticipant.identity.split(":")[1],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleDataReceived = (
|
||||
payload: Uint8Array,
|
||||
participant?: Participant,
|
||||
// eslint-disable-next-line camelcase
|
||||
kind?: DataPacket_Kind,
|
||||
): void => {
|
||||
const decoder = new TextDecoder();
|
||||
const strData = decoder.decode(payload);
|
||||
// get json object from strData
|
||||
const data = JSON.parse(strData);
|
||||
setRaisedHands(data.raisedHands);
|
||||
};
|
||||
|
||||
livekitRoom.on(RoomEvent.DataReceived, handleDataReceived);
|
||||
|
||||
return (): void => {
|
||||
livekitRoom.off(RoomEvent.DataReceived, handleDataReceived);
|
||||
};
|
||||
}, [livekitRoom, setRaisedHands]);
|
||||
|
||||
useEffect(() => {
|
||||
widget?.api.transport
|
||||
.send(
|
||||
@@ -479,6 +518,37 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
.catch(logger.error);
|
||||
}, [localParticipant, isScreenShareEnabled]);
|
||||
|
||||
const toggleRaisedHand = useCallback(() => {
|
||||
// TODO: wtf
|
||||
const userId =
|
||||
localParticipant.identity.split(":")[0] +
|
||||
":" +
|
||||
localParticipant.identity.split(":")[1];
|
||||
const raisedHand = raisedHands.includes(userId);
|
||||
let result = raisedHands;
|
||||
if (raisedHand) {
|
||||
result = raisedHands.filter((id) => id !== userId);
|
||||
} else {
|
||||
result = [...raisedHands, userId];
|
||||
}
|
||||
try {
|
||||
const strData = JSON.stringify({
|
||||
raisedHands: result,
|
||||
});
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(strData);
|
||||
livekitRoom.localParticipant.publishData(data, { reliable: true });
|
||||
setRaisedHands(result);
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
}
|
||||
}, [
|
||||
livekitRoom.localParticipant,
|
||||
localParticipant.identity,
|
||||
raisedHands,
|
||||
setRaisedHands,
|
||||
]);
|
||||
|
||||
let footer: JSX.Element | null;
|
||||
|
||||
if (noControls) {
|
||||
@@ -513,7 +583,14 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
/>,
|
||||
);
|
||||
}
|
||||
buttons.push(<SettingsButton key="4" onClick={openSettings} />);
|
||||
buttons.push(
|
||||
<RaiseHandButton
|
||||
key="4"
|
||||
onClick={toggleRaisedHand}
|
||||
raised={isHandRaised}
|
||||
/>,
|
||||
);
|
||||
buttons.push(<SettingsButton key="5" onClick={openSettings} />);
|
||||
}
|
||||
|
||||
buttons.push(
|
||||
|
||||
47
src/room/useRaisedHands.tsx
Normal file
47
src/room/useRaisedHands.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2024 Milton Moura <miltonmoura@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { createContext, useContext, useState, ReactNode } from "react";
|
||||
|
||||
interface RaisedHandsContextType {
|
||||
raisedHands: string[];
|
||||
setRaisedHands: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
}
|
||||
|
||||
const RaisedHandsContext = createContext<RaisedHandsContextType | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
export const useRaisedHands = (): RaisedHandsContextType => {
|
||||
const context = useContext(RaisedHandsContext);
|
||||
if (!context) {
|
||||
throw new Error("useRaisedHands must be used within a RaisedHandsProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export const RaisedHandsProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}): JSX.Element => {
|
||||
const [raisedHands, setRaisedHands] = useState<string[]>([]);
|
||||
return (
|
||||
<RaisedHandsContext.Provider value={{ raisedHands, setRaisedHands }}>
|
||||
{children}
|
||||
</RaisedHandsContext.Provider>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user