startResize(e, "top-right")}
+ onPointerMove={resizePopout}
+ onPointerUp={stopResize}
+ onPointerCancel={stopResize}
+ />
+
startResize(e, "bottom-right")}
+ onPointerMove={resizePopout}
+ onPointerUp={stopResize}
+ onPointerCancel={stopResize}
+ />
+
startResize(e, "bottom-left")}
+ onPointerMove={resizePopout}
+ onPointerUp={stopResize}
+ onPointerCancel={stopResize}
+ />
+
+ );
+}
+
+export default function Wrapper() {
+ const room = getRoom();
+ const { pathname } = useLocation();
+ const { openMobile } = useSidebar();
+ const isMobile = useIsMobile();
+ const state = useCall((state) => state.state);
+ const callId = useCall((state) => state.callId);
+ const watchedStreamParticipantIds = useCall(
+ (state) => state.watchedStreamParticipantIds,
+ );
+ const lastFocusedParticipantId = useCall(
+ (state) => state.lastFocusedParticipantId,
+ );
+ const participant = room.getParticipantByIdentity(
+ String(lastFocusedParticipantId),
+ );
+
+ if (isMobile) {
+ return (
+
+ );
+ }
+
+ if (!participant || !lastFocusedParticipantId) {
+ return null;
+ }
+
+ const active =
+ !pathname.startsWith("/call") &&
+ state === "open" &&
+ watchedStreamParticipantIds.includes(Number(participant.identity ?? 0));
+
+ return active &&
;
+}
diff --git a/packages/call/src/components/screenshareDialog.tsx b/packages/call/src/components/screenshareDialog.tsx
deleted file mode 100644
index 180c63e..0000000
--- a/packages/call/src/components/screenshareDialog.tsx
+++ /dev/null
@@ -1,335 +0,0 @@
-import { useEffect, useState } from "react";
-import {
- Button,
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- Label,
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
- Switch,
-} from "@tensamin/ui";
-import {
- type DesktopScreenShareAudioOutput,
- type DesktopScreenShareCapabilities,
- type DesktopScreenShareSource,
- useDesktopMedia,
-} from "@tensamin/tauri/context";
-import { toast } from "@tensamin/shared/log";
-import { AppWindow, Loader2, MonitorUp } from "lucide-react";
-import type { ScreenShareCaptureOptions } from "livekit-client";
-import { setScreenShareEnabled, startLinuxDesktopScreenShare } from "../store";
-
-const NONE_AUDIO_OUTPUT = "__none__";
-
-function buildScreenShareOptions(
- source: DesktopScreenShareSource,
- capabilities: DesktopScreenShareCapabilities,
- selectedAudioOutputId: string,
- shareAudio: boolean,
-): ScreenShareCaptureOptions {
- const wantsAudio = capabilities.showAudioOutputSelector
- ? selectedAudioOutputId !== NONE_AUDIO_OUTPUT
- : capabilities.hasReliableSystemAudio && shareAudio;
-
- return {
- audio: wantsAudio
- ? {
- autoGainControl: false,
- echoCancellation: false,
- noiseSuppression: false,
- }
- : false,
- video: {
- displaySurface: source.kind === "window" ? "window" : "monitor",
- },
- systemAudio: wantsAudio ? "include" : "exclude",
- surfaceSwitching: "exclude",
- selfBrowserSurface: "exclude",
- contentHint: "detail",
- };
-}
-
-export default function ScreenShareDialog({
- open,
- onOpenChange,
- isScreensharing,
- portalContainer,
-}: {
- open: boolean;
- onOpenChange: (open: boolean) => void;
- isScreensharing: boolean;
- portalContainer?: HTMLElement;
-}) {
- const {
- getScreenShareCapabilities,
- listScreenShareAudioOutputs,
- listScreenShareSources,
- } = useDesktopMedia();
-
- const [loading, setLoading] = useState(false);
- const [sources, setSources] = useState
([]);
- const [audioOutputs, setAudioOutputs] = useState<
- DesktopScreenShareAudioOutput[]
- >([]);
- const [capabilities, setCapabilities] =
- useState(null);
- const [selectedSourceId, setSelectedSourceId] = useState(null);
- const [selectedAudioOutputId, setSelectedAudioOutputId] =
- useState(NONE_AUDIO_OUTPUT);
- const [shareAudio, setShareAudio] = useState(false);
-
- useEffect(() => {
- if (!open) {
- return;
- }
-
- let active = true;
-
- // eslint-disable-next-line
- setLoading(true);
- setSelectedSourceId(null);
- setSelectedAudioOutputId(NONE_AUDIO_OUTPUT);
- setShareAudio(false);
-
- Promise.all([listScreenShareSources(), getScreenShareCapabilities()])
- .then(async ([nextSources, nextCapabilities]) => {
- if (!active) {
- return;
- }
-
- setSources(nextSources);
- setCapabilities(nextCapabilities);
-
- if (nextCapabilities.showAudioOutputSelector) {
- const nextOutputs = await listScreenShareAudioOutputs();
-
- if (!active) {
- return;
- }
-
- setAudioOutputs(nextOutputs);
- } else {
- setAudioOutputs([]);
- }
- })
- .catch((error) => {
- console.error("Failed to load desktop share sources", error);
- toast("error", "Failed to load screen share sources.");
- })
- .finally(() => {
- if (active) {
- setLoading(false);
- }
- });
-
- return () => {
- active = false;
- };
- }, [
- getScreenShareCapabilities,
- listScreenShareAudioOutputs,
- listScreenShareSources,
- open,
- ]);
-
- const selectedSource =
- sources.find((source) => source.id === selectedSourceId) ?? null;
-
- async function startSharing() {
- if (!selectedSource || !capabilities) {
- return;
- }
-
- setLoading(true);
-
- try {
- if (capabilities.platform === "linux") {
- await startLinuxDesktopScreenShare(selectedSource.id);
- } else {
- await setScreenShareEnabled(
- true,
- buildScreenShareOptions(
- selectedSource,
- capabilities,
- selectedAudioOutputId,
- shareAudio,
- ),
- );
- }
- onOpenChange(false);
- } catch (error) {
- console.error("Failed to start screen share", error);
- toast(
- "error",
- error instanceof Error
- ? error.message
- : "Failed to start screen sharing.",
- );
- } finally {
- setLoading(false);
- }
- }
-
- async function stopSharing() {
- setLoading(true);
-
- try {
- await setScreenShareEnabled(false);
- onOpenChange(false);
- } catch (error) {
- console.error("Failed to stop screen share", error);
- toast("error", "Failed to stop screen sharing.");
- } finally {
- setLoading(false);
- }
- }
-
- return (
-
- );
-}
diff --git a/packages/call/src/components/sidebarBox.tsx b/packages/call/src/components/sidebarBox.tsx
index 294d051..1f1840d 100644
--- a/packages/call/src/components/sidebarBox.tsx
+++ b/packages/call/src/components/sidebarBox.tsx
@@ -1,5 +1,5 @@
import { Lock, LockOpen } from "lucide-react";
-import { openCallPage, useCall } from "../store";
+import { openCallPage, useCall, getRoom } from "../store";
import {
Button,
Card,
@@ -9,8 +9,8 @@ import {
TooltipContent,
TooltipTrigger,
useIsMobile,
-} from "@tensamin/ui";
-import ScreenshareButton from "./buttons/screenshare";
+} from "@methanium/ui";
+import MediaShareButton from "./buttons/mediaShare";
import MuteButton from "./buttons/mute";
import DeafButton from "./buttons/deaf";
import { Room, Track } from "livekit-client";
@@ -18,34 +18,39 @@ import { useEffect, useState } from "react";
import { AreaChart, Area } from "recharts";
import LeaveButton from "./buttons/leave";
-export default function SidebarBox() {
- const state = useCall((store) => store.state);
- const screenRef = useCall((store) => store.screenRef);
- const isMobile = useIsMobile();
- const [portalContainer, setPortalContainer] = useState();
+async function getPing(room: Room): Promise {
+ const report = await room.localParticipant
+ .getTrackPublication(Track.Source.Microphone)
+ ?.track?.getRTCStatsReport();
- useEffect(() => {
- setPortalContainer(screenRef?.current ?? undefined);
- }, [screenRef]);
+ if (!report) return;
- return state === "closed" ? null : (
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
+ let bestRtt: number | undefined;
+
+ report.forEach((stat) => {
+ if (
+ stat.type === "candidate-pair" &&
+ stat.state === "succeeded" &&
+ stat.currentRoundTripTime != null
+ ) {
+ bestRtt = stat.currentRoundTripTime * 1000;
+ }
+
+ if (stat.type === "remote-inbound-rtp" && stat.roundTripTime != null) {
+ bestRtt = stat.roundTripTime * 1000;
+ }
+ });
+
+ if (bestRtt == null || bestRtt <= 0) return;
+
+ const roundedRtt = Math.round(bestRtt);
+
+ if (roundedRtt <= 0) return;
+
+ return roundedRtt;
}
-function ConnectionBar({ portalContainer }: { portalContainer?: HTMLElement }) {
+function ConnectionBar() {
const state = useCall((store) => store.state);
const isEncrypted = useCall((store) => store.isEncrypted);
const callId = useCall((store) => store.callId);
@@ -53,14 +58,18 @@ function ConnectionBar({ portalContainer }: { portalContainer?: HTMLElement }) {
return (
(
- }
+ )}
/>
-
- Click to open call page
-
+ Click to open call page
);
}
-export function TinyPingGraph({
- portalContainer,
-}: {
- portalContainer?: HTMLElement;
-}) {
- const room = useCall((store) => store.room);
+export function TinyPingGraph() {
+ const room = getRoom();
const [mapData, setMapData] = useState