upd
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { useRouteError } from "react-router-dom";
|
||||
|
||||
function ErrorBoundary() {
|
||||
const error: any = useRouteError();
|
||||
console.error(error);
|
||||
|
||||
return (
|
||||
<div className="h-screen flex items-center justify-center p-8 text-white">
|
||||
<div className="flex items-center gap-4">
|
||||
<p className="font-gilroy text-2xl">{error.status}</p>
|
||||
<p>{error.statusText}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ErrorBoundary;
|
||||
@@ -1,4 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable no-empty */
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
@@ -6,16 +8,17 @@ import {
|
||||
Config,
|
||||
AllSettings,
|
||||
PixelStreaming,
|
||||
LatencyTestResults,
|
||||
} from "@epicgames-ps/lib-pixelstreamingfrontend-ue5.3";
|
||||
import { Trans } from "react-i18next";
|
||||
import LoaderIcon from "./icons/LoaderIcon";
|
||||
|
||||
export interface PixelStreamingWrapperProps {
|
||||
initialSettings?: Partial<AllSettings>;
|
||||
onVideoInitialized?: () => void;
|
||||
}
|
||||
|
||||
export const PixelStreamingWrapper = ({
|
||||
initialSettings,
|
||||
onVideoInitialized,
|
||||
}: PixelStreamingWrapperProps) => {
|
||||
// A reference to parent div element that the Pixel Streaming library attaches into:
|
||||
const videoParent = useRef<HTMLDivElement>(null);
|
||||
@@ -24,10 +27,10 @@ export const PixelStreamingWrapper = ({
|
||||
const [pixelStreaming, setPixelStreaming] = useState<PixelStreaming>();
|
||||
|
||||
// A boolean state variable that determines if the Click to play overlay is shown:
|
||||
const [clickToPlayVisible, setClickToPlayVisible] = useState<boolean>(false);
|
||||
const [videoInitialized, setVideoInitialized] = useState<boolean>(false);
|
||||
const videoInitializedRef = useRef<boolean>();
|
||||
videoInitializedRef.current = videoInitialized;
|
||||
const [clickToPlayVisible, setClickToPlayVisible] = useState(false);
|
||||
|
||||
const [latencyTestResult, setLatencyTestResult] =
|
||||
useState<LatencyTestResults>();
|
||||
|
||||
// Run on component mount:
|
||||
useEffect(() => {
|
||||
@@ -38,132 +41,93 @@ export const PixelStreamingWrapper = ({
|
||||
videoElementParent: videoParent.current,
|
||||
});
|
||||
|
||||
streaming.addEventListener("videoInitialized", () => {
|
||||
setVideoInitialized(true);
|
||||
});
|
||||
|
||||
// register a playStreamRejected handler to show Click to play overlay if needed:
|
||||
streaming.addEventListener("playStreamRejected", () => {
|
||||
setClickToPlayVisible(true);
|
||||
});
|
||||
|
||||
streaming.addEventListener("videoInitialized", () => {
|
||||
onVideoInitialized && onVideoInitialized();
|
||||
});
|
||||
|
||||
streaming.addEventListener("latencyTestResult", (e) => {
|
||||
setLatencyTestResult(e.data.latencyTimings);
|
||||
// console.log("Data", e.data.latencyTimings);
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
streaming.requestLatencyTest();
|
||||
}, 500);
|
||||
|
||||
// Save the library instance into component state so that it can be accessed later:
|
||||
setPixelStreaming(streaming);
|
||||
|
||||
document.getElementById("hiddenInput")?.remove();
|
||||
document.getElementById("editTextButton")?.remove();
|
||||
|
||||
setTimeout(() => {
|
||||
if (!videoInitializedRef.current) {
|
||||
window.location.reload();
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
// Clean up on component unmount:
|
||||
return () => {
|
||||
try {
|
||||
streaming.disconnect();
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
} catch {}
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="relative w-screen h-screen">
|
||||
<div className="w-full h-[100svh]" ref={videoParent} />
|
||||
{!videoInitialized && (
|
||||
<div className="absolute top-0 left-0 w-full h-full flex justify-center items-center gap-4">
|
||||
<LoaderIcon className="animate-spin w-8 h-8" />
|
||||
<Trans i18nKey="streamBuffering">Буферизация потока</Trans>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
ref={videoParent}
|
||||
/>
|
||||
{clickToPlayVisible && (
|
||||
<div className="absolute top-0 left-0 w-full h-full flex justify-center items-center z-10 bg-[#131317]">
|
||||
<div className="flex flex-col justify-center items-center w-[400px] p-10 space-y-10 rounded-lg">
|
||||
<div className="space-y-4 text-center">
|
||||
<p className="text-4xl font-gilroy">
|
||||
<Trans i18nKey="demoStarted">Демонстрация начата</Trans>
|
||||
</p>
|
||||
<p className="text-[#C5C7CE]">
|
||||
<Trans i18nKey="clickToContinue">
|
||||
Нажмите, чтобы продолжить
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
pixelStreaming?.play();
|
||||
setClickToPlayVisible(false);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="88"
|
||||
height="88"
|
||||
viewBox="0 0 88 88"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g filter="url(#filter0_b_0_1121)">
|
||||
<path
|
||||
d="M55.6667 43.9999L34.6668 57.9999L34.6668 30L55.6667 43.9999Z"
|
||||
fill="#F2F2F2"
|
||||
stroke="#F2F2F2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="87"
|
||||
height="87"
|
||||
rx="43.5"
|
||||
stroke="url(#paint0_linear_0_1121)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
id="filter0_b_0_1121"
|
||||
x="-20"
|
||||
y="-20"
|
||||
width="128"
|
||||
height="128"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feGaussianBlur in="BackgroundImageFix" stdDeviation="10" />
|
||||
<feComposite
|
||||
in2="SourceAlpha"
|
||||
operator="in"
|
||||
result="effect1_backgroundBlur_0_1121"
|
||||
/>
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="effect1_backgroundBlur_0_1121"
|
||||
result="shape"
|
||||
/>
|
||||
</filter>
|
||||
<linearGradient
|
||||
id="paint0_linear_0_1121"
|
||||
x1="88"
|
||||
y1="-2.6226e-06"
|
||||
x2="2.6226e-06"
|
||||
y2="88"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#BC75FF" />
|
||||
<stop offset="1" stopColor="#798FFF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
cursor: "pointer",
|
||||
color: "#fff",
|
||||
}}
|
||||
onClick={() => {
|
||||
pixelStreaming?.play();
|
||||
setClickToPlayVisible(false);
|
||||
}}
|
||||
>
|
||||
<div>Click to play</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="absolute bottom-0 left-0 p-4 text-white text-xs bg-black">
|
||||
{latencyTestResult &&
|
||||
Object.entries(latencyTestResult).map(([key, value], i) => {
|
||||
if (
|
||||
[
|
||||
"EncodeMs",
|
||||
"CaptureToSendMs",
|
||||
"latencyExcludingDecode",
|
||||
"networkLatency",
|
||||
"testStartTimeMs",
|
||||
].includes(key)
|
||||
)
|
||||
return (
|
||||
<div key={i}>
|
||||
{key}: {value}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
function AttachIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M19 5V10M19 5H14M19 5L14 10"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.2502 5L8.00015 5C6.34331 5 5.00018 6.34312 5.00015 7.99996L5.00004 16C5.00002 17.6568 6.34317 19 8.00004 19H16C17.6569 19 19 17.6569 19 16V13.75"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default AttachIcon;
|
||||
@@ -1,17 +1,17 @@
|
||||
interface IconProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function ChatIcon({ className }: IconProps) {
|
||||
function ChatIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className={className}
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M4.913 2.658c2.075-.27 4.19-.408 6.337-.408 2.147 0 4.262.139 6.337.408 1.922.25 3.291 1.861 3.405 3.727a4.403 4.403 0 00-1.032-.211 50.89 50.89 0 00-8.42 0c-2.358.196-4.04 2.19-4.04 4.434v4.286a4.47 4.47 0 002.433 3.984L7.28 21.53A.75.75 0 016 21v-4.03a48.527 48.527 0 01-1.087-.128C2.905 16.58 1.5 14.833 1.5 12.862V6.638c0-1.97 1.405-3.718 3.413-3.979z" />
|
||||
<path d="M15.75 7.5c-1.376 0-2.739.057-4.086.169C10.124 7.797 9 9.103 9 10.609v4.285c0 1.507 1.128 2.814 2.67 2.94 1.243.102 2.5.157 3.768.165l2.782 2.781a.75.75 0 001.28-.53v-2.39l.33-.026c1.542-.125 2.67-1.433 2.67-2.94v-4.286c0-1.505-1.125-2.811-2.664-2.94A49.392 49.392 0 0015.75 7.5z" />
|
||||
<path
|
||||
d="M17 6H6C4.89543 6 4 6.89543 4 8V15C4 16.1046 4.89543 17 6 17H11L14.3753 19.7002C15.0301 20.2241 16 19.7579 16 18.9194V17H17C18.1046 17 19 16.1046 19 15V8C19 6.89543 18.1046 6 17 6Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,16 +7,13 @@ function CloseIcon() {
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/Close">
|
||||
<path
|
||||
id="Vector 106"
|
||||
d="M12.0002 11.9999L17.6572 6.34331M12.0002 11.9999L6.34337 6.34302M12.0002 11.9999L17.6571 17.6567M12.0002 11.9999L6.34326 17.6568"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M12 12L17 7.00026M12 12L7.00009 7M12 12L16.9999 16.9999M12 12L7 17"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
function DetachIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect
|
||||
x="5"
|
||||
y="5"
|
||||
width="14"
|
||||
height="14"
|
||||
rx="3"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<path
|
||||
d="M5 8C5 6.34315 6.34315 5 8 5H16C17.6569 5 19 6.34315 19 8V9H5V8Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetachIcon;
|
||||
@@ -7,16 +7,20 @@ function FullscreenIcon() {
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/OpenFullscreen">
|
||||
<path
|
||||
id="expand"
|
||||
d="M9 20H4M4 20L4 15M4 20L9 15M15 9L20 4M19.997 9V4L15 4"
|
||||
stroke="#F2F2F2"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M4 20V14M4 20H10M4 20L10 14"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M20 4V10M20 4H14M20 4L14 10"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
function GearIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.59372 3.94C9.68372 3.398 10.1537 3 10.7037 3H13.2967C13.8467 3 14.3167 3.398 14.4067 3.94L14.6197 5.221C14.6827 5.595 14.9327 5.907 15.2647 6.091C15.3387 6.131 15.4117 6.174 15.4847 6.218C15.8087 6.414 16.2047 6.475 16.5597 6.342L17.7767 5.886C18.0261 5.79221 18.3007 5.78998 18.5516 5.87971C18.8025 5.96945 19.0134 6.14531 19.1467 6.376L20.4427 8.623C20.5758 8.8537 20.6227 9.12413 20.5751 9.38617C20.5275 9.6482 20.3884 9.88485 20.1827 10.054L19.1797 10.881C18.8867 11.121 18.7417 11.494 18.7487 11.873C18.7503 11.958 18.7503 12.043 18.7487 12.128C18.7417 12.506 18.8867 12.878 19.1787 13.118L20.1837 13.946C20.6077 14.296 20.7177 14.9 20.4437 15.376L19.1457 17.623C19.0126 17.8536 18.8019 18.0296 18.5512 18.1195C18.3005 18.2094 18.0261 18.2074 17.7767 18.114L16.5597 17.658C16.2047 17.525 15.8097 17.586 15.4837 17.782C15.4112 17.8261 15.3379 17.8688 15.2637 17.91C14.9327 18.093 14.6827 18.405 14.6197 18.779L14.4067 20.059C14.3167 20.602 13.8467 21 13.2967 21H10.7027C10.1527 21 9.68272 20.602 9.59272 20.06L9.37972 18.779C9.31772 18.405 9.06772 18.093 8.73572 17.909C8.66157 17.8681 8.58822 17.8258 8.51572 17.782C8.19072 17.586 7.79572 17.525 7.43972 17.658L6.22272 18.114C5.97345 18.2075 5.69908 18.2096 5.44842 18.1199C5.19775 18.0302 4.98703 17.8545 4.85372 17.624L3.55672 15.377C3.42366 15.1463 3.37676 14.8759 3.42437 14.6138C3.47198 14.3518 3.61101 14.1152 3.81672 13.946L4.82072 13.119C5.11272 12.879 5.25772 12.506 5.25072 12.127C5.24915 12.042 5.24915 11.957 5.25072 11.872C5.25772 11.494 5.11272 11.122 4.82072 10.882L3.81672 10.054C3.61126 9.88489 3.47239 9.64843 3.42479 9.38662C3.37719 9.12481 3.42393 8.8546 3.55672 8.624L4.85372 6.377C4.9869 6.14614 5.19773 5.97006 5.44863 5.88014C5.69953 5.79021 5.97421 5.79229 6.22372 5.886L7.43972 6.342C7.79572 6.475 8.19072 6.414 8.51572 6.218C8.58772 6.174 8.66172 6.131 8.73572 6.09C9.06772 5.907 9.31772 5.595 9.37972 5.221L9.59372 3.94Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M15 12C15 12.7956 14.6839 13.5587 14.1213 14.1213C13.5587 14.6839 12.7956 15 12 15C11.2044 15 10.4413 14.6839 9.87868 14.1213C9.31607 13.5587 9 12.7956 9 12C9 11.2044 9.31607 10.4413 9.87868 9.87868C10.4413 9.31607 11.2044 9 12 9C12.7956 9 13.5587 9.31607 14.1213 9.87868C14.6839 10.4413 15 11.2044 15 12Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default GearIcon;
|
||||
File diff suppressed because one or more lines are too long
@@ -7,25 +7,21 @@ function MicroOffIcon() {
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/MicroOff">
|
||||
<path
|
||||
id="Subtract"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M9 11.9387V12.4C9 13.2837 9.71634 14 10.6 14H11.0613L9 11.9387ZM13.0613 16H10C8.34315 16 7 14.6569 7 13V12C7 11.4477 6.55228 11 6 11C5.44772 11 5 11.4477 5 12V13C5 15.7614 7.23858 18 10 18H11V19.9998H9.6C9.04772 19.9998 8.6 20.4475 8.6 20.9998C8.6 21.5521 9.04772 21.9998 9.6 21.9998H14.4C14.9523 21.9998 15.4 21.5521 15.4 20.9998C15.4 20.4475 14.9523 19.9998 14.4 19.9998H13V18H14C14.3311 18 14.6547 17.9678 14.9678 17.9064L13.0613 16ZM18.37 15.4315L16.8568 13.9184C16.9498 13.6289 17 13.3203 17 13V12C17 11.4477 17.4477 11 18 11C18.5523 11 19 11.4477 19 12V13C19 13.8826 18.7713 14.7118 18.37 15.4315ZM15 12.0615L9 6.06152V4.1C9 3.21634 9.71634 2.5 10.6 2.5H13.4C14.2837 2.5 15 3.21634 15 4.1V12.0615Z"
|
||||
fill="#F2F2F2"
|
||||
/>
|
||||
<rect
|
||||
id="Rectangle 1100"
|
||||
x="3.41406"
|
||||
y="2"
|
||||
width="26.2811"
|
||||
height="2"
|
||||
rx="1"
|
||||
transform="rotate(45 3.41406 2)"
|
||||
fill="#E94444"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M8.25 11.3407V12.6429C8.25 13.5643 8.67801 14.4198 9.39059 15.0306C10.0996 15.6383 11.0396 15.9643 12 15.9643C12.2691 15.9643 12.5366 15.9387 12.7978 15.8885L11.2757 14.3664C10.9294 14.2701 10.6173 14.1064 10.3668 13.8917C10.0204 13.5948 9.821 13.2283 9.76584 12.8565L8.25 11.3407ZM14.25 11.16V5.57143C14.25 5.12894 14.0459 4.67629 13.6332 4.3226C13.217 3.96584 12.6309 3.75 12 3.75C11.3691 3.75 10.783 3.96584 10.3668 4.3226C9.95413 4.67629 9.75 5.12894 9.75 5.57143V6.65997L8.27524 5.18521C8.37666 4.41304 8.77871 3.70817 9.39059 3.18371C10.0996 2.576 11.0396 2.25 12 2.25C12.9604 2.25 13.9004 2.576 14.6094 3.18371C15.322 3.79449 15.75 4.64994 15.75 5.57143V12.6429C15.75 12.6485 15.75 12.6542 15.75 12.6599L14.25 11.16ZM13.7058 16.7965C13.1641 16.9528 12.5886 17.0357 12 17.0357C10.5735 17.0357 9.22428 16.549 8.24545 15.71C7.2702 14.874 6.75 13.7673 6.75 12.6429V11.3571C6.75 10.9429 6.41421 10.6071 6 10.6071C5.58579 10.6071 5.25 10.9429 5.25 11.3571V12.6429C5.25 14.2463 5.99408 15.7558 7.26927 16.8489C8.35879 17.7827 9.76505 18.3547 11.25 18.4993V20.25H8.25C7.83579 20.25 7.5 20.5858 7.5 21C7.5 21.4142 7.83579 21.75 8.25 21.75H12H15.75C16.1642 21.75 16.5 21.4142 16.5 21C16.5 20.5858 16.1642 20.25 15.75 20.25H12.75V18.4993C13.4901 18.4272 14.2106 18.249 14.8821 17.9728L13.7058 16.7965ZM18.1549 15.0648L17.0189 13.9289C17.1717 13.5127 17.25 13.0791 17.25 12.6429V11.3571C17.25 10.9429 17.5858 10.6071 18 10.6071C18.4142 10.6071 18.75 10.9429 18.75 11.3571V12.6429C18.75 13.4926 18.5411 14.3159 18.1549 15.0648Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<rect
|
||||
x="6"
|
||||
y="4.58618"
|
||||
width="18.9662"
|
||||
height="2"
|
||||
rx="1"
|
||||
transform="rotate(45 6 4.58618)"
|
||||
fill="#E94444"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,18 +7,13 @@ function MicroOnIcon() {
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/MicroOn">
|
||||
<g id="Vector">
|
||||
<path
|
||||
d="M10.6 2.5H13.4C14.2837 2.5 15 3.21634 15 4.1V12.4C15 13.2837 14.2837 14 13.4 14H10.6C9.71634 14 9 13.2837 9 12.4V4.1C9 3.21634 9.71634 2.5 10.6 2.5Z"
|
||||
fill="#F2F2F2"
|
||||
/>
|
||||
<path
|
||||
d="M10 16C8.34315 16 7 14.6569 7 13V12C7 11.4477 6.55228 11 6 11C5.44772 11 5 11.4477 5 12V13C5 15.7614 7.23858 18 10 18H11V19.9998H9.6C9.04772 19.9998 8.6 20.4475 8.6 20.9998C8.6 21.5521 9.04772 21.9998 9.6 21.9998H14.4C14.9523 21.9998 15.4 21.5521 15.4 20.9998C15.4 20.4475 14.9523 19.9998 14.4 19.9998H13V18H14C16.7614 18 19 15.7614 19 13V12C19 11.4477 18.5523 11 18 11C17.4477 11 17 11.4477 17 12V13C17 14.6569 15.6569 16 14 16H10Z"
|
||||
fill="#F2F2F2"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
d="M12 17.7857C13.5913 17.7857 15.1174 17.2439 16.2426 16.2794C17.3679 15.3149 18 14.0068 18 12.6429V11.3571M12 17.7857C10.4087 17.7857 8.88258 17.2439 7.75736 16.2794C6.63214 15.3149 6 14.0068 6 12.6429V11.3571M12 17.7857V21M8.25 21H15.75M12 15.2143C11.2044 15.2143 10.4413 14.9434 9.87868 14.4611C9.31607 13.9789 9 13.3248 9 12.6429V5.57143C9 4.88944 9.31607 4.23539 9.87868 3.75315C10.4413 3.27092 11.2044 3 12 3C12.7956 3 13.5587 3.27092 14.1213 3.75315C14.6839 4.23539 15 4.88944 15 5.57143V12.6429C15 13.3248 14.6839 13.9789 14.1213 14.4611C13.5587 14.9434 12.7956 15.2143 12 15.2143Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
function MoreIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="5" cy="12" r="1.5" fill="currentColor" />
|
||||
<circle cx="12" cy="12" r="1.5" fill="currentColor" />
|
||||
<circle cx="19" cy="12" r="1.5" fill="currentColor" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default MoreIcon;
|
||||
@@ -7,25 +7,18 @@ function PersonsIcon() {
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/Persons">
|
||||
<g id="user">
|
||||
<path
|
||||
d="M17.4495 8.63457C17.4495 10.6419 15.9051 12.7428 13.9999 12.7428C12.0948 12.7428 10.5504 10.6419 10.5504 8.63457C10.5504 6.62725 12.0948 5 13.9999 5C15.9051 5 17.4495 6.62725 17.4495 8.63457Z"
|
||||
fill="#F2F2F2"
|
||||
/>
|
||||
<path
|
||||
d="M14 19.9999C17.4171 19.9999 18.4866 20.0186 19.3999 19.4209C19.8272 19.1412 20.0379 18.6262 19.9944 18.0927C19.935 17.364 19.6934 16.6363 19.4474 16.0591C19.1244 15.3012 18.9916 14.9503 18.4873 14.416C17.2309 13.0848 15.7818 14.9671 14 14.9671C12.2182 14.9671 10.7691 13.0848 9.51271 14.416C9.00842 14.9503 8.87561 15.3012 8.55259 16.0591C8.30658 16.6363 8.06495 17.364 8.0056 18.0927C7.96214 18.6262 8.17276 19.1412 8.6001 19.4209C9.51336 20.0186 10.5829 19.9999 14 19.9999Z"
|
||||
fill="#F2F2F2"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
id="Subtract"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M10.8041 10.0751C11.7912 9.29544 12.4495 7.94506 12.4495 6.63457C12.4495 6.23357 12.3879 5.84773 12.274 5.48701C11.2435 6.11561 10.5503 7.28985 10.5503 8.63469C10.5503 9.11967 10.6405 9.61012 10.8041 10.0751ZM8.91837 10.7415C7.0509 10.6827 5.55037 8.61317 5.55037 6.63457C5.55037 4.62725 7.09478 3 8.99993 3C9.82378 3 10.5802 3.3043 11.1734 3.81204C9.57742 4.81661 8.55031 6.64022 8.55031 8.63469C8.55031 9.35547 8.68173 10.0702 8.91837 10.7415ZM6.0089 17.9735C4.79527 17.9257 4.16752 17.7923 3.6001 17.4209C3.17276 17.1412 2.96214 16.6262 3.0056 16.0927C3.06495 15.364 3.30658 14.6363 3.55259 14.0591L3.60475 13.9365C3.89151 13.2617 4.03642 12.9206 4.51271 12.416C5.27428 11.6091 6.10665 11.9829 7.041 12.4025C7.42296 12.574 7.82195 12.7532 8.24012 12.8625C8.17815 12.9202 8.11747 12.9805 8.05815 13.0434C7.33114 13.8137 7.06365 14.4459 6.77613 15.1255L6.71268 15.2751C6.42491 15.9502 6.09598 16.9012 6.01215 17.9304C6.01098 17.9447 6.0099 17.9591 6.0089 17.9735ZM14.7454 14.8664C14.8661 15.2559 14.9603 15.6741 14.9944 16.0927C15.0379 16.6262 14.8272 17.1412 14.3999 17.4209C13.5141 18.0007 12.4811 18.0005 9.30138 18L9 17.9999L8.69862 18C8.45849 18 8.2306 18 8.01409 17.9998C8.08546 17.3025 8.31698 16.6119 8.55254 16.0592L8.60469 15.9366C8.89145 15.2618 9.03637 14.9208 9.51265 14.4161C10.2742 13.6092 11.1066 13.983 12.0409 14.4026C12.648 14.6753 13.2982 14.9672 13.9999 14.9672C14.2551 14.9672 14.5035 14.9286 14.7454 14.8664ZM13.7595 12.7318C13.1417 12.6743 12.5687 12.3982 12.083 11.9825C12.579 11.877 13.0451 11.9475 13.4873 12.416C13.5925 12.5274 13.6815 12.6309 13.7595 12.7318Z"
|
||||
fill="#F2F2F2"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M11.5504 8.63457C11.5504 10.6419 13.0948 12.7428 14.9999 12.7428C16.9051 12.7428 18.4495 10.6419 18.4495 8.63457C18.4495 6.62725 16.9051 5 14.9999 5C13.0948 5 11.5504 6.62725 11.5504 8.63457ZM11.5504 8.63457C11.5504 7.28973 12.2436 6.11548 13.2741 5.48689M11.5504 8.63457C11.5504 9.11955 11.6405 9.61 11.8041 10.075M15 14.9671C16.7818 14.9671 18.2309 13.0848 19.4873 14.416C19.9916 14.9503 20.1244 15.3012 20.4474 16.0591C20.6934 16.6363 20.9351 17.364 20.9944 18.0927C21.0379 18.6262 20.8272 19.1412 20.3999 19.4209C19.4866 20.0186 18.4171 19.9999 15 19.9999C11.5829 19.9999 10.5134 20.0186 9.6001 19.4209C9.17276 19.1412 8.96214 18.6262 9.0056 18.0927C9.06495 17.364 9.30658 16.6363 9.55259 16.0591M15 14.9671C13.2182 14.9671 11.7691 13.0848 10.5127 14.416M15 14.9671C14.2982 14.9671 13.6481 14.6751 13.041 14.4025C12.1066 13.9829 11.2743 13.6091 10.5127 14.416M15 14.9671C15.2552 14.9671 15.5035 14.9285 15.7455 14.8662M10.5127 14.416C10.0084 14.9503 9.87561 15.3012 9.55259 16.0591M10.5127 14.416C10.0364 14.9206 9.8915 15.2617 9.60474 15.9365L9.55259 16.0591M9.55259 16.0591C9.31704 16.6118 9.08551 17.3024 9.01414 17.9997M14.7595 12.7316C14.1418 12.6741 13.5688 12.3981 13.083 11.9823"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.91278 7.66535C3.91278 9.13738 5.07109 10.6781 6.49994 10.6781C7.9288 10.6781 9.08712 9.13738 9.08712 7.66535C9.08712 6.19332 7.9288 5 6.49994 5C5.07109 5 3.91278 6.19332 3.91278 7.66535ZM3.91278 7.66535C3.91278 6.67914 4.43269 5.81802 5.20556 5.35705M3.91278 7.66535C3.91278 8.021 3.98039 8.38066 4.1031 8.72164M10.5856 14.11C10.3433 13.5542 10.2437 13.2969 9.86547 12.9051C8.92317 11.9288 7.83636 13.3092 6.5 13.3092M6.5 13.3092C5.16364 13.3092 4.07683 11.9288 3.13453 12.9051M6.5 13.3092C5.97368 13.3092 5.48607 13.0951 5.03075 12.8952C4.32998 12.5874 3.70571 12.3133 3.13453 12.9051M6.5 13.3092C6.69139 13.3092 6.87765 13.2809 7.05911 13.2353M3.13453 12.9051C2.75631 13.2969 2.65671 13.5542 2.41445 14.11M3.13453 12.9051C2.77732 13.2751 2.66862 13.5252 2.45355 14.0201L2.41445 14.11M2.41445 14.11C2.22993 14.5333 2.04871 15.067 2.0042 15.6013C1.9716 15.9925 2.12957 16.3702 2.45008 16.5753C3.13502 17.0137 3.9372 17 6.5 17C7.68428 17 8.4926 17.0029 9.08712 16.9628M2.41445 14.11C2.23778 14.5153 2.06413 15.0218 2.01061 15.5331M6.31964 10.6699C5.85634 10.6277 5.42659 10.4253 5.06226 10.1204"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,17 +7,13 @@ function ShareIcon() {
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="Icon/Share">
|
||||
<path
|
||||
id="Vector 1835"
|
||||
d="M16 18L5 12L16 6"
|
||||
stroke="#F2F2F2"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
<circle id="Ellipse 226" cx="5" cy="12" r="3" fill="#F2F2F2" />
|
||||
<circle id="Ellipse 227" cx="16" cy="6" r="3" fill="#F2F2F2" />
|
||||
<circle id="Ellipse 228" cx="16" cy="18" r="3" fill="#F2F2F2" />
|
||||
</g>
|
||||
<path
|
||||
d="M7.57375 11.305C7.36853 10.9357 7.04651 10.6451 6.65823 10.4786C6.26995 10.3122 5.83739 10.2794 5.42845 10.3854C5.01952 10.4914 4.65736 10.7302 4.39882 11.0643C4.14028 11.3984 4 11.8089 4 12.2313C4 12.6538 4.14028 13.0643 4.39882 13.3984C4.65736 13.7325 5.01952 13.9713 5.42845 14.0773C5.83739 14.1833 6.26995 14.1505 6.65823 13.984C7.04651 13.8176 7.36853 13.5269 7.57375 13.1577M7.57375 11.305C7.7263 11.5796 7.8136 11.8949 7.8136 12.2313C7.8136 12.5678 7.7263 12.8839 7.57375 13.1577M7.57375 11.305L15.6812 6.80124M7.57375 13.1577L15.6812 17.6614M15.6812 6.80124C15.8 7.0251 15.9624 7.22302 16.1586 7.38343C16.3549 7.54383 16.5811 7.6635 16.8242 7.73545C17.0672 7.80739 17.3221 7.83015 17.5741 7.80241C17.826 7.77468 18.0699 7.69699 18.2915 7.57389C18.5131 7.4508 18.7079 7.28477 18.8645 7.08551C19.0212 6.88625 19.1365 6.65776 19.2038 6.4134C19.2712 6.16903 19.2891 5.9137 19.2566 5.66233C19.2241 5.41096 19.1418 5.16859 19.0145 4.94939C18.7637 4.51745 18.3534 4.20115 17.8719 4.06849C17.3903 3.93583 16.8759 3.99741 16.4393 4.23998C16.0027 4.48255 15.6787 4.88675 15.5369 5.36569C15.3951 5.84464 15.4469 6.36009 15.6812 6.80124ZM15.6812 17.6614C15.5595 17.8804 15.4822 18.1212 15.4536 18.3701C15.425 18.6189 15.4457 18.871 15.5146 19.1119C15.5834 19.3527 15.699 19.5777 15.8547 19.7738C16.0105 19.97 16.2034 20.1336 16.4223 20.2553C16.6413 20.3769 16.8821 20.4543 17.131 20.4828C17.3798 20.5114 17.6319 20.4907 17.8728 20.4219C18.1136 20.3531 18.3386 20.2375 18.5348 20.0817C18.7309 19.926 18.8945 19.7331 19.0162 19.5141C19.2619 19.0719 19.3218 18.5501 19.1828 18.0637C19.0438 17.5773 18.7173 17.1659 18.275 16.9203C17.8328 16.6746 17.311 16.6146 16.8246 16.7536C16.3382 16.8926 15.9269 17.2192 15.6812 17.6614Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
function WindowIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10 14V20M10 14H4M10 14L4 20"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14 10V4M14 10H20M14 10L20 4"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default WindowIcon;
|
||||
@@ -0,0 +1,67 @@
|
||||
interface ButtonProps {
|
||||
type?: "button" | "reset" | "submit";
|
||||
variant?: "primary" | "secondary" | "tertiary";
|
||||
fullWidth?: boolean;
|
||||
large?: boolean;
|
||||
icon?: JSX.Element;
|
||||
onlyIcon?: boolean;
|
||||
disabled?: boolean;
|
||||
children?: React.ReactNode;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
function Button({
|
||||
type = "button",
|
||||
variant = "primary",
|
||||
fullWidth,
|
||||
large,
|
||||
icon,
|
||||
onlyIcon,
|
||||
disabled,
|
||||
children,
|
||||
onClick,
|
||||
}: ButtonProps) {
|
||||
const variantClasses = [
|
||||
{
|
||||
name: "primary",
|
||||
classes:
|
||||
"enabled:bg-[#49A1F5] enabled:hover:bg-[#4190DB] enabled:text-white disabled:bg-[#F2F2F2] disabled:text-[#CCCCCC]",
|
||||
},
|
||||
{
|
||||
name: "secondary",
|
||||
classes:
|
||||
"enabled:bg-[#F0F1F2] enabled:hover:bg-[#E6ECF2] enabled:text-[#77828C] disabled:bg-[#F2F2F2] disabled:text-[##CCCCCC]",
|
||||
},
|
||||
{
|
||||
name: "tertiary",
|
||||
classes:
|
||||
"enabled:hover:bg-[#E6ECF2] enabled:text-[#77828C] disabled:text-[#CCCCCC]",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<button
|
||||
disabled={disabled}
|
||||
type={type}
|
||||
className={`rounded-lg text-sm font-semibold transition-colors flex items-center justify-center gap-1 ${
|
||||
variantClasses.find((item) => item.name === variant)?.classes
|
||||
} ${fullWidth ? "w-full" : "w-fit"} ${
|
||||
large
|
||||
? `h-10 ${onlyIcon ? "p-2" : icon ? "pl-4 pr-6" : "px-4"}`
|
||||
: `h-8 ${onlyIcon ? "p-1" : icon ? "pl-2 pr-4" : "px-6"}`
|
||||
} `}
|
||||
onClick={onClick}
|
||||
>
|
||||
{onlyIcon ? (
|
||||
icon
|
||||
) : (
|
||||
<>
|
||||
{icon}
|
||||
{children}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default Button;
|
||||
+4
-2
@@ -8,22 +8,24 @@ import {
|
||||
import "./index.css";
|
||||
import "./i18n";
|
||||
import App from "./App";
|
||||
import StreamPage from "./StreamPage";
|
||||
import HistoryPage from "./HistoryPage";
|
||||
import ScheduledPage from "./ScheduledPage";
|
||||
import CalendarPage from "./CalendarPage";
|
||||
import useAuthStore from "./stores/useAuthStore";
|
||||
import PersonalAreaLoginPage from "./PersonalAreaLoginPage";
|
||||
import PersonalAreaDashboardPage from "./PersonalAreaDashboardPage";
|
||||
import StreamPage2 from "./pages/StreamPage2";
|
||||
import ErrorBoundary from "./ErrorBoundary";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
element: <App />,
|
||||
errorElement: <ErrorBoundary />,
|
||||
},
|
||||
{
|
||||
path: "/stream/:id",
|
||||
element: <StreamPage />,
|
||||
element: <StreamPage2 />,
|
||||
},
|
||||
{
|
||||
path: "/history",
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
.entering {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.entered {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.exiting {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.exited {
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import "./StreamPage2.css";
|
||||
import ky from "ky";
|
||||
import { PixelStreamingWrapper } from "../components/PixelStreamingWrapper";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { FormEvent, useEffect, useRef, useState } from "react";
|
||||
import { Transition } from "react-transition-group";
|
||||
import Button from "../components/ui/Button";
|
||||
import CloseIcon from "../components/icons/CloseIcon";
|
||||
import HandOffIcon from "../components/icons/HandOffIcon";
|
||||
import MicroOffIcon from "../components/icons/MicroOffIcon";
|
||||
import PersonsIcon from "../components/icons/PersonsIcon";
|
||||
import MicroOnIcon from "../components/icons/MicroOnIcon";
|
||||
import MoreIcon from "../components/icons/MoreIcon";
|
||||
import FullscreenIcon from "../components/icons/FullscreenIcon";
|
||||
import WindowIcon from "../components/icons/WindowIcon";
|
||||
import ChatIcon from "../components/icons/ChatIcon";
|
||||
import ShareIcon from "../components/icons/ShareIcon";
|
||||
import GearIcon from "../components/icons/GearIcon";
|
||||
import { useFullscreen } from "ahooks";
|
||||
import AttachIcon from "../components/icons/AttachIcon";
|
||||
import DetachIcon from "../components/icons/DetachIcon";
|
||||
import Draggable from "react-draggable";
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
function StreamPage2() {
|
||||
const params = useParams();
|
||||
const [wsUrl, setWsUrl] = useState<string>();
|
||||
const [username, setUsername] = useState<string>("");
|
||||
const [step, setStep] = useState<number>(1);
|
||||
const usernameRef = useRef<HTMLInputElement>(null!);
|
||||
const [users, setUsers] = useState<User[]>([
|
||||
{
|
||||
id: "1",
|
||||
username: "User 1",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
username: "User 2",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
username: "User 3",
|
||||
},
|
||||
{
|
||||
id: "4",
|
||||
username: "User 3",
|
||||
},
|
||||
{
|
||||
id: "1",
|
||||
username: "User 1",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
username: "User 2",
|
||||
},
|
||||
]);
|
||||
const [isShowUsers, setIsShowUsers] = useState<boolean>(false);
|
||||
const [isShowChat, setIsShowChat] = useState<boolean>(false);
|
||||
const [isMicEnabled, setIsMicEnabled] = useState<boolean>(false);
|
||||
const [isVideoInitialized, setIsVideoInitialized] = useState<boolean>(false);
|
||||
const ref = useRef(null);
|
||||
const [isFullscreen, { toggleFullscreen }] = useFullscreen(ref);
|
||||
const [isUsersDetached, setIsUsersDetached] = useState<boolean>(false);
|
||||
const [isChatDetached, setIsChatDetached] = useState<boolean>(false);
|
||||
|
||||
async function getWsUrl() {
|
||||
const activeSession: any = await ky
|
||||
.get(`${import.meta.env.VITE_COORD_URL}/active_sessions/${params.id}`)
|
||||
.json();
|
||||
|
||||
setWsUrl(
|
||||
`wss://${activeSession.location}.sess.stream.graff.tech/${activeSession.server}/${activeSession.cirrusPort}/`
|
||||
);
|
||||
}
|
||||
|
||||
function handleSetUsername(e: FormEvent) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!username) {
|
||||
usernameRef.current.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
setStep(2);
|
||||
}
|
||||
|
||||
function setUsernameGuest() {
|
||||
setUsername("Гость");
|
||||
setStep(2);
|
||||
}
|
||||
|
||||
function allowMic() {
|
||||
setStep(3);
|
||||
}
|
||||
|
||||
function disallowMic() {
|
||||
setStep(3);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!username) return;
|
||||
|
||||
setUsername(() => username.trim());
|
||||
}, [username]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isChatDetached || isUsersDetached) {
|
||||
document.body.classList.add("overflow-hidden");
|
||||
} else {
|
||||
document.body.classList.remove("overflow-hidden");
|
||||
}
|
||||
}, [isUsersDetached, isChatDetached]);
|
||||
|
||||
useEffect(() => {
|
||||
getWsUrl();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={ref} className="">
|
||||
<div className="h-screen flex flex-col">
|
||||
<div className="h-12 bg-white px-6 py-3 flex items-center justify-between">
|
||||
<div id="left" className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-6 w-6 flex items-center justify-center bg-[#E6ECF2] font-semibold text-[10px] rounded-full">
|
||||
{username && username[0].toUpperCase()}
|
||||
</div>
|
||||
<p className="text-xs">{username}</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="secondary" icon={<HandOffIcon />} onlyIcon />
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon={isMicEnabled ? <MicroOnIcon /> : <MicroOffIcon />}
|
||||
onlyIcon
|
||||
onClick={() => setIsMicEnabled((prev) => !prev)}
|
||||
/>
|
||||
</div>
|
||||
<div id="divider" className="w-px h-4 bg-[#DAE0E5]"></div>
|
||||
<div className="flex gap-6">
|
||||
{users.map(
|
||||
(user, index) =>
|
||||
index < 3 && (
|
||||
<div key={user.id} className="flex items-center gap-2">
|
||||
<div className="h-6 w-6 flex items-center justify-center bg-[#E6ECF2] font-semibold text-[10px] rounded-full">
|
||||
{user.username && user.username[0].toUpperCase()}
|
||||
</div>
|
||||
<p className="text-xs">{user.username}</p>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
{users.length > 3 && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon={<PersonsIcon />}
|
||||
onlyIcon
|
||||
onClick={() => setIsShowUsers((prev) => !prev)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="right" className="flex items-center gap-4">
|
||||
<div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon={<ChatIcon />}
|
||||
onlyIcon
|
||||
onClick={() => setIsShowChat((prev) => !prev)}
|
||||
/>
|
||||
</div>
|
||||
<div id="divider" className="w-px h-4 bg-[#DAE0E5]"></div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon={isFullscreen ? <WindowIcon /> : <FullscreenIcon />}
|
||||
onlyIcon
|
||||
onClick={toggleFullscreen}
|
||||
/>
|
||||
<Button variant="secondary" icon={<ShareIcon />} onlyIcon />
|
||||
<Button variant="secondary" icon={<GearIcon />} onlyIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 flex">
|
||||
<div className="w-full">
|
||||
{wsUrl && (
|
||||
<PixelStreamingWrapper
|
||||
initialSettings={{
|
||||
AutoPlayVideo: true,
|
||||
AutoConnect: true,
|
||||
ss: wsUrl,
|
||||
StartVideoMuted: true,
|
||||
HoveringMouse: true,
|
||||
WaitForStreamer: true,
|
||||
}}
|
||||
onVideoInitialized={() => setIsVideoInitialized(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col bg-white max-h-[calc(100vh-48px)]">
|
||||
<Draggable
|
||||
position={!isUsersDetached ? { x: 0, y: 0 } : undefined}
|
||||
disabled={!isUsersDetached}
|
||||
>
|
||||
<div
|
||||
id="modal-users"
|
||||
className={`max-h-[calc(100vh-48px)] ${
|
||||
isUsersDetached ? "absolute right-0 p-4" : ""
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`${
|
||||
isUsersDetached ? "rounded-lg" : "h-full"
|
||||
} bg-white flex flex-col ${
|
||||
isShowUsers ? "w-[296px]" : "w-0 hidden"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`flex items-center justify-between p-2 pl-4 ${
|
||||
isUsersDetached ? "border-b" : ""
|
||||
}`}
|
||||
>
|
||||
<p className="font-semibold">Участники</p>
|
||||
<div className="flex">
|
||||
<Button
|
||||
variant="tertiary"
|
||||
icon={isUsersDetached ? <AttachIcon /> : <DetachIcon />}
|
||||
onlyIcon
|
||||
onClick={() => setIsUsersDetached((prev) => !prev)}
|
||||
/>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
icon={<CloseIcon />}
|
||||
onlyIcon
|
||||
onClick={() => setIsShowUsers(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 p-4">
|
||||
{users.map((user) => (
|
||||
<div className="flex justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 bg-[#E6ECF2] rounded-full flex items-center justify-center">
|
||||
<p className="font-medium text-[10px] select-none">
|
||||
{user.username[0].toUpperCase()}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-sm">{user.username}</p>
|
||||
</div>
|
||||
<div className="">
|
||||
<Button
|
||||
variant="tertiary"
|
||||
icon={<MoreIcon />}
|
||||
onlyIcon
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Draggable>
|
||||
|
||||
<Draggable
|
||||
position={!isChatDetached ? { x: 0, y: 0 } : undefined}
|
||||
disabled={!isChatDetached}
|
||||
>
|
||||
<div
|
||||
id="modal-chat"
|
||||
className={`max-h-[calc(100vh-48px)] ${
|
||||
isChatDetached
|
||||
? "absolute right-[346px] p-4 h-fit"
|
||||
: "h-full"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`${
|
||||
isChatDetached ? "rounded-lg" : "h-full"
|
||||
} bg-white flex flex-col ${
|
||||
isShowChat ? "w-[296px]" : "w-0 hidden"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`flex items-center justify-between p-2 pl-4 ${
|
||||
isChatDetached ? "border-b" : ""
|
||||
}`}
|
||||
>
|
||||
<p className="font-semibold">Чат</p>
|
||||
<div className="flex">
|
||||
<Button
|
||||
variant="tertiary"
|
||||
icon={isChatDetached ? <AttachIcon /> : <DetachIcon />}
|
||||
onlyIcon
|
||||
onClick={() => setIsChatDetached((prev) => !prev)}
|
||||
/>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
icon={<CloseIcon />}
|
||||
onlyIcon
|
||||
onClick={() => setIsShowChat(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 p-4">//</div>
|
||||
</div>
|
||||
</div>
|
||||
</Draggable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
in={step !== 3 || !isVideoInitialized}
|
||||
timeout={300}
|
||||
mountOnEnter
|
||||
unmountOnExit
|
||||
>
|
||||
{(state) => (
|
||||
<div
|
||||
className={`absolute top-0 left-0 w-full h-full flex items-center justify-center bg-[#14161F] transition-all ${state} ${
|
||||
step !== 3 || !isVideoInitialized
|
||||
? "bg-opacity-50 backdrop-blur-2xl"
|
||||
: "bg-opacity-0 backdrop-blur-none"
|
||||
} `}
|
||||
>
|
||||
<Transition
|
||||
in={step === 1}
|
||||
timeout={300}
|
||||
mountOnEnter
|
||||
unmountOnExit
|
||||
>
|
||||
{(state) => (
|
||||
<form
|
||||
className={`absolute bg-white rounded-lg p-12 w-[396px] transition-opacity ${state}`}
|
||||
onSubmit={handleSetUsername}
|
||||
>
|
||||
<div className="mb-6">
|
||||
<p className="text-2xl font-semibold">Здравствуйте!</p>
|
||||
</div>
|
||||
<div className="mb-6 flex flex-col gap-2">
|
||||
<p className="font-semibold">Представьтесь, пожалуйста</p>
|
||||
<p className="text-[#77828C] text-sm">
|
||||
Так мы будем знать, как к вам обратиться
|
||||
</p>
|
||||
</div>
|
||||
<div className="mb-10">
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="text-[#77828C] text-xs">Имя</label>
|
||||
<input
|
||||
ref={usernameRef}
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
autoFocus
|
||||
className="rounded-lg px-2 py-2.5 bg-white border border-[#DAE0E5] hover:border-[#49A1F5] focus:border-[#49A1F5] outline-none transition-colors text-sm"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
fullWidth
|
||||
large
|
||||
onClick={setUsernameGuest}
|
||||
>
|
||||
Не указывать
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
fullWidth
|
||||
large
|
||||
onClick={void handleSetUsername}
|
||||
>
|
||||
Продолжить
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
in={step === 2}
|
||||
timeout={300}
|
||||
mountOnEnter
|
||||
unmountOnExit
|
||||
>
|
||||
{(state) => (
|
||||
<div
|
||||
className={`absolute bg-white rounded-lg p-12 w-[396px] transition-opacity ${state}`}
|
||||
>
|
||||
<div className="mb-6">
|
||||
<p className="text-2xl font-semibold">
|
||||
Хотите принять участие в обсуждении?
|
||||
</p>
|
||||
</div>
|
||||
<div className="mb-6 flex gap-6">
|
||||
<div className="border border-[#DAE0E5] text-[#49A1F5] p-2 rounded-full">
|
||||
<MicroOnIcon />
|
||||
</div>
|
||||
<p className="text-sm">Разрешите использование микрофона</p>
|
||||
</div>
|
||||
<div className="mb-10">
|
||||
<p className="text-[#77828C] text-xs">
|
||||
Выключить микрофон можно в любой момент
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
fullWidth
|
||||
large
|
||||
onClick={disallowMic}
|
||||
>
|
||||
Запретить
|
||||
</Button>
|
||||
<Button fullWidth large onClick={allowMic}>
|
||||
Разрешить
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
in={step === 3 && !isVideoInitialized}
|
||||
timeout={300}
|
||||
mountOnEnter
|
||||
unmountOnExit
|
||||
>
|
||||
{(state) => (
|
||||
<div
|
||||
className={`absolute bg-white rounded-lg p-12 w-[396px] transition-opacity ${state}`}
|
||||
>
|
||||
<div className="mb-6">
|
||||
<p className="text-2xl font-semibold">
|
||||
Пожалуйста, подождите
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
src="/icons/LoaderPrimary.png"
|
||||
alt=""
|
||||
className="w-8 h-8 animate-spin"
|
||||
/>
|
||||
<p className="font-semibold text-[#49A1F5]">Подключение</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Transition>
|
||||
</div>
|
||||
)}
|
||||
</Transition>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default StreamPage2;
|
||||
Reference in New Issue
Block a user