From 357f50e105642158dc0f85cae91dd530fb4078c9 Mon Sep 17 00:00:00 2001 From: fraisai Date: Fri, 22 Dec 2023 18:49:10 -0500 Subject: [PATCH 1/5] edited slack-notify workflow --- .eslintignore | 2 +- .github/workflows/npm-publish.yml | 3 +-- .github/workflows/rtconnect-test.yml | 19 ++++++++++++++++++- .github/workflows/slack-notify.yml | 3 +-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/.eslintignore b/.eslintignore index e4601ab..0485d3d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,4 @@ # don't lint build or dist output dist build -tsconfig.json +tsconfig.json \ No newline at end of file diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 0540015..4d708d7 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -18,8 +18,7 @@ jobs: - name: Publish npm package when version is updated uses: JS-DevTools/npm-publish@v3 with: - token: ${{ secrets.NPM_TOKEN }} - strategy: upgrade + token: ${{ secrets.NPM_TOKEN }} # This works BUT it shows up as problem for some unknown reason ("Context access might be invalid: NPM_TOKEN") and there should not be any errors # https://github.com/JS-DevTools/npm-publish \ No newline at end of file diff --git a/.github/workflows/rtconnect-test.yml b/.github/workflows/rtconnect-test.yml index aac8a4c..938505b 100644 --- a/.github/workflows/rtconnect-test.yml +++ b/.github/workflows/rtconnect-test.yml @@ -29,4 +29,21 @@ jobs: - run: npm run lint - run: npm test # env: - # CI: true \ No newline at end of file + # CI: true + + + # slackNotification: + # name: Slack CI status - notify on failure + # runs-on: ubuntu-latest + # steps: + # - name: Slack Notify on Failure + # if: ${{ failure() }} + # id: slack + # uses: slackapi/slack-github-action@v1.24.0 + # with: + # channel-id: 'C067F896WG5' + # slack-message: "Github CI Result: ${{ job.status }}\nGithub PR/Commit URL: ${{ github.event.pull_request.html_url || github.event.head_commit.url }}" + # env: + # SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + # # SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + # # SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK diff --git a/.github/workflows/slack-notify.yml b/.github/workflows/slack-notify.yml index 0b1f7d0..fcf6bc6 100644 --- a/.github/workflows/slack-notify.yml +++ b/.github/workflows/slack-notify.yml @@ -19,9 +19,8 @@ jobs: channel-id: 'C067F896WG5' slack-message: "Github CI Result: ${{ job.status }}\nGithub PR/Commit URL: ${{ github.event.pull_request.html_url || github.event.head_commit.url }}" env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} # This works BUT it shows up as problem for some unknown reason ("Context access might be invalid: NPM_TOKEN") and there should not be any errors # SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} - # SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK # https://github.com/slackapi/slack-github-action \ No newline at end of file From 1e7e89452aecd111667e6dc44b8f6745095c36d9 Mon Sep 17 00:00:00 2001 From: fraisai Date: Fri, 22 Dec 2023 20:04:13 -0500 Subject: [PATCH 2/5] edited documentation for callUser and createPeer functions --- lib/src/components/VideoCall.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/src/components/VideoCall.tsx b/lib/src/components/VideoCall.tsx index 5a8515e..0f09789 100644 --- a/lib/src/components/VideoCall.tsx +++ b/lib/src/components/VideoCall.tsx @@ -121,8 +121,10 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control * | |-------------------|------------------>| Peer B's NAT * |<--------------------------|-------------------| Accepting Peer A's call, sending Answer SDP * |<--------------------------|-------------------| Peer B's ICE Candidates are now being trickled in to peer A for connectivity. - * |-------------------------->|------------------>| ICE Candidates from Peer A, these steps repeat and are only necessary if Peer B can't connect to the earlier candidates sent. - * |<--------------------------|-------------------| ICE Candidate trickling from Peer B, could also take a second if there's a firewall to be circumvented. + * |-------------------------->|------------------>| ICE Candidates from Peer A, these steps repeat and are only necessary if Peer B can't connect to the + * | | | | earlier candidates sent. + * |<--------------------------|-------------------| ICE Candidate trickling from Peer B, could also take a second if there's a firewall to be + * | | | | circumvented. * | | | | Connected! Peer to Peer connection is made and now both users are streaming data to eachother! * * If Peer A starts a call their order of functions being invoked is... handleOffer --> callUser --> createPeer --> peerRef.current.negotiationNeeded event (handleNegotiationNeededEvent) --> ^send Offer SDP^ --> start ICE trickle, handleIceCandidateEvent --> ^receive Answer^ SDP --> handleIceCandidateMsg --> once connected, handleTrackEvent @@ -154,7 +156,7 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control /** * @func handleOffer - * @desc When a username is entered that the client wants to "Call" and the client clicks the Call button, into the input field, this starts the Offer-Answer Model exchange + * @desc When a username is entered into the input field that the client wants to "Call" and the client clicks the Call button, this starts the SDP Offer-Answer exchange */ const handleOffer = (): void => { const inputField:HTMLInputElement | null = document.querySelector('#receiverName'); @@ -201,7 +203,7 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control }; /** - * @function callUser - Constructs a new RTCPeerConnection object that also adds the local client's media tracks to this object. + * @function callUser - Constructs a new RTCPeerConnection object using the createPeer function and then adds the local client's (Peer A/caller) media tracks to peer connection ref object. * @param {string} userID */ const callUser = (userID: string): void => { @@ -216,7 +218,7 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionstatechange_event and other events */ const createPeer = (userID: string): RTCPeerConnection => { - const peer:RTCPeerConnection = new RTCPeerConnection(configuration); + const peer: RTCPeerConnection = new RTCPeerConnection(configuration); peer.onicecandidate = handleIceCandidateEvent; peer.ontrack = handleTrackEvent; peer.onnegotiationneeded = () => handleNegotiationNeededEvent(userID); @@ -253,8 +255,7 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control * @param {string} userID */ function handleNegotiationNeededEvent(userID: string): void { - peerRef.current - ?.createOffer() + peerRef.current?.createOffer() .then((offer) => { return peerRef.current?.setLocalDescription(offer); }) From 5dcec6a6ccb779a9df4a6dfc06e04a07078bf9b8 Mon Sep 17 00:00:00 2001 From: fraisai Date: Fri, 22 Dec 2023 20:16:20 -0500 Subject: [PATCH 3/5] updated documentation for configuration object --- lib/src/components/VideoCall.tsx | 19 +++++++++---------- lib/src/constants/rtcConfiguration.ts | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/src/components/VideoCall.tsx b/lib/src/components/VideoCall.tsx index 0f09789..cf6aeb2 100644 --- a/lib/src/components/VideoCall.tsx +++ b/lib/src/components/VideoCall.tsx @@ -38,7 +38,7 @@ interface icePayObj extends payloadObj { * @desc Wrapper component containing the logic necessary for peer connections using WebRTC APIs (RTCPeerConnect API + MediaSession API) and WebSockets. * - * ws, localVideo, remoteVideo, peerRef, localStream, otherUser, senders are all mutable ref objects that are created using the useRef hook. The useRef hook allows you to persist values between renders and it is used to store a mutable value that does NOT cause a re-render when updated. + * ws, localVideo, remoteVideo, peerRef, localStreamRef, otherUser, senders are all mutable ref objects that are created using the useRef hook. The useRef hook allows you to persist values between renders and it is used to store a mutable value that does NOT cause a re-render when updated. * * The WebSocket connection (ws.current) is established using the useEffect hook and once the component mounts, the Socket component is rendered. The Socket component adds event listeners that handle the offer-answer model and the exchange of SDP objects between peers and the socket. * @@ -86,9 +86,9 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control const otherUser = useRef(); /** - * @type {mutable ref object} localStream - It cannot be null or undefined. + * @type {mutable ref object} localStreamRef - It cannot be null or undefined. */ - const localStream = useRef(null!); + const localStreamRef = useRef(null!); /** * @type {mutable ref array} senders - @@ -195,7 +195,7 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control const openUserMedia = async (): Promise => { try { if (localVideo.current){ - localStream.current = localVideo.current.srcObject = await navigator.mediaDevices.getUserMedia(constraints); + localStreamRef.current = localVideo.current.srcObject = await navigator.mediaDevices.getUserMedia(constraints); } } catch (error) { console.log('Error in openUserMedia: ', error); @@ -208,7 +208,7 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control */ const callUser = (userID: string): void => { peerRef.current = createPeer(userID); - localStream.current.getTracks().forEach((track) => senders.current.push(peerRef.current.addTrack(track, localStream.current))); + localStreamRef.current.getTracks().forEach((track) => senders.current.push(peerRef.current.addTrack(track, localStreamRef.current))); }; /** @@ -216,6 +216,7 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control * @param {string} userID * @returns {RTCPeerConnection} RTCPeerConnection object * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionstatechange_event and other events + * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection */ const createPeer = (userID: string): RTCPeerConnection => { const peer: RTCPeerConnection = new RTCPeerConnection(configuration); @@ -223,8 +224,6 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control peer.ontrack = handleTrackEvent; peer.onnegotiationneeded = () => handleNegotiationNeededEvent(userID); - console.log('registerPeerConnectionListners has activated'); - peer.addEventListener('negotiationneeded', () => { console.log('negotiationneeded event has fired'); }); @@ -321,7 +320,7 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control peerRef.current.setRemoteDescription(desc) .then(() => { - localStream.current?.getTracks().forEach((track) => peerRef.current?.addTrack(track, localStream.current)); + localStreamRef.current?.getTracks().forEach((track) => peerRef.current?.addTrack(track, localStreamRef.current)); }) .then(() => { return peerRef.current?.createAnswer(); @@ -413,8 +412,8 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control screenTrack.onended = function() { // ended event is fired when playback or streaming has stopped because the end of the media was reached or because no further data is available senders.current ?.find(sender => sender.track?.kind === 'video') - ?.replaceTrack(localStream.current.getTracks()[1]); // - localVideo.current.srcObject = localStream.current; // changing local video displayed back to webcam + ?.replaceTrack(localStreamRef.current.getTracks()[1]); // + localVideo.current.srcObject = localStreamRef.current; // changing local video displayed back to webcam }; }); } diff --git a/lib/src/constants/rtcConfiguration.ts b/lib/src/constants/rtcConfiguration.ts index 26b2ac4..fc0162a 100644 --- a/lib/src/constants/rtcConfiguration.ts +++ b/lib/src/constants/rtcConfiguration.ts @@ -1,5 +1,5 @@ /** - * @type {Object} configuration - The RTCConfiguration object is used to provide configuration options for how an RTCPeerConnection should be created. + * @type {RTCConfiguration} configuration - The RTCConfiguration object is used to provide configuration options for how an RTCPeerConnection should be created. * * @property {array} iceServers - An array of RTCIceServer objects, each describing one server which * may be used by the ICE agent. These servers are typically STUN and/or TURN servers. If this From d27cc769d9500440f1367503f33ddef45a809a54 Mon Sep 17 00:00:00 2001 From: fraisai Date: Fri, 22 Dec 2023 20:22:01 -0500 Subject: [PATCH 4/5] added iceGatheringState property - 232 --- lib/src/components/VideoCall.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/components/VideoCall.tsx b/lib/src/components/VideoCall.tsx index cf6aeb2..c0931f5 100644 --- a/lib/src/components/VideoCall.tsx +++ b/lib/src/components/VideoCall.tsx @@ -229,6 +229,8 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control }); peer.addEventListener('icegatheringstatechange', () => { + // const stateOfIceGathering = peer.iceGatheringState; // returns a string that describes the connection's ICE gathering state (new, gathering, or complete) + // console.log(`ICE gathering state: ${stateOfIceGathering}`); console.log(`ICE gathering state changed: ${peerRef.current?.iceGatheringState}`); }); From e29d6707af60f93e8ed6d27d3c90ac8ce8b620f3 Mon Sep 17 00:00:00 2001 From: fraisai Date: Fri, 22 Dec 2023 20:52:11 -0500 Subject: [PATCH 5/5] updated classnames for unit testing --- lib/src/components/VideoCall.tsx | 36 ++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/src/components/VideoCall.tsx b/lib/src/components/VideoCall.tsx index c0931f5..3fc66eb 100644 --- a/lib/src/components/VideoCall.tsx +++ b/lib/src/components/VideoCall.tsx @@ -204,7 +204,7 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control /** * @function callUser - Constructs a new RTCPeerConnection object using the createPeer function and then adds the local client's (Peer A/caller) media tracks to peer connection ref object. - * @param {string} userID + * @param {string} userID the remote client's (Peer B/callee) username */ const callUser = (userID: string): void => { peerRef.current = createPeer(userID); @@ -212,8 +212,8 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control }; /** - * @function createPeer - Creates a new RTCPeerConnection object, which represents a WebRTC connection between the local device and a remote peer and adds event listeners to it - * @param {string} userID + * @function createPeer - Creates a new RTCPeerConnection object, which represents a WebRTC connection between the local device and a remote peer and adds event listeners (handleIceCandidateEvent, handleTrackEvent, handleNegotiationNeededEvent) to it + * @param {string} userID the remote client's (Peer B/callee) username * @returns {RTCPeerConnection} RTCPeerConnection object * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionstatechange_event and other events * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection @@ -252,8 +252,12 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control /** * @function handleNegotiationNeededEvent - * @desc invokes WebRTC's built-in createOffer() function to create an SDP offer, which is an RTCSessionDescription object. This offer is then set as the local description using WebRTC's built-in setLocalDescription() function. Finally, the offer, sender and receiver is sent via ws.current.send to the Signaling Channel in the backend - * @param {string} userID + * @desc creates an SDP offer and sends it through the signaling channel to the remote peer: invokes WebRTC's built-in createOffer() function to create an SDP offer, which is an RTCSessionDescription object. After creating the offer, the local end is configured by calling RTCPeerConnection.setLocalDescription(). + * + * Then a signaling message is created and sent to the remote peer through the signaling server, to share the offer with the other peer. The other peer should recognize this message and follow up by creating its own RTCPeerConnection, setting the remote description with setRemoteDescription(), and then creating an answer to send back to the offering peer. Finally, the offer, sender and receiver is sent via ws.current.send to the Signaling Channel in the backend + * + * @param {string} userID the remote client's (Peer B/callee) username + * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/negotiationneeded_event */ function handleNegotiationNeededEvent(userID: string): void { peerRef.current?.createOffer() @@ -263,7 +267,7 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control .then(() => { const offerPayload: offerPayObj = { ACTION_TYPE: OFFER, - sender: username, + sender: username, // local peer receiver: userID, payload: peerRef.current?.localDescription }; @@ -498,14 +502,14 @@ const VideoCall = ({ URL, mediaOptions }: { URL: string, mediaOptions: { control > userField = e.target.value} + onChange={(e) => userField = e.target.value} + placeholder='username' style={{ paddingBottom:'40px', width:'200px' - }} + }} + type='text' >