upd
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable no-empty */
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
Config,
|
||||
AllSettings,
|
||||
PixelStreaming,
|
||||
} from "@epicgames-ps/lib-pixelstreamingfrontend-ue5.5";
|
||||
|
||||
export interface PixelStreamingWrapperProps {
|
||||
initialSettings?: Partial<AllSettings>;
|
||||
}
|
||||
|
||||
export const PixelStreamingWrapper2 = ({
|
||||
initialSettings,
|
||||
}: PixelStreamingWrapperProps) => {
|
||||
// A reference to parent div element that the Pixel Streaming library attaches into:
|
||||
const videoParent = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Pixel streaming library instance is stored into this state variable after initialization:
|
||||
const [pixelStreaming, setPixelStreaming] = useState<PixelStreaming>();
|
||||
|
||||
// A boolean state variable that determines if the Click to play overlay is shown:
|
||||
const [clickToPlayVisible, setClickToPlayVisible] = useState(false);
|
||||
|
||||
// Run on component mount:
|
||||
useEffect(() => {
|
||||
if (videoParent.current) {
|
||||
// Attach Pixel Streaming library to videoParent element:
|
||||
const config = new Config({ initialSettings });
|
||||
const streaming = new PixelStreaming(config, {
|
||||
videoElementParent: videoParent.current,
|
||||
});
|
||||
|
||||
// register a playStreamRejected handler to show Click to play overlay if needed:
|
||||
streaming.addEventListener("playStreamRejected", () => {
|
||||
setClickToPlayVisible(true);
|
||||
});
|
||||
|
||||
// Save the library instance into component state so that it can be accessed later:
|
||||
setPixelStreaming(streaming);
|
||||
|
||||
// Clean up on component unmount:
|
||||
return () => {
|
||||
try {
|
||||
streaming.disconnect();
|
||||
} catch {}
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
ref={videoParent}
|
||||
/>
|
||||
{clickToPlayVisible && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
pixelStreaming?.play();
|
||||
setClickToPlayVisible(false);
|
||||
}}
|
||||
>
|
||||
<div>Click to play</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -10,7 +10,6 @@ import { Trans } from "react-i18next";
|
||||
import i18n from "../i18n";
|
||||
import { useState } from "react";
|
||||
import LoaderIcon from "./icons/LoaderIcon";
|
||||
import api from "../utils/api";
|
||||
|
||||
function SidebarTab4() {
|
||||
const {
|
||||
@@ -28,17 +27,6 @@ function SidebarTab4() {
|
||||
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
async function sendInvite(email: string, link: string) {
|
||||
try {
|
||||
const reuslt: any = await api
|
||||
.post("sendInvite", { json: { email, link } })
|
||||
.json();
|
||||
console.log("reuslt", reuslt);
|
||||
} catch (error) {
|
||||
console.log({ error: (error as Error).message });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleClickSignUp() {
|
||||
if (!selectedTime || !selectedDay) {
|
||||
return;
|
||||
@@ -63,7 +51,6 @@ function SidebarTab4() {
|
||||
})
|
||||
.json();
|
||||
|
||||
sendInvite(email, result.url);
|
||||
setUrl(result.url);
|
||||
setCurrentTab(currentTab + 1);
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
function ToastContainer() {
|
||||
return <div></div>;
|
||||
}
|
||||
|
||||
export default ToastContainer;
|
||||
@@ -1,69 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useRoomContext } from "@livekit/components-react";
|
||||
import { Track } from "livekit-client";
|
||||
import { useEffect, useState } from "react";
|
||||
import MicroOnIcon from "./icons/MicroOnIcon";
|
||||
import MicroOffIcon from "./icons/MicroOffIcon";
|
||||
|
||||
function ToggleMic({ socket, handleUpdate }: any) {
|
||||
const room = useRoomContext();
|
||||
const [muted, setMuted] = useState(true);
|
||||
|
||||
function toggleMic(value: boolean) {
|
||||
const audioTrack = room.localParticipant.getTrack(Track.Source.Microphone);
|
||||
|
||||
if (!audioTrack) return;
|
||||
|
||||
if (value) {
|
||||
audioTrack.mute();
|
||||
} else {
|
||||
audioTrack.unmute();
|
||||
}
|
||||
|
||||
setMuted(!audioTrack.isMuted);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!socket) return;
|
||||
|
||||
socket.on("update", (socketId: string, data: { [key: string]: any }) => {
|
||||
if (
|
||||
socket.id === socketId &&
|
||||
Object.prototype.hasOwnProperty.call(data, "muted")
|
||||
) {
|
||||
toggleMic(data.muted);
|
||||
}
|
||||
});
|
||||
}, [socket]);
|
||||
|
||||
useEffect(() => {
|
||||
// room.on(RoomEvent.TrackMuted, (_, participant) => {
|
||||
// console.log(participant);
|
||||
// });
|
||||
|
||||
// room.on(RoomEvent.TrackUnmuted, (_, participant) => {
|
||||
// console.log(participant);
|
||||
// });
|
||||
|
||||
room.on("localTrackPublished", () => {
|
||||
room.localParticipant.getTrack(Track.Source.Microphone)?.mute();
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() =>
|
||||
handleUpdate(room.localParticipant.identity, {
|
||||
muted: !room.localParticipant.getTrack(Track.Source.Microphone)
|
||||
?.isMuted,
|
||||
})
|
||||
}
|
||||
className="relative group outline-none bg-[#131313] rounded-full shadow-lg shadow-[#131313] p-2 opacity-90"
|
||||
>
|
||||
{!muted ? <MicroOnIcon /> : <MicroOffIcon />}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default ToggleMic;
|
||||
@@ -0,0 +1,35 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import useIsAudioActive from "use-is-audio-active";
|
||||
|
||||
interface Props {
|
||||
mediaStream: MediaStream | null;
|
||||
muted: boolean;
|
||||
}
|
||||
|
||||
function Video({ mediaStream, muted }: Props) {
|
||||
const remoteVideoRef = useRef<HTMLVideoElement>(null);
|
||||
const isSpeaking = useIsAudioActive({ source: mediaStream });
|
||||
|
||||
useEffect(() => {
|
||||
if (!remoteVideoRef.current) return;
|
||||
|
||||
remoteVideoRef.current.srcObject = mediaStream;
|
||||
remoteVideoRef.current.onloadedmetadata = () => {
|
||||
remoteVideoRef.current?.play();
|
||||
};
|
||||
}, [mediaStream]);
|
||||
|
||||
return (
|
||||
<video
|
||||
ref={remoteVideoRef}
|
||||
className={`aspect-video bg-black lg:w-[216px] lg:h-[162px] w-[112px] h-[84px] rounded-lg object-cover ring-2 ${
|
||||
isSpeaking ? "ring-green-500" : "ring-transparent"
|
||||
}`}
|
||||
playsInline
|
||||
autoPlay
|
||||
muted={muted}
|
||||
></video>
|
||||
);
|
||||
}
|
||||
|
||||
export default Video;
|
||||
@@ -0,0 +1,20 @@
|
||||
function CameraOffIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="size-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M12 18.75H4.5a2.25 2.25 0 0 1-2.25-2.25V9m12.841 9.091L16.5 19.5m-1.409-1.409c.407-.407.659-.97.659-1.591v-9a2.25 2.25 0 0 0-2.25-2.25h-9c-.621 0-1.184.252-1.591.659m12.182 12.182L2.909 5.909M1.5 4.5l1.409 1.409"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default CameraOffIcon;
|
||||
@@ -0,0 +1,20 @@
|
||||
function CameraOnIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="size-6"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25h-9A2.25 2.25 0 0 0 2.25 7.5v9a2.25 2.25 0 0 0 2.25 2.25Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default CameraOnIcon;
|
||||
Reference in New Issue
Block a user