Skip to content

Instantly share code, notes, and snippets.

@kn0ll
Last active December 11, 2020 23:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kn0ll/5f82bd04b995a2c5eb3d0c0ecb9da447 to your computer and use it in GitHub Desktop.
Save kn0ll/5f82bd04b995a2c5eb3d0c0ecb9da447 to your computer and use it in GitHub Desktop.
// just a quick and dirty port, didn't really think it out much. just made this to help me conceptualize RTC
// (i was not happy with my findings)
import { useCallback, useMemo, useRef, useState } from "react";
const WebRTCDemo: React.FC = () => {
const connection = useRef<
Partial<{
localConnection: RTCPeerConnection;
sendChannel: RTCDataChannel;
remoteConnection: RTCPeerConnection;
receiveChannel: RTCDataChannel;
}>
>({});
const [sendChannelReadyState, setSendChannelReadyState] = useState<
RTCDataChannelState | undefined
>(undefined);
const [messages, setMessages] = useState<string[]>([]);
const [inputMessage, setInputMessage] = useState("");
const connectOnClick = useCallback(() => {
let receiveChannel: RTCDataChannel | undefined;
const localConnection = new RTCPeerConnection();
const sendChannel = localConnection.createDataChannel("sendChannel");
const handleSendChannelStatusChange = () =>
setSendChannelReadyState(sendChannel.readyState);
const handleReceiveMessage = ({ data }: MessageEvent) =>
setMessages((messages) => [...messages, data]);
const handleReceiveChannelStatusChange = () => {
if (receiveChannel) {
setSendChannelReadyState(receiveChannel.readyState);
console.log(
"Receive channel's status has changed to " + receiveChannel.readyState
);
}
// Here you would do stuff that needs to be done
// when the channel's status changes.
};
const receiveChannelCallback = (event: RTCDataChannelEvent) => {
receiveChannel = event.channel;
receiveChannel.onmessage = handleReceiveMessage;
receiveChannel.onopen = handleReceiveChannelStatusChange;
receiveChannel.onclose = handleReceiveChannelStatusChange;
connection.current = { ...connection.current, receiveChannel };
};
sendChannel.onopen = handleSendChannelStatusChange;
sendChannel.onclose = handleSendChannelStatusChange;
const remoteConnection = new RTCPeerConnection();
remoteConnection.ondatachannel = receiveChannelCallback;
const handleAddCandidateError = () =>
console.log("Oh noes! addICECandidate failed!");
const handleCreateDescriptionError = (error: Error) =>
console.log("Unable to create an offer: " + error.toString());
localConnection.onicecandidate = (e) =>
!e.candidate ||
remoteConnection
.addIceCandidate(e.candidate)
.catch(handleAddCandidateError);
remoteConnection.onicecandidate = (e) =>
!e.candidate ||
localConnection
.addIceCandidate(e.candidate)
.catch(handleAddCandidateError);
localConnection
.createOffer()
.then((offer) => localConnection.setLocalDescription(offer))
.then(() =>
remoteConnection.setRemoteDescription(localConnection.localDescription)
)
.then(() => remoteConnection.createAnswer())
.then((answer) => remoteConnection.setLocalDescription(answer))
.then(() =>
localConnection.setRemoteDescription(remoteConnection.localDescription)
)
.catch(handleCreateDescriptionError);
connection.current = {
...connection.current,
localConnection,
sendChannel,
remoteConnection,
};
}, []);
const disconnectOnClick = useCallback(() => {
const {
sendChannel,
receiveChannel,
localConnection,
remoteConnection,
} = connection.current;
sendChannel?.close();
receiveChannel?.close();
localConnection?.close();
remoteConnection?.close();
connection.current = {};
setInputMessage("");
setSendChannelReadyState(undefined);
}, []);
const sendOnClick = useCallback(() => {
setInputMessage("");
connection.current.sendChannel?.send(inputMessage);
}, [inputMessage]);
const inputMessageOnChange = useCallback<
React.ChangeEventHandler<HTMLInputElement>
>((e) => {
setInputMessage(e.currentTarget.value);
}, []);
const ready = useMemo(() => sendChannelReadyState === "open", [
sendChannelReadyState,
]);
return (
<div>
<button onClick={connectOnClick} disabled={ready}>
Connect
</button>
<button onClick={disconnectOnClick} disabled={!ready}>
Disconnect
</button>
<button onClick={sendOnClick} disabled={!ready}>
Send
</button>
<input
type="text"
disabled={!ready}
value={inputMessage}
onChange={inputMessageOnChange}
/>
<ul>
{messages.map((message, idx) => (
<li key={idx}>{message}</li>
))}
</ul>
</div>
);
};
export default WebRTCDemo;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment