Skip to content

Commit 7046e63

Browse files
authored
Merge pull request #13 from oslabs-beta/npmpackage
Npmpackage
2 parents f145946 + 55c71b0 commit 7046e63

29 files changed

+15947
-6
lines changed

.eslintignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# don't lint build or dist output
2+
dist
3+
build
4+
tsconfig.json

.eslintrc.json

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"env": {
3+
"browser": true,
4+
"es2021": true
5+
},
6+
"extends": [
7+
"eslint:recommended",
8+
"plugin:react/recommended",
9+
"plugin:@typescript-eslint/recommended"
10+
],
11+
"parser": "@typescript-eslint/parser",
12+
"parserOptions": {
13+
"ecmaFeatures": {
14+
"jsx": true
15+
},
16+
"ecmaVersion": "latest",
17+
"sourceType": "module"
18+
},
19+
"plugins": [
20+
"react",
21+
"@typescript-eslint"
22+
],
23+
"rules": {
24+
"indent": ["warn", 2, {
25+
"ignoreComments": false,
26+
"SwitchCase": 1,
27+
"MemberExpression": 0,
28+
"flatTernaryExpressions": true
29+
}],
30+
"no-unused-vars": ["off", { "vars": "local" }],
31+
"no-case-declarations": "warn",
32+
"prefer-const": "warn",
33+
"quotes": ["warn", "single"],
34+
"react/prop-types": "off",
35+
"semi": ["warn", "always"],
36+
"space-infix-ops": "warn"
37+
}
38+
}

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
build
2+
npm-debug.log
3+
.DS_Store
4+
.env
5+
node_modules

.npmignore

Whitespace-only changes.

.prettierrc

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"semi": true,
3+
"trailingComma": "none",
4+
"singleQuote": true,
5+
"printWidth": 80
6+
}

README.md

+25-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
# RTConnect
2+
3+
RTConnect is an easy-to-use React component library that enables developers to set up live, real-time video calls, between multiple connected peers on different computers in a React app.
4+
5+
RTConnect achieves these features within the functional scope of React components by simplifying the implementation of WebRTC and Websockets to establish low latency, real-time communications for developers.
6+
7+
# Installation
8+
9+
```
10+
npm install rtconnect
11+
```
12+
After installing the rtconnect npm package, import the Rtconnect component in your React file:
13+
114
<p align="center">
215
<img src='' alt="logo" width="300">
316
<br>
@@ -26,11 +39,15 @@ npm install rtconnect
2639

2740
## <a name="implementation"/> Implementation
2841
After installing the rtconnect npm package, import the VideoComponent component in your React file:
42+
43+
Then use the RTConnect component as you would any other React component and include your URL.
44+
2945
```
30-
import { Rtconnect } from ‘rtconnect’;
46+
<Rtconnect URL={'localhost:PORT'}>
3147
```
3248

