diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1c3e9b9ec..5bf240180 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,6 +30,23 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + + - name: Use Node.js 18 + uses: actions/setup-node@v3 + with: + node-version: 18.20.6 # Or your preferred 18.x version + + - name: Print Node.js version + run: node -v + + - name: Install dependencies + run: npm ci # Or npm install if you prefer + + - name: Build project + run: | + make boostrap + make release + npm run build # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL @@ -43,19 +60,15 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 + # - name: Autobuild + # uses: github/codeql-action/autobuild@v2 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release + # uses a compiled language (we moved the "below" lines up to 42 and edited them) - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/createPackages.yml b/.github/workflows/createPackages.yml index 6451f1da5..43c43f881 100644 --- a/.github/workflows/createPackages.yml +++ b/.github/workflows/createPackages.yml @@ -9,16 +9,16 @@ jobs: tests: strategy: matrix: - node-version: [16] + node-version: [18.20.6] os: [macos-latest, ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - name: Checkout repo uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} + - name: Use Node.js 18.20.6 # this used to be dynamically updated, but kept pulling an old version number. you'll have to manually update these throughout this file to be safe. uses: actions/setup-node@v3 with: - node-version: ${{ matrix.node-version }} + node-version: 18.20.6 - name: Install dependencies run: npm i && npm ci - name: Run unit tests @@ -40,15 +40,15 @@ jobs: - name: Use Node.js uses: actions/setup-node@v2 with: - node-version: '16.15' + node-version: '18.20.6' - name: Create Release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} with: - tag_name: v1.16.0 # Replace with your desired tag or version number - release_name: Release v1.16.0 # Replace with your desired release name + tag_name: v1.19.0 # Replace with your desired tag or version number + release_name: Release v1.19.0 # Replace with your desired release name draft: true body: | @@ -67,7 +67,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v2 with: - node-version: '16.15' + node-version: '18.20.6' - name: Install Dependencies run: npm install @@ -89,7 +89,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v2 with: - node-version: '16.15' # Use the specific version of Node that your project requires + node-version: '18.20.6' # Use the specific version of Node that your project requires - name: Install Dependencies run: npm install @@ -118,7 +118,7 @@ jobs: - name: Use Node.js uses: actions/setup-node@v2 with: - node-version: '16.15' # Use the specific version of Node that your project requires + node-version: '18.20.6' # Use the specific version of Node that your project requires - name: Install Dependencies run: npm install diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index c6c1dea01..7ad54fa6a 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -6,7 +6,7 @@ jobs: tests: strategy: matrix: - node-version: [16] + node-version: [18.20.6] os: [macos-latest, ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} steps: @@ -18,7 +18,11 @@ jobs: node-version: ${{ matrix.node-version }} - name: Install dependencies run: npm i && npm ci + - name: Apply patches + env: + NODE_ENV: --no-node-snapshot + run: npx patch-package - name: Run unit tests uses: coactions/setup-xvfb@v1 with: - run: npm run test-jest \ No newline at end of file + run: npm run test-jest diff --git a/Dockerfile-dev b/Dockerfile-dev new file mode 100644 index 000000000..dd6134d60 --- /dev/null +++ b/Dockerfile-dev @@ -0,0 +1,6 @@ +From node:18.20 +RUN npm install -g webpack +Workdir /app +Copy package*.json /app +Run npm install +Expose 8080 \ No newline at end of file diff --git a/build/config.gypi b/build/config.gypi index 71c5bb224..20f7f8776 100644 --- a/build/config.gypi +++ b/build/config.gypi @@ -1,7 +1,7 @@ # Do not edit. File was generated by node-gyp's "configure" step { "target_defaults": { - "cflags": [], + "cflags": ['-std=c++20'], "default_configuration": "Release", "defines": [], "include_dirs": [], @@ -52,7 +52,7 @@ "node_use_dtrace": "true", "node_use_etw": "false", "node_use_node_code_cache": "true", - "node_use_node_snapshot": "true", + "node_use_node_snapshot": "false", "node_use_openssl": "true", "node_use_v8_platform": "true", "node_with_ltcg": "false", diff --git a/docs/DEV-README.md b/docs/DEV-README.md index e869a175a..f5f7447d9 100644 --- a/docs/DEV-README.md +++ b/docs/DEV-README.md @@ -19,7 +19,7 @@ Thank you for your consideration and let's work together on making Swell one of - TypeScript + JavaScript - React -- Redux +- Redux Toolkit - SASS - Node - Express @@ -111,6 +111,7 @@ From a functionality standpoint: - HTTP/2 stress testing with `GET` requests - GraphQL stress testing with `Query` - Mock server for HTTP/2 (`Express`) +- WebRTC video/audio and text channel testing - Ability to store historical requests and create/delete workspaces - Frontend conversion to TypeScript - From a codebase standpoint: @@ -140,9 +141,9 @@ endeavour. The impacts to the product are: As you iterate the product, keep in mind the footprint your new feature(s) could add to the codebase. Could you re-use some of the existing modules? Can you even refactor and/or remove the obsolete code to help maintain the health of the codebase? -There are many parts of the codebase that break DRY principles, and with such a large application, really keep in mind that when you add features to ask if it is completely necessary. Past iterators added an experimental feature(s) without it fully working and the following team(s) would add their own experimental feature. Fixing features the past teams couldn't get to is not only a great way to learn these technologies but also a great thing to talk about in interviews. "I fixed the webRTC feature that has been stagnant for 5 years", "I addressed the technical debt and reorganized the state...", or "Increased the quality of typeScript". These all show maturity as a developer and will allow us to focus the entire time of OSP on the final 20% problems. +There are many parts of the codebase that break DRY principles, and with such a large application, really keep in mind when you add features to ask if it is completely necessary. Past iterators added an experimental feature(s) without it fully working and the following team(s) would add their own experimental feature. Fixing features the past teams couldn't get to is not only a great way to learn these technologies but also a great thing to talk about in interviews. "I fixed the webRTC feature that has been stagnant for 5 years", "I addressed the technical debt and reorganized the state...", or "Increased the quality of typeScript". These all show maturity as a developer and will allow us to focus the entire time of OSP on the final 20% problems. -Legacy Components - As a part of a clean up effort, all files that are no longer being used have been moved to the legacy component folder. Examples of these files come from the migration to shared components. The original location of the components is mentioned in the comments of the relocated files. +Legacy Components - As a part of a cleanup effort, all files that are no longer being used have been moved to the legacy component folder. Examples of these files come from the migration to shared components. The original location of the components is mentioned in the comments of the relocated files. ### _Ensure consistent redux state management_ @@ -250,7 +251,7 @@ Finally, if future iterators would like to completely cover the list of API-test --- -## Backlog from Iteration Group v1.18 +## Backlog from Iteration Groups v1.18 and v1.19 - Fix/Update GitHub Actions for Unit Testing - Create a feature/function/endpoint to delete a mock route @@ -267,7 +268,7 @@ Finally, if future iterators would like to completely cover the list of API-test - Convert WebRTCSessionEntryForm to MUI - Convert WebRTC components to all use material UI as per line 7 of WebRTCComposer.tsx - Combine newRequestSlice.ts and newRequestFieldSlice.ts -- Update Excalidraw if necessary with new features/redux changes +- Update Excalidraw as necessary with new features/redux changes - ErrorBoundary.tsx may not be functional or necessary (Leave for now) --- diff --git a/package.json b/package.json index f8133ee47..b427f2480 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "swell", - "version": "1.18.0", + "version": "1.19.0", "description": "Swell", "main": "main.js", "repository": "https://github.com/open-source-labs/Swell", @@ -47,7 +47,7 @@ "preload.js", "src/server/*" ], - "nodeVersion": "16.15.0", + "nodeVersion": "18.18.0", "nsis": { "createDesktopShortcut": "always" }, @@ -91,7 +91,7 @@ "license": "MIT", "homepage": "http://www.getswell.io", "engines": { - "node": ">=16.15.0", + "node": ">=18.0.0", "npm": ">=7.0.0" }, "dependencies": { @@ -139,13 +139,14 @@ "graphql-tag": "^2.12.6", "graphql-ws": "^5.8.1", "highland": "^2.13.5", - "isolated-vm": "^4.6.0", + "isolated-vm": "^5.0.3", "jest-environment-jsdom": "^29.7.0", "mali": "^0.46.1", "ngrok": "^4.3.1", "node-fetch": "^3.3.0", + "node-gyp": "^11.0.0", "npm": "^8.7.0", - "patch-package": "^6.4.7", + "patch-package": "^6.5.1", "path": "^0.12.7", "prop-types": "^15.8.1", "react": "^18.0.0", @@ -154,6 +155,7 @@ "react-dom": "^18.0.0", "react-dropzone": "^12.1.0", "react-github-btn": "^1.2.2", + "react-icons": "^5.4.0", "react-redux": "^8.0.1", "react-router-dom": "^6.3.0", "react-split": "^2.0.14", @@ -569,7 +571,26 @@ { "name": "Nitesh Manem", "url": "https://github.com/NManem" + }, + { + "name": "Kiki Hunt", + "url": "https://github.com/Iloveeverything" + }, + { + "name": "Isaac Mbambo", + "url": "https://github.com/IM236" + }, + { + "name": "Ting Lee", + "url": "https://github.com/tingEng" + }, + { + "name": "Rachel Dean", + "url": "https://github.com/rchldn" + }, + { + "name": "Kadeem Reid", + "url": "https://github.com/Kadeem929" } ] } - diff --git a/src/assets/style/WebRtc.css b/src/assets/style/WebRtc.css new file mode 100644 index 000000000..2cd66aaea --- /dev/null +++ b/src/assets/style/WebRtc.css @@ -0,0 +1,94 @@ +.toggle-refresh-container { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + min-height: 40px; +} + +.refresh-button { + width: 70px; + height: 40px; + border-radius: 30px; + border-style: none; + background-color: #58a4b0; +} + +.refresh-button:hover { + box-shadow: 2px 2px 1px rgba(0, 0, 0, 0.75); +} + +.Audio-Toggle-Container { + display: flex; + justify-content: flex-start; + align-items: center; + gap: 10px; + min-width: 150px; +} + +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +.slider-on { + background-color: #58a4b0 !important; +} + +.slider:before { + position: absolute; + content: ''; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: 0.4s; + transition: 0.4s; +} + +input:focus + .slider { + box-shadow: 0 0 1px #ccc; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +.slider.round { + border-radius: 5px; +} + +.slider.round:before { + border-radius: 50%; +} + +/* .is-3rem-footer.is-clickable.is-margin-top-auto { + display: flex; + justify-content: flex-end; + padding: 0; +} + */ + diff --git a/src/client/components/main/WebRTC-composer/WebRTCComposer.tsx b/src/client/components/main/WebRTC-composer/WebRTCComposer.tsx index cb45e5b61..d3f689428 100644 --- a/src/client/components/main/WebRTC-composer/WebRTCComposer.tsx +++ b/src/client/components/main/WebRTC-composer/WebRTCComposer.tsx @@ -14,7 +14,10 @@ import NewRequestButton from '../sharedComponents/requestButtons/NewRequestButto import { Box } from '@mui/material'; import WebRTCVideoBox from './WebRTCVideoBox'; import { RootState } from '../../../toolkit-refactor/store'; -import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; +import { + useAppDispatch, + useAppSelector, +} from '../../../toolkit-refactor/hooks'; import { composerFieldsReset } from '../../../toolkit-refactor/slices/newRequestSlice'; import { setWorkspaceActiveTab } from '../../../toolkit-refactor/slices/uiSlice'; import { reqResItemAdded } from '../../../toolkit-refactor/slices/reqResSlice'; @@ -40,7 +43,7 @@ export default function WebRTCComposer() { checkSelected: false, request: newRequestWebRTC, response: { - webRTCMessages: [] + webRTCMessages: [], }, checked: false, minimized: false, @@ -56,20 +59,33 @@ export default function WebRTCComposer() { ) return true; } catch { - return false + return false; } return false; - } + }; const addNewRequest = (): void => { - if (!(checkValidSDP(newRequestWebRTC.webRTCOffer) && checkValidSDP(newRequestWebRTC.webRTCAnswer))){ - return alert('Invalid offer or answer SDP') + console.log('newRequestWebRTCatANR:', newRequestWebRTC); + if ( + !( + checkValidSDP(newRequestWebRTC.webRTCOffer) && + checkValidSDP(newRequestWebRTC.webRTCAnswer) + ) + ) { + return alert('Invalid offer or answer SDP'); } + // let localStream = peerConnection.createDataChannel('textChannel'); + // // localStream.onopen = () => console.log('data channel opened'); + // // localStream.onclose = () => console.log('data channel closed') + // localStream.addEventListener("open", (event) => { + // beginTransmission(localStream); + // }); + const reqRes: ReqRes = composeReqRes(); // addHistory removed because RTCPeerConnection objects cant typically be cloned // historyController.addHistoryToIndexedDb(reqRes); - + console.log('reqRes:', reqRes); dispatch(reqResItemAdded(reqRes)); dispatch(composerFieldsReset()); setShowRTCEntryForms(false); @@ -87,17 +103,18 @@ export default function WebRTCComposer() { style={{ overflowX: 'hidden' }} > + {showRTCEntryForms && ( <>
+ {newRequestWebRTC.webRTCDataChannel === 'Video' && ( +
+ +
+ )}
- {newRequestWebRTC.webRTCDataChannel === 'Video' && ( -
- -
- )} )} diff --git a/src/client/components/main/WebRTC-composer/WebRTCServerEntryForm.tsx b/src/client/components/main/WebRTC-composer/WebRTCServerEntryForm.tsx index 0145b8fc9..9ed04a426 100644 --- a/src/client/components/main/WebRTC-composer/WebRTCServerEntryForm.tsx +++ b/src/client/components/main/WebRTC-composer/WebRTCServerEntryForm.tsx @@ -1,4 +1,10 @@ import React from 'react'; +import { useState, useRef, useEffect } from 'react'; +import { MdRefresh } from 'react-icons/md'; + +// import '/Users/katharinehunt/Swell/src/assets/style/WebRtcEntry.css'; +import '../../../../assets/style/WebRtc.css'; + // import dropDownArrow from '../../../../assets/icons/arrow_drop_down_white_192x192.png'; // import CodeMirror from '@uiw/react-codemirror'; // import { EditorView } from '@codemirror/view'; @@ -6,12 +12,16 @@ import React from 'react'; // import { vscodeDark } from '@uiw/codemirror-theme-vscode'; // import Select from '@mui/material/Select'; // import MenuItem from '@mui/material/MenuItem'; -import { RequestWebRTC } from '../../../../types'; +import { ReqRes, RequestWebRTC } from '../../../../types'; import TextCodeArea from '../sharedComponents/TextCodeArea'; -import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; -import { newRequestWebRTCSet } from '../../../toolkit-refactor/slices/newRequestSlice'; +import { + useAppDispatch, + useAppSelector, +} from '../../../toolkit-refactor/hooks'; +import { resetWebRTCconnection, newRequestWebRTCSet } from '../../../toolkit-refactor/slices/newRequestSlice'; import webrtcPeerController from '../../../controllers/webrtcPeerController'; import { RootState } from '../../../toolkit-refactor/store'; +import { compose } from 'redux'; // const jBeautify = require('js-beautify').js; @@ -23,22 +33,84 @@ interface Props { } const WebRTCServerEntryForm: React.FC = (props: Props) => { + const [isToggled, setIsToggled] = useState(false); + const dispatch = useAppDispatch(); const newRequestWebRTC: RequestWebRTC = useAppSelector( (store: RootState) => store.newRequest.newRequestWebRTC ); + const currentReqRes = useAppSelector( + (store: RootState) => store.reqRes.currentResponse + ) as ReqRes; + + const hasResetRef = useRef(false); + + + const handleResetWebRTCconnection = () => { + dispatch(resetWebRTCconnection()); + console.log('WebRTC connection reset to initial state:'); + console.log('newRequestWebRTCFromConnect:', { + newRequestWebRTC: newRequestWebRTC, // This will be the empty reset state + }); + hasResetRef.current = true; + } + + useEffect(() => {// so we only trigger a new peer connection here for the reset if the offer has been cleared specifically via our reset function + if (hasResetRef.current && newRequestWebRTC.webRTCOffer === '') { + console.log('Creating a new Peer Connection with reset state'); + webrtcPeerController.createPeerConnection(newRequestWebRTC, currentReqRes); + hasResetRef.current = false; + } + }, [newRequestWebRTC, currentReqRes]); return (
+
+
+ {newRequestWebRTC.webRTCDataChannel === 'Video' && ( + <> + + Audio + + + + )} +
+ +
+ +
+
{ + console.log('value before dispatch:', value); dispatch( newRequestWebRTCSet({ ...newRequestWebRTC, webRTCOffer: value }) ); + console.log( + 'value after dispatch, Im assuming it is the same:', + value + ); }} placeholder={'Click "Get Offer" or paste in Offer SDP'} readOnly={true} @@ -66,14 +138,15 @@ const WebRTCServerEntryForm: React.FC = (props: Props) => { width: '58px', }} onClick={() => { - navigator.clipboard.readText().then((text) => + navigator.clipboard.readText().then((text) => { + console.log('text:', text); dispatch( newRequestWebRTCSet({ ...newRequestWebRTC, webRTCOffer: text, }) - ) - ); + ); + }); }} > Paste @@ -82,11 +155,23 @@ const WebRTCServerEntryForm: React.FC = (props: Props) => { className="button is-normal is-primary-100 add-request-button no-border-please" style={{ margin: '10px' }} onClick={() => { + console.log('newRequestWebRTCfromOclick:', newRequestWebRTC); webrtcPeerController.createOffer(newRequestWebRTC); }} > Get Offer +
{/* Code box for Answer */}
@@ -94,10 +179,15 @@ const WebRTCServerEntryForm: React.FC = (props: Props) => { mode={'application/json'} value={newRequestWebRTC.webRTCAnswer || ''} height={'85px'} + width={'100%'} onChange={(value, viewUpdate) => { dispatch( newRequestWebRTCSet({ ...newRequestWebRTC, webRTCAnswer: value }) ); + console.log( + 'newRequestWebRTC (though may not be updated bc async):', + newRequestWebRTC + ); }} placeholder={'Answer here'} readOnly={true} @@ -137,17 +227,18 @@ const WebRTCServerEntryForm: React.FC = (props: Props) => { > Paste - {/* ANSWER BUTTON IS WORK-IN-PROGRESS */} - {/* */} + + {/* {warningMessage ?
{warningMessage.body}
: null} */}
diff --git a/src/client/components/main/WebRTC-composer/WebRTCSessionEntryForm.tsx b/src/client/components/main/WebRTC-composer/WebRTCSessionEntryForm.tsx index d9a8c29b1..18800562e 100644 --- a/src/client/components/main/WebRTC-composer/WebRTCSessionEntryForm.tsx +++ b/src/client/components/main/WebRTC-composer/WebRTCSessionEntryForm.tsx @@ -1,9 +1,11 @@ import React, { useState } from 'react'; -import { NewRequestWebRTCSet, RequestWebRTC } from '../../../../types'; +import { NewRequestWebRTCSet, RequestWebRTC, ReqRes } from '../../../../types'; import webrtcPeerController from '../../../controllers/webrtcPeerController'; - import dropDownArrow from '../../../../assets/icons/arrow_drop_down_white_192x192.png'; -import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; +import { + useAppDispatch, + useAppSelector, +} from '../../../toolkit-refactor/hooks'; import { newRequestWebRTCSet } from '../../../toolkit-refactor/slices/newRequestSlice'; import { RootState } from '../../../toolkit-refactor/store'; @@ -16,8 +18,13 @@ const WebRTCSessionEntryForm: React.FC = (props: Props) => { const newRequestWebRTC: RequestWebRTC = useAppSelector( (store: RootState) => store.newRequest.newRequestWebRTC ); + const currentReqRes = useAppSelector( + (store: RootState) => store.reqRes.currentResponse + ) as ReqRes; - const isDark = useAppSelector((store: { ui: { isDark: boolean }}) => store.ui.isDark); + const isDark = useAppSelector( + (store: { ui: { isDark: boolean } }) => store.ui.isDark + ); const { setShowRTCEntryForms } = props; const [entryTypeDropdownIsActive, setEntryTypeDropdownIsActive] = @@ -25,6 +32,9 @@ const WebRTCSessionEntryForm: React.FC = (props: Props) => { const [dataTypeDropdownIsActive, setDataTypeDropdownIsActive] = useState(false); + // may have to have a connect button for peer 1 and a different one for peer 2 + // this is because peer 2 does not need to create a data channel, rather they receive one from peer 1 + return (
= (props: Props) => { className="ml-1 is-rest button no-border-please" onClick={() => { setShowRTCEntryForms(true); - webrtcPeerController.createPeerConnection(newRequestWebRTC); + console.log('newRequestWebRTCFromConnect:', { + newRequestWebRTC: newRequestWebRTC, + }); + webrtcPeerController.createPeerConnection( + newRequestWebRTC, + currentReqRes + ); }} > Connect @@ -140,49 +160,57 @@ const WebRTCSessionEntryForm: React.FC = (props: Props) => { ); }; - +// update export default WebRTCTextContainer; diff --git a/src/client/components/main/sharedComponents/TextCodeArea.tsx b/src/client/components/main/sharedComponents/TextCodeArea.tsx index ce7eeac26..5e2258d52 100644 --- a/src/client/components/main/sharedComponents/TextCodeArea.tsx +++ b/src/client/components/main/sharedComponents/TextCodeArea.tsx @@ -13,6 +13,7 @@ interface TextCodeAreaProps { mode: string; onChange: (value: string, viewUpdate: ViewUpdate) => void; height?: string; + width?: string; placeholder?: string; readOnly?: boolean; } @@ -38,6 +39,7 @@ export default function TextCodeArea({ height = '200px', placeholder = 'Enter body here', readOnly = false, + width, }: TextCodeAreaProps) { const lang: string = mode.substring(mode.indexOf('/') + 1); // Grab language mode based on value passed in return ( @@ -45,6 +47,7 @@ export default function TextCodeArea({ { className="is-flex-basis-0 is-flex-grow-1 button is-primary-100 is-size-7 border-curve" id={`send-button-${index}`} onClick={() => { + console.log('content:',content); if (content.request.network === 'webrtc') { dispatch(setResponsePaneActiveTab('webrtc')); dispatch(responseDataSaved(content)); - webrtcPeerController.addAnswer(content, currentResponse); + console.log('currentResponse:',currentResponse); + webrtcPeerController.dataStream(content, currentResponse); setWebRTCSend(true); // connectionController.setReqResConnectionToClosed(content.id); } else if (content.graphQL && request.method === 'SUBSCRIPTION') { diff --git a/src/client/controllers/webrtcPeerController.ts b/src/client/controllers/webrtcPeerController.ts index 626529729..6d8177539 100644 --- a/src/client/controllers/webrtcPeerController.ts +++ b/src/client/controllers/webrtcPeerController.ts @@ -2,6 +2,7 @@ import store, { appDispatch } from '../toolkit-refactor/store'; import { newRequestWebRTCSet, newRequestWebRTCOfferSet, + newRequestWebRTCAnswerSet, } from '../toolkit-refactor/slices/newRequestSlice'; import { ReqRes, @@ -11,17 +12,16 @@ import { ResponseWebRTCText, } from '../../types'; import { responseDataSaved } from '../toolkit-refactor/slices/reqResSlice'; +import { send } from 'process'; const webrtcPeerController = { createPeerConnection: async ( - newRequestWebRTC: RequestWebRTC + newRequestWebRTC: RequestWebRTC, + currentReqRes: ReqRes ): Promise => { let servers = { iceServers: [ { - urls: [ - 'stun:stun1.1.google.com:19302', - 'stun:stun2.1.google.com:19302', - ], + urls: ['stun:stun.l.google.com:19302', 'stun:stun.l.google.com:5349'], }, ], }; @@ -61,19 +61,67 @@ const webrtcPeerController = { peerConnection.onicecandidate = async ( event: RTCPeerConnectionIceEvent ): Promise => { - if (event.candidate) { + if ( + event.candidate && + peerConnection.localDescription!.type === 'offer' + ) { appDispatch( newRequestWebRTCOfferSet( JSON.stringify(peerConnection.localDescription) ) ); + } else if ( + event.candidate && + peerConnection.localDescription!.type === 'answer' + ) { + appDispatch( + newRequestWebRTCAnswerSet( + JSON.stringify(peerConnection.localDescription) + ) + ); } }; } else if (newRequestWebRTC.webRTCDataChannel === 'Text') { + // const { request, response } = currentReqRes as { + // request: RequestWebRTCText; + // response: ResponseWebRTCText; + // }; + let localStream = peerConnection.createDataChannel('textChannel'); - localStream.onopen = () => console.log('data channel opened'); - localStream.onclose = () => console.log('data channel closed') + localStream.onopen = () => console.log('data channel opened!!!'); + localStream.onclose = () => console.log('data channel closed :('); + peerConnection.ondatachannel = (event) => { + const receiveChannel = event.channel; + // receiveChannel.onmessage = (event) => { + // console.log('message received:', event.data); + + // }; + receiveChannel.onmessage = (event: MessageEvent) => { + let newString = event.data.slice(1, -1); + let messageObject = { + data: newString, + timeReceived: Date.now(), + }; + + let state = store.getState(); + if (state.reqRes.currentResponse.response) { + let newWebRTCMessages = (( + state.reqRes.currentResponse.response + )).webRTCMessages.concat(messageObject); + let request = state.reqRes.currentResponse.request; + appDispatch( + responseDataSaved({ + ...currentReqRes, + request, + response: { + webRTCMessages: newWebRTCMessages, + }, + }) + ); + } + }; + }; appDispatch( newRequestWebRTCSet({ ...newRequestWebRTC, @@ -85,31 +133,49 @@ const webrtcPeerController = { peerConnection.onicecandidate = async ( event: RTCPeerConnectionIceEvent ): Promise => { - if (event.candidate) { + if ( + event.candidate && + peerConnection.localDescription!.type === 'offer' + ) { + //debugged appDispatch( newRequestWebRTCOfferSet( + //should we be adding a ...peerConnection here spreading out the rest of the peerConnection object? also why isn't this updating the newWebRTCRequest object? JSON.stringify(peerConnection.localDescription) ) ); + } else if ( + event.candidate && + peerConnection.localDescription!.type === 'answer' + ) { + //added this plus an answerSet reducer so the answer wasn't populating both the answer and offer text boxes (and being two different versions of the answer at that) + appDispatch( + newRequestWebRTCAnswerSet( + JSON.stringify(peerConnection.localDescription) + ) + ); + console.log('eventIceCandidate:', event.candidate.candidate); } }; } }, - + // what in create offer triggers the ice candidate to be sent? createOffer: async (newRequestWebRTC: RequestWebRTC): Promise => { //grab the peer connection off the state to manipulate further let { webRTCpeerConnection } = newRequestWebRTC; + console.log('webRTCPeerConnect:', webRTCpeerConnection); let offer = await webRTCpeerConnection!.createOffer(); - await webRTCpeerConnection!.setLocalDescription(offer); + console.log('offer:', offer); + await webRTCpeerConnection!.setLocalDescription(offer); //what is this line doing that is not already done? appDispatch( newRequestWebRTCSet({ + // newRequestWebRTCSet mutates the newRequestWebRTC state to have the offer ...newRequestWebRTC, webRTCOffer: JSON.stringify(offer), }) ); }, - // work-in-progress createAnswer: async (newRequestWebRTC: RequestWebRTC): Promise => { let { webRTCpeerConnection, webRTCOffer } = newRequestWebRTC; @@ -119,6 +185,7 @@ const webrtcPeerController = { await webRTCpeerConnection.setRemoteDescription(offer); let answer = await webRTCpeerConnection.createAnswer(); + console.log('answer:', answer); await webRTCpeerConnection.setLocalDescription(answer); appDispatch( @@ -127,18 +194,31 @@ const webrtcPeerController = { webRTCAnswer: JSON.stringify(answer), }) ); + console.log('newRequestWebRTCCheckAfterAnswer:', newRequestWebRTC); + }, + + addAnswer: async (newRequestWebRTC: RequestWebRTC): Promise => { + let { webRTCpeerConnection } = newRequestWebRTC; + let answer = JSON.parse(newRequestWebRTC.webRTCAnswer); + await webRTCpeerConnection!.setRemoteDescription(answer); }, - addAnswer: async (reqRes: ReqRes): Promise => { + sendMessages: async (reqRes: ReqRes, messages: string): Promise => { + let { request } = reqRes as { request: RequestWebRTCText }; + console.log('im here too'); + console.log('request from mesaages :', request); + + (request).webRTCLocalStream!.send( + JSON.stringify({ data: messages }) + ); + }, + + dataStream: async (reqRes: ReqRes): Promise => { let { request, response } = reqRes as { request: RequestWebRTC; response: ResponseWebRTC; }; - request.webRTCpeerConnection!.setRemoteDescription( - JSON.parse(request.webRTCAnswer) - ); - if (request.webRTCDataChannel === 'Video') { request.webRTCpeerConnection!.ontrack = async (event: RTCTrackEvent) => { event.streams[0].getTracks().forEach((track: MediaStreamTrack) => { @@ -174,11 +254,10 @@ const webrtcPeerController = { let state = store.getState(); if (state.reqRes.currentResponse.response) { - let newWebRTCMessages = - (state.reqRes.currentResponse.response).webRTCMessages.concat( - messageObject - ); - let request = state.reqRes.currentResponse.request + let newWebRTCMessages = (( + state.reqRes.currentResponse.response + )).webRTCMessages.concat(messageObject); + let request = state.reqRes.currentResponse.request; appDispatch( responseDataSaved({ ...reqRes, diff --git a/src/client/toolkit-refactor/slices/newRequestSlice.ts b/src/client/toolkit-refactor/slices/newRequestSlice.ts index 09f8d5515..308c2e062 100644 --- a/src/client/toolkit-refactor/slices/newRequestSlice.ts +++ b/src/client/toolkit-refactor/slices/newRequestSlice.ts @@ -30,7 +30,6 @@ type NewRequestStore = { newRequestBody: NewRequestBody; newRequestSSE: NewRequestSSE; newRequestWebRTC: RequestWebRTC; - }; const initialState: NewRequestStore = { @@ -47,7 +46,7 @@ const initialState: NewRequestStore = { bodyIsNew: false, }, newRequestStreams: { - streamsArr: [], + streamsArr: [], count: 0, streamContent: [], selectedPackage: null, @@ -79,7 +78,7 @@ const initialState: NewRequestStore = { webRTCLocalStream: null, webRTCRemoteStream: null, webRTCMessages: [], - } + }, }; const newRequestSlice = createSlice({ @@ -101,11 +100,18 @@ const newRequestSlice = createSlice({ newRequestWebRTCSet: (state, action: PayloadAction) => { state.newRequestWebRTC = action.payload; + // console.log('newRequestWebRTCCheckAfterAnswerInReducer:', state.newRequestWebRTC.webRTCAnswer); }, newRequestWebRTCOfferSet: (state, action: PayloadAction) => { state.newRequestWebRTC.webRTCOffer = action.payload; }, + newRequestWebRTCAnswerSet: (state, action: PayloadAction) => { + state.newRequestWebRTC.webRTCAnswer = action.payload; + }, + resetWebRTCconnection: () => { + return initialState; + }, //Before toolkit conversion was SET_NEW_REQUEST_STREAMS or setNewRequestStreams newRequestStreamsSet: (state, action: PayloadAction) => { @@ -188,6 +194,8 @@ export const { newRequestContentByProtocol, newRequestWebRTCSet, newRequestWebRTCOfferSet, + newRequestWebRTCAnswerSet, + resetWebRTCconnection, } = newRequestSlice.actions; export default newRequestSlice.reducer; diff --git a/src/server/server.js b/src/server/server.js index fa07fd840..e7b03a62c 100644 --- a/src/server/server.js +++ b/src/server/server.js @@ -6,7 +6,7 @@ const cookieParser = require('cookie-parser'); const crypto = require('crypto'); dotenv.config(); -const port = 3000; +const port = 3001; const app = express(); const cors = require('cors'); app.use(express.urlencoded({ extended: true })); diff --git a/src/types.ts b/src/types.ts index a01c38eaa..526e47dd1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -150,7 +150,7 @@ export interface Message { data: string; } -export interface NewRequestBody { +export interface NewRequestBody { bodyContent: string; bodyVariables: string; bodyType: string; @@ -325,7 +325,10 @@ export interface ResponseWebRTCText { webRTCMessages: WebMessages[]; } -export type RequestWebRTC = RequestWebRTCVideo | RequestWebRTCText; +export type RequestWebRTC = + | RequestWebRTCVideo + | RequestWebRTCText + | RequestWebRTCAudio; export interface RequestWebRTCVideo { network: 'webrtc'; @@ -339,6 +342,18 @@ export interface RequestWebRTCVideo { webRTCRemoteStream: MediaStream | null; } +export interface RequestWebRTCAudio { + network: 'webrtc'; + webRTCEntryMode: 'Manual' | 'WS'; + webRTCDataChannel: 'Audio'; + webRTCWebsocketServer: string; + webRTCOffer: string; + webRTCAnswer: string; + webRTCpeerConnection: RTCPeerConnection | null; + webRTCLocalStream: MediaStream | null; + webRTCRemoteStream: MediaStream | null; +} + export interface RequestWebRTCText { network: 'webrtc'; webRTCEntryMode: 'Manual' | 'WS'; diff --git a/webpack.config.js b/webpack.config.js index c31f08d20..a336f60ca 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -129,8 +129,8 @@ module.exports = { 'base-uri': ["'self'"], 'default-src': [ "'self'", - 'http://localhost:3000', - 'ws://localhost:3000', + 'http://localhost:3001', + 'ws://localhost:3001', 'https://api.github.com', "'unsafe-inline'", "'unsafe-eval'", diff --git a/webpack.development.js b/webpack.development.js index 1b734d578..cf87d7e82 100644 --- a/webpack.development.js +++ b/webpack.development.js @@ -13,16 +13,16 @@ module.exports = merge(base, { compress: true, proxy: { '/webhookServer': { - target: 'http://localhost:3000', + target: 'http://localhost:3001', }, '/webhook': { - target: 'http://localhost:3000', + target: 'http://localhost:3001', }, '/api': { - target: 'http://localhost:3000', + target: 'http://localhost:3001', /** * @todo Change secure option to true, and refactor code to account for - * change + * change // https://github.com/electron/electron/issues/19775 ??? maybe this is the solution */ secure: false, }, @@ -31,6 +31,7 @@ module.exports = merge(base, { if (!devServer) { throw new Error('webpack-dev-server is not defined'); } + // console.log('Setting up middlewares...:', middlewares); middlewares.unshift({ // unshift does not work, ends in infinite calls to this function name: 'run-in-electron',