Fixing Moveable
This commit is contained in:
parent
3d5f63aa0a
commit
ac1ca4ec8f
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
.MediaControl {
|
.MediaControl {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: fixed;
|
position: absolute;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -10,6 +10,8 @@ import Box from "@mui/material/Box";
|
|||||||
import useWebSocket, { ReadyState } from "react-use-websocket";
|
import useWebSocket, { ReadyState } from "react-use-websocket";
|
||||||
import { Session } from "./GlobalContext";
|
import { Session } from "./GlobalContext";
|
||||||
import WebRTCStatus from "./WebRTCStatus";
|
import WebRTCStatus from "./WebRTCStatus";
|
||||||
|
import Moveable from "react-moveable";
|
||||||
|
import { flushSync } from "react-dom";
|
||||||
|
|
||||||
const debug = true;
|
const debug = true;
|
||||||
// When true, do not send host candidates to the signaling server. Keeps TURN relays preferred.
|
// When true, do not send host candidates to the signaling server. Keeps TURN relays preferred.
|
||||||
@ -559,20 +561,20 @@ const MediaAgent = (props: MediaAgentProps) => {
|
|||||||
|
|
||||||
if (connection.connectionState === "failed") {
|
if (connection.connectionState === "failed") {
|
||||||
console.error(`media-agent - addPeer:${peer.peer_name} Connection failed for`, peer.peer_name);
|
console.error(`media-agent - addPeer:${peer.peer_name} Connection failed for`, peer.peer_name);
|
||||||
|
|
||||||
// Immediate cleanup of failed connection
|
// Immediate cleanup of failed connection
|
||||||
connectionsRef.current.delete(peer_id);
|
connectionsRef.current.delete(peer_id);
|
||||||
makingOfferRef.current.delete(peer_id);
|
makingOfferRef.current.delete(peer_id);
|
||||||
isNegotiatingRef.current.delete(peer_id);
|
isNegotiatingRef.current.delete(peer_id);
|
||||||
initiatedOfferRef.current.delete(peer_id);
|
initiatedOfferRef.current.delete(peer_id);
|
||||||
|
|
||||||
// Clean up the peer from state
|
// Clean up the peer from state
|
||||||
setPeers((prevPeers) => {
|
setPeers((prevPeers) => {
|
||||||
const updated = { ...prevPeers };
|
const updated = { ...prevPeers };
|
||||||
delete updated[peer_id];
|
delete updated[peer_id];
|
||||||
return updated;
|
return updated;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close the connection
|
// Close the connection
|
||||||
try {
|
try {
|
||||||
connection.close();
|
connection.close();
|
||||||
@ -580,8 +582,12 @@ const MediaAgent = (props: MediaAgentProps) => {
|
|||||||
console.warn(`media-agent - Error closing failed connection:`, e);
|
console.warn(`media-agent - Error closing failed connection:`, e);
|
||||||
}
|
}
|
||||||
} else if (connection.connectionState === "disconnected") {
|
} else if (connection.connectionState === "disconnected") {
|
||||||
console.warn(`media-agent - addPeer:${peer.peer_name} Connection disconnected for`, peer.peer_name, "- may recover");
|
console.warn(
|
||||||
|
`media-agent - addPeer:${peer.peer_name} Connection disconnected for`,
|
||||||
|
peer.peer_name,
|
||||||
|
"- may recover"
|
||||||
|
);
|
||||||
|
|
||||||
// Set a timeout for disconnected state recovery
|
// Set a timeout for disconnected state recovery
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (connection.connectionState === "disconnected" || connection.connectionState === "failed") {
|
if (connection.connectionState === "disconnected" || connection.connectionState === "failed") {
|
||||||
@ -759,7 +765,7 @@ const MediaAgent = (props: MediaAgentProps) => {
|
|||||||
label: t.label,
|
label: t.label,
|
||||||
id: t.id,
|
id: t.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Enable tracks for bots that need audio/video input (whisper, synthetic media, etc.)
|
// Enable tracks for bots that need audio/video input (whisper, synthetic media, etc.)
|
||||||
if (peer.peer_name.includes("-bot")) {
|
if (peer.peer_name.includes("-bot")) {
|
||||||
if (t.kind === "audio" || t.kind === "video") {
|
if (t.kind === "audio" || t.kind === "video") {
|
||||||
@ -767,7 +773,7 @@ const MediaAgent = (props: MediaAgentProps) => {
|
|||||||
console.log(`media-agent - addPeer:${peer.peer_name} Force enabled ${t.kind} track for bot`);
|
console.log(`media-agent - addPeer:${peer.peer_name} Force enabled ${t.kind} track for bot`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.addTrack(t, media);
|
connection.addTrack(t, media);
|
||||||
});
|
});
|
||||||
} else if (!localUserHasMedia) {
|
} else if (!localUserHasMedia) {
|
||||||
@ -809,7 +815,7 @@ const MediaAgent = (props: MediaAgentProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[peers, setPeers, media, sendJsonMessage, localUserHasMedia, updatePeerConnectionState]
|
[peers, setPeers, media, sendJsonMessage, updatePeerConnectionState, session?.has_media]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Process queued peers when media becomes available
|
// Process queued peers when media becomes available
|
||||||
@ -893,10 +899,7 @@ const MediaAgent = (props: MediaAgentProps) => {
|
|||||||
const candidateInit: RTCIceCandidateInit = {
|
const candidateInit: RTCIceCandidateInit = {
|
||||||
candidate: candStr ?? "",
|
candidate: candStr ?? "",
|
||||||
sdpMid: candidate.sdpMid ?? undefined,
|
sdpMid: candidate.sdpMid ?? undefined,
|
||||||
sdpMLineIndex:
|
sdpMLineIndex: typeof candidate.sdpMLineIndex === "number" ? candidate.sdpMLineIndex : undefined,
|
||||||
typeof candidate.sdpMLineIndex === "number"
|
|
||||||
? candidate.sdpMLineIndex
|
|
||||||
: undefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1300,6 +1303,26 @@ const MediaControl: React.FC<MediaControlProps> = ({ isSelf, peer, className })
|
|||||||
const [muted, setMuted] = useState<boolean>(peer?.muted || false);
|
const [muted, setMuted] = useState<boolean>(peer?.muted || false);
|
||||||
const [videoOn, setVideoOn] = useState<boolean>(peer?.video_on !== false);
|
const [videoOn, setVideoOn] = useState<boolean>(peer?.video_on !== false);
|
||||||
const [isValid, setIsValid] = useState<boolean>(false);
|
const [isValid, setIsValid] = useState<boolean>(false);
|
||||||
|
const [frame, setFrame] = useState<{ translate: [number, number] }>({ translate: [0, 0] });
|
||||||
|
const targetRef = useRef<HTMLDivElement>(null);
|
||||||
|
const moveableRef = useRef<any>(null);
|
||||||
|
|
||||||
|
// Initialize position based on peer session_id to avoid stacking
|
||||||
|
useEffect(() => {
|
||||||
|
if (!peer) return;
|
||||||
|
|
||||||
|
// Create a simple hash of the session_id to get a position offset
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < peer.session_id.length; i++) {
|
||||||
|
hash = (hash << 5) - hash + peer.session_id.charCodeAt(i);
|
||||||
|
hash = hash & hash; // Convert to 32-bit integer
|
||||||
|
}
|
||||||
|
|
||||||
|
const x = (Math.abs(hash) % 300) + 50; // Random x between 50-350px
|
||||||
|
const y = (Math.abs(hash) % 200) + 50; // Random y between 50-250px
|
||||||
|
|
||||||
|
setFrame({ translate: [x, y] });
|
||||||
|
}, [peer]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!peer) return;
|
if (!peer) return;
|
||||||
@ -1394,14 +1417,21 @@ const MediaControl: React.FC<MediaControlProps> = ({ isSelf, peer, className })
|
|||||||
});
|
});
|
||||||
}, [muted, peer]);
|
}, [muted, peer]);
|
||||||
|
|
||||||
|
// Debug target element
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!peer || peer.dead || !peer.attributes?.srcObject) return;
|
console.log("Target ref current:", targetRef.current, "for peer:", peer?.session_id);
|
||||||
|
if (targetRef.current) {
|
||||||
const stream = peer.attributes.srcObject as MediaStream;
|
console.log("Target element rect:", targetRef.current.getBoundingClientRect());
|
||||||
stream.getVideoTracks().forEach((t) => {
|
console.log("Target element computed style:", {
|
||||||
t.enabled = videoOn;
|
position: getComputedStyle(targetRef.current).position,
|
||||||
});
|
left: getComputedStyle(targetRef.current).left,
|
||||||
}, [videoOn, peer]);
|
top: getComputedStyle(targetRef.current).top,
|
||||||
|
transform: getComputedStyle(targetRef.current).transform,
|
||||||
|
width: getComputedStyle(targetRef.current).width,
|
||||||
|
height: getComputedStyle(targetRef.current).height
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [peer?.session_id]);
|
||||||
|
|
||||||
const toggleMute = (e: React.MouseEvent | React.TouchEvent) => {
|
const toggleMute = (e: React.MouseEvent | React.TouchEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -1427,7 +1457,16 @@ const MediaControl: React.FC<MediaControlProps> = ({ isSelf, peer, className })
|
|||||||
return (
|
return (
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", minWidth: "200px", minHeight: "100px" }}>
|
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", minWidth: "200px", minHeight: "100px" }}>
|
||||||
<div className={`MediaControlSpacer ${className}`} />
|
<div className={`MediaControlSpacer ${className}`} />
|
||||||
<div className={`MediaControl ${className}`} data-peer={peer.session_id}>
|
<div
|
||||||
|
ref={targetRef}
|
||||||
|
className={`MediaControl ${className}`}
|
||||||
|
data-peer={peer.session_id}
|
||||||
|
style={{
|
||||||
|
left: '0px',
|
||||||
|
top: '0px',
|
||||||
|
transform: `translate(${frame.translate[0]}px, ${frame.translate[1]}px)`
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="Controls">
|
<div className="Controls">
|
||||||
{isSelf ? (
|
{isSelf ? (
|
||||||
<div onTouchStart={toggleMute} onClick={toggleMute}>
|
<div onTouchStart={toggleMute} onClick={toggleMute}>
|
||||||
@ -1463,36 +1502,53 @@ const MediaControl: React.FC<MediaControlProps> = ({ isSelf, peer, className })
|
|||||||
<WebRTCStatus isNegotiating={peer.isNegotiating || false} connectionState={peer.connectionState} />
|
<WebRTCStatus isNegotiating={peer.isNegotiating || false} connectionState={peer.connectionState} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{/* <Moveable
|
<Moveable
|
||||||
flushSync={flushSync}
|
ref={moveableRef}
|
||||||
pinchable
|
flushSync={flushSync}
|
||||||
draggable
|
pinchable
|
||||||
target={document.querySelector(`.MediaControl[data-peer="${peer.session_id}"]`) as HTMLElement}
|
draggable
|
||||||
resizable
|
target={targetRef.current}
|
||||||
keepRatio
|
resizable
|
||||||
hideDefaultLines={false}
|
keepRatio
|
||||||
edge
|
hideDefaultLines={false}
|
||||||
onDragStart={(e) => e.set(frame.translate)}
|
edge
|
||||||
onDrag={(e) => {
|
onDragStart={(e) => {
|
||||||
if (Array.isArray(e.beforeTranslate) && e.beforeTranslate.length === 2) {
|
console.log("Moveable drag start", { target: targetRef.current, event: e });
|
||||||
frame.translate = [e.beforeTranslate[0], e.beforeTranslate[1]];
|
}}
|
||||||
}
|
onDrag={(e) => {
|
||||||
}}
|
console.log("Moveable drag", { beforeTranslate: e.beforeTranslate, transform: e.transform });
|
||||||
onResizeStart={(e) => {
|
// Apply the transform directly to the target element
|
||||||
e.setOrigin(["%", "%"]);
|
if (targetRef.current) {
|
||||||
e.dragStart && e.dragStart.set(frame.translate);
|
targetRef.current.style.transform = e.transform;
|
||||||
}}
|
}
|
||||||
onResize={(e) => {
|
}}
|
||||||
const { translate } = frame;
|
onDragEnd={(e) => {
|
||||||
e.target.style.width = `${e.width}px`;
|
console.log("Moveable drag end", { target: targetRef.current });
|
||||||
e.target.style.height = `${e.height}px`;
|
// Get the final transform from the element
|
||||||
e.target.style.transform = `translate(${translate[0]}px, ${translate[1]}px)`;
|
if (targetRef.current) {
|
||||||
}}
|
const computedStyle = getComputedStyle(targetRef.current);
|
||||||
onRender={(e) => {
|
const transform = computedStyle.transform;
|
||||||
const { translate } = frame;
|
console.log("Final computed transform:", transform);
|
||||||
e.target.style.transform = `translate(${translate[0]}px, ${translate[1]}px)`;
|
|
||||||
}}
|
// Parse the transform matrix to get translate values
|
||||||
/> */}
|
if (transform && transform !== "none") {
|
||||||
|
const matrix = new DOMMatrix(transform);
|
||||||
|
console.log("Parsed matrix translate:", [matrix.m41, matrix.m42]);
|
||||||
|
setFrame({ translate: [matrix.m41, matrix.m42] });
|
||||||
|
} else {
|
||||||
|
// If no transform, reset to 0,0
|
||||||
|
setFrame({ translate: [0, 0] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onResizeStart={(e) => {
|
||||||
|
e.setOrigin(["%", "%"]);
|
||||||
|
}}
|
||||||
|
onResize={(e) => {
|
||||||
|
e.target.style.width = `${e.width}px`;
|
||||||
|
e.target.style.height = `${e.height}px`;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user