33-
Then use the RTConnect component as you would any other React component and include your URL.
49+
For example, if your server is hosted on port 3000, then you would do the following:
50+
3451
```
3552
<Rtconnect URL={'localhost:PORT'}>
3653
```
@@ -40,16 +57,19 @@ For example, if your server is hosted on port 3000, then you would do the follow
4057
<Rtconnect URL={'localhost:3000'}>
4158
```
4259

60+
61+
The Team
62+
Anthony King | [GitHub](https://github.com/thecapedcrusader) | [LinkedIn](https://www.linkedin.com/in/aking97)
63+
<br>
4364
## <a name="demo"/> Demo
4465

4566

46-
## <a name="team "/> The Team
67+
## <a name="team "/> # The Co-Creators of RTConnect
68+
4769
F. Raisa Iftekher | [GitHub](https://github.com/fraisai) | [LinkedIn](https://www.linkedin.com/in/fraisa/)
4870
<br>
4971
Yoojin Chang | [GitHub](https://github.com/ychang49265) | [LinkedIn](https://www.linkedin.com/in/yoojin-chang-32a75892/)
5072
<br>
5173
Louis Disen | [GitHub](https://github.com/LouisDisen) | [LinkedIn](https://www.linkedin.com/in/louis-disen/)
5274
<br>
5375
Anthony King | [GitHub](https://github.com/thecapedcrusader) | [LinkedIn](www.linkedin.com/in/aking97)
54-
55-

client/App.jsx

-1
This file was deleted.

dist/server/server.d.ts

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/// <reference types="node" />
2+
/// <reference types="node" />
3+
import { WebSocket, WebSocketServer } from 'ws';
4+
import { Server } from 'http';
5+
import { Server as httpsServer } from 'https';
6+
/**
7+
* @class
8+
* @classdesc Class representing the SignalingChannel using websockets to allow communication between clients connected to the websocket server
9+
* @prop { WebsocketServer } websocketServer - a simple websocket server
10+
* @prop { Map } users - an object of users in the following fashion { username1: socket1, username2: socket2, usernameN: socketN, ... }
11+
*/
12+
declare class SignalingChannel {
13+
webSocketServer: WebSocketServer;
14+
users: Map<string, WebSocket>;
15+
/**
16+
*
17+
* @constructor constructing a websocket server with an https object passed in upon instantiating SignalingChannel
18+
* @param {Server} server - pass in a server (http or https), or pass in port (not the same port (this port can't be the same as the application port and has to listen on the same port in rtconnect!)
19+
*/
20+
constructor(server: Server | httpsServer | number);
21+
/**
22+
* @description Upon creation and connection to the websocket server, the websocket server will add these event listeners to their socket to perform key functionality
23+
* @function initializeConnection Signaling server will listen to client when client has been connected.
24+
* when the message event is triggered, it will either send each user list to each user upon login or sending the receiver the data
25+
* @return a socket that corresponds to the client conencting.
26+
*/
27+
initializeConnection(): void;
28+
/**
29+
* @description Broadcasting from sender to receiver. Accessing the receiver from the data object and if the user exists, the data is sent
30+
* @param {object} data
31+
*/
32+
transmit(data: {
33+
ACTION_TYPE: string;
34+
receiver: string;
35+
}): void;
36+
/**
37+
* @description Getting user from Map
38+
* @function getByValue identifies user and their specific websocket
39+
* @param {Map} map
40+
* @param {WebSocket} searchValue
41+
* @returns {string} user
42+
*/
43+
getByValue(map: Map<string, WebSocket>, searchValue: WebSocket): string;
44+
}
45+
export default SignalingChannel;

dist/server/server.js

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
const ws_1 = require("ws");
7+
const actions_1 = __importDefault(require("../src/constants/actions"));
8+
const { OFFER, ANSWER, ICECANDIDATE, LOGIN, LEAVE } = actions_1.default;
9+
/**
10+
* @class
11+
* @classdesc Class representing the SignalingChannel using websockets to allow communication between clients connected to the websocket server
12+
* @prop { WebsocketServer } websocketServer - a simple websocket server
13+
* @prop { Map } users - an object of users in the following fashion { username1: socket1, username2: socket2, usernameN: socketN, ... }
14+
*/
15+
class SignalingChannel {
16+
/**
17+
*
18+
* @constructor constructing a websocket server with an https object passed in upon instantiating SignalingChannel
19+
* @param {Server} server - pass in a server (http or https), or pass in port (not the same port (this port can't be the same as the application port and has to listen on the same port in rtconnect!)
20+
*/
21+
constructor(server) {
22+
this.webSocketServer = typeof server === 'number' ? new ws_1.WebSocket.Server({ port: server }) : new ws_1.WebSocket.Server({ server: server });
23+
this.users = new Map();
24+
// this.rooms = new Map(); //focus on later when constructing 2+ video conferencing functionality, SFU topology
25+
}
26+
/**
27+
* @description Upon creation and connection to the websocket server, the websocket server will add these event listeners to their socket to perform key functionality
28+
* @function initializeConnection Signaling server will listen to client when client has been connected.
29+
* when the message event is triggered, it will either send each user list to each user upon login or sending the receiver the data
30+
* @return a socket that corresponds to the client conencting.
31+
*/
32+
initializeConnection() {
33+
this.webSocketServer.on('connection', (socket) => {
34+
console.log('A user has connected to the websocket server.');
35+
// when a client closes their browser or connection to the websocket server (onclose), their socket gets terminated and they are removed from the map of users
36+
// lastly a new user list is sent out to all clients connected to the websocket server.
37+
socket.on('close', () => {
38+
const userToDelete = this.getByValue(this.users, socket);
39+
this.users.delete(userToDelete);
40+
socket.terminate();
41+
const userList = { ACTION_TYPE: LOGIN, payload: Array.from(this.users.keys()) };
42+
this.webSocketServer.clients.forEach(client => client.send(JSON.stringify(userList)));
43+
});
44+
// the meat of the websocket server, when messages are received from the client...
45+
// we will filter through what course of action to take based on data.ACTION_TYPE (see constants/actions.ts)
46+
socket.on('message', (message) => {
47+
// messages sent between the client and websocket server must be strings
48+
// importantly, messages sent to the websocket server are passed as Buffer objects encoded in utf-8 format
49+
const stringifiedMessage = message.toString('utf-8');
50+
const data = JSON.parse(stringifiedMessage);
51+
switch (data.ACTION_TYPE) {
52+
case OFFER:
53+
this.transmit(data);
54+
break;
55+
case ANSWER:
56+
this.transmit(data);
57+
break;
58+
case ICECANDIDATE:
59+
this.transmit(data);
60+
break;
61+
case LOGIN:
62+
this.users.set(data.payload, socket);
63+
this.webSocketServer.clients.forEach(client => client.send(JSON.stringify({
64+
ACTION_TYPE: LOGIN,
65+
payload: Array.from(this.users.keys())
66+
})));
67+
break;
68+
case LEAVE:
69+
this.transmit(data);
70+
break;
71+
default:
72+
console.error('error', data);
73+
break;
74+
}
75+
});
76+
});
77+
}
78+
/**
79+
* @description Broadcasting from sender to receiver. Accessing the receiver from the data object and if the user exists, the data is sent
80+
* @param {object} data
81+
*/
82+
transmit(data) {
83+
var _a;
84+
(_a = this.users.get(data.receiver)) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(data));
85+
}
86+
/**
87+
* @description Getting user from Map
88+
* @function getByValue identifies user and their specific websocket
89+
* @param {Map} map
90+
* @param {WebSocket} searchValue
91+
* @returns {string} user
92+
*/
93+
getByValue(map, searchValue) {
94+
let user = '';
95+
for (const [key, value] of map.entries()) {
96+
if (value === searchValue)
97+
user = key;
98+
}
99+
return user;
100+
}
101+
}
102+
exports.default = SignalingChannel;

dist/src/components/Socket.d.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/// <reference types="react" />
2+
declare type SocketType = {
3+
ws: WebSocket;
4+
getUsers: (parsedData: {
5+
payload: string[];
6+
}) => void;
7+
handleReceiveCall: (data: {
8+
sender: string;
9+
payload: RTCSessionDescriptionInit;
10+
}) => void;
11+
handleAnswer: (parsedData: {
12+
payload: RTCSessionDescriptionInit;
13+
}) => void;
14+
handleNewIceCandidate: (data: {
15+
payload: RTCIceCandidateInit;
16+
}) => void;
17+
endCall: (parsedData: boolean) => void;
18+
};
19+
/**
20+
* @desc Using the initial websocket connection, this functional component provides the event listeners for each client socket to allow bi-lateral communication.
21+
* @param props containing the socket starting the connection with the websocket server and functions to be performed on each switch case event
22+
* @returns an empty element when rendered but populates the client's socket connection with event listeners to be able to handle the offer-answer model and SDP objects being communicated between both peers.
23+
*/
24+
declare const Socket: ({ ws, getUsers, handleReceiveCall, handleAnswer, handleNewIceCandidate, endCall }: SocketType) => JSX.Element;
25+
export default Socket;

dist/src/components/Socket.js

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
const react_1 = __importDefault(require("react"));
7+
const actions_1 = __importDefault(require("../constants/actions"));
8+
const { LOGIN, ICECANDIDATE, OFFER, ANSWER, LEAVE } = actions_1.default;
9+
/**
10+
* @desc Using the initial websocket connection, this functional component provides the event listeners for each client socket to allow bi-lateral communication.
11+
* @param props containing the socket starting the connection with the websocket server and functions to be performed on each switch case event
12+
* @returns an empty element when rendered but populates the client's socket connection with event listeners to be able to handle the offer-answer model and SDP objects being communicated between both peers.
13+
*/
14+
const Socket = ({ ws, getUsers, handleReceiveCall, handleAnswer, handleNewIceCandidate, endCall }) => {
15+
// IIFE, this function gets invoked when a new socket component is created
16+
(function initalizeConnection() {
17+
ws.addEventListener('open', () => {
18+
console.log('Websocket connection has opened.');
19+
});
20+
ws.addEventListener('close', () => {
21+
console.log('Websocket connection closed.');
22+
});
23+
ws.addEventListener('error', (e) => {
24+
console.error('Socket Error:', e);
25+
});
26+
ws.addEventListener('message', message => {
27+
const parsedData = JSON.parse(message.data);
28+
switch (parsedData.ACTION_TYPE) {
29+
case LOGIN:
30+
getUsers(parsedData);
31+
break;
32+
case OFFER:
33+
handleReceiveCall(parsedData);
34+
break;
35+
case ANSWER:
36+
handleAnswer(parsedData);
37+
break;
38+
case ICECANDIDATE:
39+
handleNewIceCandidate(parsedData);
40+
break;
41+
case LEAVE:
42+
endCall(true);
43+
break;
44+
default:
45+
console.error('error', parsedData);
46+
break;
47+
}
48+
});
49+
})();
50+
// an empty jsx element is rendered because this component is only meant to initalize and load the client's socket connection with event listeners
51+
return (react_1.default.createElement(react_1.default.Fragment, null));
52+
};
53+
exports.default = Socket;

dist/src/components/VideoCall.d.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/// <reference types="react" />
2+
/**
3+
* @desc Wrapper component containing logic necessary for P2P connections using WebRTC (RTCPeerConnect API + MediaSession API) and Websockets.
4+
* Any client can call another thus not all functions are invoked for every user.
5+
* ws.current.send enqueues the specified messages that need to be transmitted to the server over the WebSocket connection, which we connected in our backend using RTConnect's importable SignalingChannel
6+
* @param {string} this.props.URL
7+
* @returns A component that renders two VideoComponents (currently not dynamic), a
8+
*/
9+
declare const VideoCall: ({ URL, mediaOptions }: {
10+
URL: string;
11+
mediaOptions: {
12+
controls: boolean;
13+
style: {
14+
width: string;
15+
height: string;
16+
};
17+
};
18+
}) => JSX.Element;
19+
export default VideoCall;

0 commit comments

Comments
 (0)