diff --git a/.husky/pre-commit b/.husky/pre-commit
index a481e02de..3c0fbeb62 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -4,4 +4,4 @@
echo "\nRunning GIT hooks..."
yarn cleanbuild
yarn nx affected --target=lint
-yarn nx affected --target=test
+# yarn nx affected --target=test
diff --git a/packages/examples/omegle-push-video/.gitignore b/packages/examples/omegle-push-video/.gitignore
new file mode 100644
index 000000000..dfcfdb385
--- /dev/null
+++ b/packages/examples/omegle-push-video/.gitignore
@@ -0,0 +1,4 @@
+/.dist
+
+./client/node_modules
+./server/node_modules
\ No newline at end of file
diff --git a/packages/examples/omegle-push-video/README.md b/packages/examples/omegle-push-video/README.md
new file mode 100644
index 000000000..0aa181007
--- /dev/null
+++ b/packages/examples/omegle-push-video/README.md
@@ -0,0 +1,65 @@
+# Omegle but Push Chat
+
+## Description
+
+This project randomly connects 2 peers online and opens up a Push chat between the peers.
+
+## Getting Started
+
+### Installation - Server
+
+1. Navigate to the server directory:
+
+ ```bash
+ cd server
+ ```
+
+2. Install dependencies using Yarn or npm:
+
+ ```bash
+ # Using Yarn
+ yarn
+
+ # Using npm
+ npm install
+ ```
+
+3. Start the Server:
+
+ ```bash
+ # Using Yarn
+ yarn start
+
+ # Using npm
+ npm start
+ ```
+
+#### The server will run on the specified port (default is 3001).
+
+### Installation - Client
+
+1. Navigate to the server directory:
+
+ ```bash
+ cd client
+ ```
+
+2. Install dependencies using Yarn or npm:
+
+ ```bash
+ # Using Yarn
+ yarn
+
+ # Using npm
+ npm install
+ ```
+
+3. Start the Client:
+
+ ```bash
+ # Using Yarn
+ yarn start
+
+ # Using npm
+ npm start
+ ```
diff --git a/packages/examples/omegle-push-video/client/.env.sample b/packages/examples/omegle-push-video/client/.env.sample
new file mode 100644
index 000000000..27a28c2c4
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/.env.sample
@@ -0,0 +1 @@
+REACT_APP_SERVER_URL=""
\ No newline at end of file
diff --git a/packages/examples/omegle-push-video/client/.gitignore b/packages/examples/omegle-push-video/client/.gitignore
new file mode 100644
index 000000000..a23084d29
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/.gitignore
@@ -0,0 +1,25 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+package-lock.json
+yarn.lock
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.env
diff --git a/packages/examples/omegle-push-video/client/config-overrides.js b/packages/examples/omegle-push-video/client/config-overrides.js
new file mode 100644
index 000000000..271ee065b
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/config-overrides.js
@@ -0,0 +1,32 @@
+const webpack = require("webpack");
+
+module.exports = function override(config) {
+ const fallback = config.resolve.fallback || {};
+ Object.assign(fallback, {
+ crypto: require.resolve("crypto-browserify"),
+ stream: require.resolve("stream-browserify"),
+ assert: require.resolve("assert"),
+ http: require.resolve("stream-http"),
+ https: require.resolve("https-browserify"),
+ os: require.resolve("os-browserify"),
+ url: require.resolve("url"),
+ zlib: require.resolve("browserify-zlib"),
+ });
+ config.resolve.fallback = fallback;
+ config.plugins = (config.plugins || []).concat([
+ new webpack.ProvidePlugin({
+ process: "process/browser",
+ Buffer: ["buffer", "Buffer"],
+ }),
+ ]);
+ config.ignoreWarnings = [/Failed to parse source map/];
+ config.module.rules.push({
+ test: /\.(js|mjs|jsx)$/,
+ enforce: "pre",
+ loader: require.resolve("source-map-loader"),
+ resolve: {
+ fullySpecified: false,
+ },
+ });
+ return config;
+};
diff --git a/packages/examples/omegle-push-video/client/package.json b/packages/examples/omegle-push-video/client/package.json
new file mode 100644
index 000000000..1a7c8e167
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/package.json
@@ -0,0 +1,64 @@
+{
+ "name": "client",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@pushprotocol/restapi": "1.6.2",
+ "@pushprotocol/socket": "^0.5.3",
+ "@pushprotocol/uiweb": "1.1.22",
+ "@rainbow-me/rainbowkit": "^1.3.1",
+ "@testing-library/jest-dom": "^5.14.1",
+ "@testing-library/react": "^12.0.0",
+ "@testing-library/user-event": "^13.2.1",
+ "@vercel/analytics": "^1.1.1",
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2",
+ "react-icons": "^5.0.1",
+ "react-router-dom": "^6.21.2",
+ "react-scripts": "5.0.0",
+ "socket.io-client": "^4.4.1",
+ "styled-components": "^6.1.1",
+ "viem": "^1.20.1",
+ "wagmi": "^1.4.12",
+ "web-vitals": "^2.1.0"
+ },
+ "devDependencies": {
+ "assert": "^2.0.0",
+ "browserify-zlib": "^0.2.0",
+ "buffer": "^6.0.3",
+ "crypto-browserify": "^3.12.0",
+ "daisyui": "^4.6.0",
+ "https-browserify": "^1.0.0",
+ "os-browserify": "^0.3.0",
+ "process": "^0.11.10",
+ "react-app-rewired": "^2.2.1",
+ "stream-browserify": "^3.0.0",
+ "stream-http": "^3.2.0",
+ "tailwindcss": "^3.4.1",
+ "url": "^0.11.0"
+ },
+ "scripts": {
+ "start": "react-app-rewired start",
+ "build": "react-app-rewired build",
+ "test": "react-app-rewired test",
+ "eject": "react-scripts eject"
+ },
+ "eslintConfig": {
+ "extends": [
+ "react-app",
+ "react-app/jest"
+ ]
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
diff --git a/packages/examples/omegle-push-video/client/public/index.html b/packages/examples/omegle-push-video/client/public/index.html
new file mode 100644
index 000000000..2671e5520
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/public/index.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+ Bored Anons
+
+
+
+
+
+
+
diff --git a/packages/examples/omegle-push-video/client/public/manifest.json b/packages/examples/omegle-push-video/client/public/manifest.json
new file mode 100644
index 000000000..0b253ae2c
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/public/manifest.json
@@ -0,0 +1,9 @@
+{
+ "short_name": "Bored Anons",
+ "name": "Bored Anons",
+
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
diff --git a/packages/examples/omegle-push-video/client/public/robots.txt b/packages/examples/omegle-push-video/client/public/robots.txt
new file mode 100644
index 000000000..e9e57dc4d
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/packages/examples/omegle-push-video/client/src/App.jsx b/packages/examples/omegle-push-video/client/src/App.jsx
new file mode 100644
index 000000000..f7e16b5fe
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/src/App.jsx
@@ -0,0 +1,266 @@
+import React, {useEffect, useRef, useReducer} from "react";
+import io from "socket.io-client";
+import {ConnectButton} from "@rainbow-me/rainbowkit";
+import {useAccount, useWalletClient} from "wagmi";
+import {CONSTANTS, PushAPI} from "@pushprotocol/restapi";
+
+import {appReducer, actionTypes} from "./reducer";
+import Modal from "./components/Modal";
+import Video from "./video";
+import Loader from "./components/Loader";
+
+function App() {
+ const socket = useRef(null);
+ const userAlice = useRef();
+ const {data: signer} = useWalletClient();
+ const {address: walletAddress, isConnected: walletConnected} = useAccount();
+ const [
+ {
+ isPeerConnected,
+ peerWalletAddress,
+ showPeerDisconnectedModal,
+ showNoActivePeersModal,
+ peerMatched,
+ videoCallInitiator,
+ userActive,
+ incomingPeerRequest,
+ },
+ dispatch,
+ ] = useReducer(appReducer, {
+ isPeerConnected: false,
+ peerWalletAddress: "",
+ showPeerDisconnectedModal: false,
+ showNoActivePeersModal: false,
+ peerMatched: false,
+ videoCallInitiator: "",
+ userActive: true,
+ incomingPeerRequest: false,
+ });
+
+ const connectToPeer = () => {
+ socket.current.emit("connect_to_peer", walletAddress);
+ };
+
+ const setupSocketListeners = () => {
+ socket.current.on("peer_matched", (peerAddress) => {
+ dispatch({type: actionTypes.SET_PEER_MATCHED, payload: true});
+ checkIfChatExists(peerAddress);
+ dispatch({
+ type: actionTypes.SET_VIDEO_CALL_INITIATOR,
+ payload: walletAddress,
+ });
+ });
+ socket.current.on("incoming_peer_request", () => {
+ dispatch({type: actionTypes.SET_INCOMING_PEER_REQUEST, payload: true});
+ });
+ socket.current.on("intent_accepted_by_peer", async (peerAddress) => {
+ dispatch({type: actionTypes.SET_IS_PEER_CONNECTED, payload: true});
+ });
+ socket.current.on("chat_exists_bw_users", async (peerAddress) => {
+ dispatch({type: actionTypes.SET_IS_PEER_CONNECTED, payload: true});
+ });
+ socket.current.on("no_active_peers_found", () => {
+ dispatch({
+ type: actionTypes.SET_SHOW_NO_ACTIVE_PEERS_MODAL,
+ payload: true,
+ });
+ });
+ socket.current.on("peer_disconnected", () => {
+ dispatch({type: actionTypes.SET_IS_PEER_CONNECTED, payload: false});
+ dispatch({type: actionTypes.SET_INCOMING_PEER_REQUEST, payload: false});
+ dispatch({type: actionTypes.SET_PEER_WALLET_ADDRESS, payload: ""});
+ dispatch({type: actionTypes.SET_PEER_MATCHED, payload: false});
+ dispatch({
+ type: actionTypes.SET_SHOW_PEER_DISCONNECTED_MODAL,
+ payload: true,
+ });
+ });
+
+ socket.current.on("chat_message_request", async (peerAddress) => {
+ const aliceChatRequsts = await userAlice.current.chat.list("REQUESTS");
+ for (const chat of aliceChatRequsts) {
+ if (
+ chat.msg.fromDID &&
+ chat.msg.fromDID.substring(7).toLowerCase() ===
+ peerAddress.toLowerCase()
+ ) {
+ await userAlice.current.chat.accept(peerAddress);
+ break;
+ }
+ }
+
+ socket.current.emit("intent_accepted", peerAddress);
+ });
+
+ socket.current.on("peer_disconnected_call", async (peerAddress) => {
+ dispatch({type: actionTypes.SET_IS_PEER_CONNECTED, payload: false});
+ dispatch({type: actionTypes.SET_INCOMING_PEER_REQUEST, payload: false});
+ dispatch({type: actionTypes.SET_PEER_MATCHED, payload: false});
+ window.location.reload();
+ });
+ };
+
+ const checkIfChatExists = async (peerAddress) => {
+ if (!userAlice.current) {
+ window.location.reload();
+ return;
+ }
+ const aliceChats = await userAlice.current.chat.list("CHATS");
+
+ let chatExists = false;
+ for (const chat of aliceChats) {
+ if (
+ chat.msg.fromDID &&
+ chat.msg.fromDID.substring(7).toLowerCase() ===
+ peerAddress.toLowerCase()
+ ) {
+ chatExists = true;
+
+ socket.current.emit("chat_exists_w_peer", peerAddress);
+ break;
+ }
+ }
+ if (!chatExists) {
+ const aliceChatRequsts = await userAlice.current.chat.list("REQUESTS");
+ for (const chat of aliceChatRequsts) {
+ if (chat.did.substring(7).toLowerCase() === peerAddress.toLowerCase()) {
+ chatExists = true;
+ await userAlice.current.chat.accept(peerAddress);
+ socket.current.emit("intent_accepted", peerAddress);
+ break;
+ }
+ }
+ }
+ if (!chatExists) {
+ await userAlice.current.chat.send(peerAddress, {
+ type: "Text",
+ content: "Hi Peer, setting up a call! from bored-anons.xyz",
+ });
+ socket.current.emit("chat_message_sent", peerAddress);
+ }
+ dispatch({type: actionTypes.SET_PEER_WALLET_ADDRESS, payload: peerAddress});
+ };
+ useEffect(() => {
+ socket.current = io.connect(process.env.REACT_APP_SERVER_URL);
+ }, []);
+
+ useEffect(() => {
+ if (!signer) return;
+ if (isPeerConnected) return;
+ const initializeUserAlice = async () => {
+ userAlice.current = await PushAPI.initialize(signer, {
+ env: CONSTANTS.ENV.PROD,
+ });
+ };
+ initializeUserAlice();
+ }, [signer, isPeerConnected]);
+
+ useEffect(() => {
+ setupSocketListeners();
+
+ if (walletConnected && walletAddress) {
+ socket.current.emit("connect_wallet", walletAddress);
+ }
+ if (!walletAddress) {
+ socket.current.emit("wallet_disconnected");
+ }
+ }, [walletAddress, walletConnected]);
+
+ return (
+
+
+ {!isPeerConnected ? (
+
+ {peerMatched || incomingPeerRequest ? (
+
+ ) : (
+
+
+
Hello Anon!
+
+ ik you're bored, fret not anon, time to make some random
+ video calls with strangersssss.
+
+
+
+ {walletConnected && !isPeerConnected && (
+
+ )}
+
+
+
{
+ dispatch({
+ type: actionTypes.SET_USER_ACTIVE,
+ payload: !userActive,
+ });
+ socket.current.emit("user_status_toggle", !userActive);
+ }}
+ />
+ {userActive ? (
+
Active, Connects automatically to an incoming call
+ ) : (
+
Inactive, Does not connect to an incoming call
+ )}
+
+
+
+ )}
+
+ ) : (
+
+
+ {/* Render the modal when the peer is disconnected */}
+ {showPeerDisconnectedModal && (
+
{
+ dispatch({
+ type: actionTypes.SET_SHOW_PEER_DISCONNECTED_MODAL,
+ payload: false,
+ });
+ }}
+ />
+ )}
+ {/* Render the modal when no active peers are found */}
+ {showNoActivePeersModal && (
+ {
+ dispatch({
+ type: actionTypes.SET_SHOW_NO_ACTIVE_PEERS_MODAL,
+ payload: false,
+ });
+ }}
+ />
+ )}
+
+ );
+}
+
+export default App;
diff --git a/packages/examples/omegle-push-video/client/src/components/Loader.jsx b/packages/examples/omegle-push-video/client/src/components/Loader.jsx
new file mode 100644
index 000000000..4fb6f980f
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/src/components/Loader.jsx
@@ -0,0 +1,28 @@
+import React, {useState, useEffect} from "react";
+
+const Loader = ({text, text2}) => {
+ const [currText, setCurrText] = useState(text);
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setCurrText((prev) => (prev === text ? text2 : text));
+ }, 3000);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ return (
+
+ {" "}
+
{currText}...
{" "}
+
+
+
+
+
+
+
+ );
+};
+
+export default Loader;
diff --git a/packages/examples/omegle-push-video/client/src/components/Modal.jsx b/packages/examples/omegle-push-video/client/src/components/Modal.jsx
new file mode 100644
index 000000000..b189d21ca
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/src/components/Modal.jsx
@@ -0,0 +1,21 @@
+import React from "react";
+
+const Modal = ({onClose, text}) => {
+ return (
+
+ );
+};
+
+export default Modal;
diff --git a/packages/examples/omegle-push-video/client/src/components/VideoFrame.jsx b/packages/examples/omegle-push-video/client/src/components/VideoFrame.jsx
new file mode 100644
index 000000000..be7978dd5
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/src/components/VideoFrame.jsx
@@ -0,0 +1,62 @@
+import VideoPlayer from "./VideoPlayer";
+
+import {
+ IoMicOffOutline,
+ IoMicSharp,
+ IoVideocamOffSharp,
+ IoVideocamOutline,
+} from "react-icons/io5";
+import {ImPhoneHangUp} from "react-icons/im";
+
+export default function VideoFrame({
+ data,
+ onToggleMic,
+ onToggleCam,
+ onEndCall,
+}) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/examples/omegle-push-video/client/src/components/VideoPlayer.jsx b/packages/examples/omegle-push-video/client/src/components/VideoPlayer.jsx
new file mode 100644
index 000000000..6c1150a6c
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/src/components/VideoPlayer.jsx
@@ -0,0 +1,30 @@
+import {useEffect, useRef} from "react";
+import {truncateAddress} from "../utils";
+
+export default function VideoPlayer({stream, isMuted, whoIs}) {
+ const videoRef = useRef(null);
+
+ useEffect(() => {
+ if (videoRef.current) {
+ videoRef.current.srcObject = stream;
+ videoRef.current.play().catch((error) => {
+ console.error("Error playing video:", error);
+ });
+ }
+ }, [videoRef, stream]);
+
+ return (
+
+
+ {whoIs === "You" ? whoIs : truncateAddress(whoIs)}
+
+
+
+
+ );
+}
diff --git a/packages/examples/omegle-push-video/client/src/index.css b/packages/examples/omegle-push-video/client/src/index.css
new file mode 100644
index 000000000..b5c61c956
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/src/index.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/packages/examples/omegle-push-video/client/src/index.jsx b/packages/examples/omegle-push-video/client/src/index.jsx
new file mode 100644
index 000000000..cf708e31f
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/src/index.jsx
@@ -0,0 +1,43 @@
+import React from "react";
+import ReactDOM from "react-dom";
+import {createBrowserRouter, RouterProvider} from "react-router-dom";
+
+import "./index.css";
+import "@rainbow-me/rainbowkit/styles.css";
+
+import {
+ darkTheme,
+ getDefaultWallets,
+ RainbowKitProvider,
+} from "@rainbow-me/rainbowkit";
+import {configureChains, createConfig, WagmiConfig} from "wagmi";
+import {mainnet} from "wagmi/chains";
+import {publicProvider} from "wagmi/providers/public";
+
+import App from "./App";
+import {inject} from "@vercel/analytics";
+const {chains, publicClient} = configureChains([mainnet], [publicProvider()]);
+const {connectors} = getDefaultWallets({
+ appName: "Bored Anons",
+ projectId: "c79671f77e15d3c16d8df828931df7a7",
+ chains,
+});
+const wagmiConfig = createConfig({
+ autoConnect: true,
+ connectors,
+ publicClient,
+});
+
+const router = createBrowserRouter([{path: "/", element: }]);
+
+ReactDOM.render(
+
+
+
+
+
+
+ ,
+ document.getElementById("root")
+);
+inject();
diff --git a/packages/examples/omegle-push-video/client/src/reducer.js b/packages/examples/omegle-push-video/client/src/reducer.js
new file mode 100644
index 000000000..b8fea761a
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/src/reducer.js
@@ -0,0 +1,37 @@
+// Action types
+const actionTypes = {
+ SET_IS_PEER_CONNECTED: "SET_IS_PEER_CONNECTED",
+ SET_PEER_WALLET_ADDRESS: "SET_PEER_WALLET_ADDRESS",
+ SET_SHOW_PEER_DISCONNECTED_MODAL: "SET_SHOW_PEER_DISCONNECTED_MODAL",
+ SET_SHOW_NO_ACTIVE_PEERS_MODAL: "SET_SHOW_NO_ACTIVE_PEERS_MODAL",
+ SET_PEER_MATCHED: "SET_PEER_MATCHED",
+ SET_VIDEO_CALL_INITIATOR: "SET_VIDEO_CALL_INITIATOR",
+ SET_USER_ACTIVE: "SET_USER_ACTIVE",
+ SET_INCOMING_PEER_REQUEST: "SET_INCOMING_PEER_REQUEST",
+};
+
+// Reducer function
+const appReducer = (state, action) => {
+ switch (action.type) {
+ case actionTypes.SET_IS_PEER_CONNECTED:
+ return {...state, isPeerConnected: action.payload};
+ case actionTypes.SET_PEER_WALLET_ADDRESS:
+ return {...state, peerWalletAddress: action.payload};
+ case actionTypes.SET_SHOW_PEER_DISCONNECTED_MODAL:
+ return {...state, showPeerDisconnectedModal: action.payload};
+ case actionTypes.SET_SHOW_NO_ACTIVE_PEERS_MODAL:
+ return {...state, showNoActivePeersModal: action.payload};
+ case actionTypes.SET_PEER_MATCHED:
+ return {...state, peerMatched: action.payload};
+ case actionTypes.SET_VIDEO_CALL_INITIATOR:
+ return {...state, videoCallInitiator: action.payload};
+ case actionTypes.SET_USER_ACTIVE:
+ return {...state, userActive: action.payload};
+ case actionTypes.SET_INCOMING_PEER_REQUEST:
+ return {...state, incomingPeerRequest: action.payload};
+ default:
+ return state;
+ }
+};
+
+export {actionTypes, appReducer};
diff --git a/packages/examples/omegle-push-video/client/src/utils.js b/packages/examples/omegle-push-video/client/src/utils.js
new file mode 100644
index 000000000..7bd58b077
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/src/utils.js
@@ -0,0 +1,7 @@
+export function truncateAddress(input) {
+ if (input.length <= 5) {
+ return input;
+ } else {
+ return input.substring(0, 5) + "...." + input.substring(input.length - 5);
+ }
+}
diff --git a/packages/examples/omegle-push-video/client/src/video.jsx b/packages/examples/omegle-push-video/client/src/video.jsx
new file mode 100644
index 000000000..01104594e
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/src/video.jsx
@@ -0,0 +1,98 @@
+import {CONSTANTS, VideoCallStatus} from "@pushprotocol/restapi";
+import {useAccount, useWalletClient} from "wagmi";
+
+import {useEffect, useRef, useState} from "react";
+import {initVideoCallData} from "@pushprotocol/restapi/src/lib/video";
+
+import VideoFrame from "./components/VideoFrame";
+import Loader from "./components/Loader";
+
+const Video = ({peerAddress, userAlice, initiator, onEndCall}) => {
+ const {data: signer} = useWalletClient();
+ const {address: walletAddress} = useAccount();
+ const aliceVideoCall = useRef();
+ const [isPushStreamConnected, setIsPushStreamConnected] = useState(false);
+ const [data, setData] = useState(initVideoCallData);
+ const createdStream = useRef();
+ const initializePushAPI = async () => {
+ createdStream.current = await userAlice.initStream([
+ CONSTANTS.STREAM.VIDEO,
+ CONSTANTS.STREAM.CONNECT,
+ CONSTANTS.STREAM.DISCONNECT,
+ ]);
+
+ createdStream.current.on(CONSTANTS.STREAM.CONNECT, () => {
+ setIsPushStreamConnected(true);
+ });
+
+ createdStream.current.on(CONSTANTS.STREAM.DISCONNECT, () => {
+ setIsPushStreamConnected(false);
+ });
+
+ createdStream.current.on(CONSTANTS.STREAM.VIDEO, async (data) => {
+ if (data.event === CONSTANTS.VIDEO.EVENT.REQUEST) {
+ await aliceVideoCall.current.approve(data?.peerInfo);
+ }
+ if (data.event === CONSTANTS.VIDEO.EVENT.DISCONNECT) {
+ createdStream.current.disconnect();
+ }
+ });
+
+ aliceVideoCall.current = await userAlice.video.initialize(setData, {
+ stream: createdStream.current,
+ config: {
+ video: true,
+ audio: true,
+ },
+ });
+
+ await createdStream.current.connect();
+
+ if (initiator.toLowerCase() === walletAddress.toLowerCase()) {
+ await aliceVideoCall.current.request([peerAddress]);
+ }
+ };
+
+ // Here we initialize the push video API, which is the first and important step to make video calls
+ useEffect(() => {
+ if (!signer) return;
+ initializePushAPI();
+ }, []);
+
+ useEffect(() => {
+ console.log("isPushStreamConnected", isPushStreamConnected);
+ }, [isPushStreamConnected]);
+
+ return (
+
+
+ {data?.incoming[0]?.status === VideoCallStatus.CONNECTED &&
+ data?.incoming[0].stream ? (
+
{
+ aliceVideoCall.current?.media({audio: !data?.local.audio});
+ }}
+ onToggleCam={() => {
+ aliceVideoCall.current?.media({video: !data?.local.video});
+ }}
+ onEndCall={async () => {
+ await aliceVideoCall.current.disconnect(
+ data?.incoming[0]?.address
+ );
+ createdStream.current.disconnect();
+ onEndCall();
+ window.location.reload();
+ }}
+ />
+ ) : (
+
+
+
+ )}
+
+
+ );
+};
+
+export default Video;
diff --git a/packages/examples/omegle-push-video/client/tailwind.config.js b/packages/examples/omegle-push-video/client/tailwind.config.js
new file mode 100644
index 000000000..6b3cac8ce
--- /dev/null
+++ b/packages/examples/omegle-push-video/client/tailwind.config.js
@@ -0,0 +1,11 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: ["./src/**/*.{js,jsx,ts,tsx}"],
+ theme: {
+ extend: {},
+ },
+ daisyui: {
+ themes: ["luxury"],
+ },
+ plugins: [require("daisyui")],
+};
diff --git a/packages/examples/omegle-push-video/server/.gitignore b/packages/examples/omegle-push-video/server/.gitignore
new file mode 100644
index 000000000..23bd09fa5
--- /dev/null
+++ b/packages/examples/omegle-push-video/server/.gitignore
@@ -0,0 +1,3 @@
+/node_modules
+package-lock.json
+yarn.lock
diff --git a/packages/examples/omegle-push-video/server/index.js b/packages/examples/omegle-push-video/server/index.js
new file mode 100644
index 000000000..47f1e1fff
--- /dev/null
+++ b/packages/examples/omegle-push-video/server/index.js
@@ -0,0 +1,198 @@
+const express = require("express");
+const http = require("http");
+const socketIO = require("socket.io");
+const cors = require("cors");
+const app = express();
+const server = http.createServer(app);
+const io = socketIO(server, {
+ cors: {
+ origin: "*",
+ methods: ["GET", "POST"],
+ allowedHeaders: ["*"],
+ credentials: true,
+ },
+});
+
+let users = [];
+
+io.on("connection", (socket) => {
+ console.log(`User connected with socket id: ${socket.id}`);
+ socket.on("connect_wallet", (walletAddress) => {
+ const userCaller = users.findIndex(
+ (user) => user.id === socket.id || user.walletAddress === walletAddress
+ );
+ if (userCaller !== -1) {
+ users[userCaller].walletAddress = walletAddress;
+ } else {
+ users.push({
+ walletAddress,
+ id: socket.id,
+ online: true,
+ busy: false,
+ lookingForPeers: true,
+ connectedPeerId: null,
+ });
+ }
+ });
+ socket.on("wallet_disconnected", () => {
+ const userIndex = users.findIndex((user) => user.id === socket.id);
+ if (userIndex !== -1) {
+ users.splice(userIndex, 1);
+ }
+ });
+ socket.on("user_status_toggle", (newStatus) => {
+ const userIndex = users.findIndex((user) => user.id === socket.id);
+ if (userIndex !== -1) users[userIndex].lookingForPeers = newStatus;
+ });
+ socket.on("connect_to_peer", (walletAddress) => {
+ const caller = users.find((user) => user.id === socket.id);
+ if (!caller || !caller.walletAddress) {
+ return;
+ }
+ if (caller && caller.busy) {
+ return;
+ }
+
+ const availableUsers = users.filter(
+ (user) =>
+ user.walletAddress !== walletAddress &&
+ user.id !== caller.id &&
+ user.walletAddress !== null &&
+ user.online &&
+ user.lookingForPeers &&
+ !user.busy
+ );
+
+ if (availableUsers.length > 0) {
+ const chosenItem =
+ availableUsers[Math.floor(Math.random() * availableUsers.length)];
+ const userIndexCaller = users.findIndex((user) => user.id === socket.id);
+ const userIndexPeer = users.findIndex(
+ (user) => user.walletAddress === chosenItem.walletAddress
+ );
+
+ if (userIndexCaller !== -1 && userIndexPeer !== -1) {
+ io.to(socket.id).emit("peer_matched", chosenItem.walletAddress);
+ io.to(chosenItem.id).emit("incoming_peer_request");
+ users[userIndexCaller].busy = true;
+ users[userIndexPeer].busy = true;
+ users[userIndexCaller].lookingForPeers = false;
+ users[userIndexPeer].lookingForPeers = false;
+ users[userIndexCaller].connectedPeerId = chosenItem.id;
+ users[userIndexPeer].connectedPeerId = caller.id;
+ } else {
+ io.to(socket.id).emit("no_active_peers_found", walletAddress);
+ }
+ } else {
+ io.to(socket.id).emit("no_active_peers_found", walletAddress);
+ console.log("No valid user found.");
+ }
+ });
+ socket.on("disconnect", () => {
+ console.log(`User disconnected with socket id: ${socket.id}`);
+
+ const id = socket.id;
+ if (id) {
+ const userIndex = users.findIndex((user) => user.id === id);
+
+ // Check if the user is found
+ if (userIndex !== -1) {
+ const disconnectedUser = users[userIndex];
+ users.splice(userIndex, 1);
+
+ console.log("Updated Users Array:", users);
+
+ // Emit "peer_disconnected" only to the connected peer
+ const connectedPeerSocket = io.sockets.sockets.get(
+ disconnectedUser.connectedPeerId
+ );
+ if (connectedPeerSocket) {
+ connectedPeerSocket.emit("peer_disconnected");
+ }
+ // Update the connected peer's properties
+ const connectedPeerIndex = users.findIndex(
+ (user) => user.id === disconnectedUser.connectedPeerId
+ );
+ if (connectedPeerIndex !== -1) {
+ users[connectedPeerIndex].busy = false;
+ users[connectedPeerIndex].lookingForPeers = true;
+ }
+ }
+ }
+ });
+
+ socket.on("chat_message_sent", (peerAddress) => {
+ const peerSocket = users.find((user) => user.walletAddress === peerAddress);
+ const currUserSocket = users.find((user) => user.id === socket.id);
+ if (peerSocket) {
+ io.to(peerSocket.id).emit(
+ "chat_message_request",
+ currUserSocket.walletAddress
+ );
+ }
+ });
+ socket.on("intent_accepted", (peerAddress) => {
+ const peerSocket = users.find((user) => user.walletAddress === peerAddress);
+ const currUserSocket = users.find((user) => user.id === socket.id);
+ if (peerSocket) {
+ io.to(peerSocket.id).emit(
+ "intent_accepted_by_peer",
+ currUserSocket.walletAddress
+ );
+ io.to(socket.id).emit("intent_accepted_by_peer", peerAddress);
+ }
+ });
+ socket.on("chat_exists_w_peer", (peerAddress) => {
+ const peerSocket = users.find((user) => user.walletAddress === peerAddress);
+ const currUserSocket = users.find((user) => user.id === socket.id);
+ if (peerSocket) {
+ io.to(peerSocket.id).emit(
+ "chat_exists_bw_users",
+ currUserSocket.walletAddress
+ );
+ io.to(socket.id).emit("chat_exists_bw_users", peerAddress);
+ }
+ });
+
+ socket.on("endPeerConnection", () => {
+ const currUserSocket = users.find((user) => user.id === socket.id);
+
+ if (currUserSocket.walletAddress) {
+ const userIndex = users.findIndex(
+ (user) => user.walletAddress === currUserSocket.walletAddress
+ );
+ const userIndexPeer = users.findIndex(
+ (user) => user.id === users[userIndex].connectedPeerId
+ );
+ // Instead of removing the user entry, update its properties
+ if (userIndex !== -1) {
+ users[userIndex].busy = false;
+ users[userIndex].lookingForPeers = true;
+ }
+ if (userIndexPeer !== -1) {
+ users[userIndexPeer].busy = false;
+ users[userIndexPeer].lookingForPeers = true;
+ users[userIndexPeer].connectedPeerId = null;
+ }
+ io.to(userIndexPeer.id).emit("peer_disconnected_call");
+ }
+ });
+});
+// Function to log users every 10 seconds
+function logUsers() {
+ console.log("-------------------Connected Users:--------------------------");
+ users.forEach((user) => {
+ console.log(
+ `Wallet Address: ${user.walletAddress}, Online: ${user.online}, Busy: ${user.busy}, LookingForPeer: ${user.lookingForPeers} socket: ${user.id} Connected Peer: ${user.connectedPeerId}`
+ );
+ });
+ console.log("-------------------------------------------------------------");
+}
+
+// Schedule the logUsers function to run every 10 seconds
+setInterval(logUsers, 10000); // 10000 milliseconds = 10 seconds
+
+const PORT = process.env.PORT || 3001;
+server.listen(PORT, () => {
+ console.log(`Server is running on port ${PORT}`);
+});
diff --git a/packages/examples/omegle-push-video/server/package.json b/packages/examples/omegle-push-video/server/package.json
new file mode 100644
index 000000000..70b1c6b25
--- /dev/null
+++ b/packages/examples/omegle-push-video/server/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "server",
+ "version": "1.0.0",
+ "main": "index.js",
+ "license": "MIT",
+ "scripts": {
+ "start": "nodemon index.js"
+ },
+ "dependencies": {
+ "cors": "^2.8.5",
+ "express": "^4.17.3",
+ "nodemon": "^2.0.15",
+ "socket.io": "^4.4.1"
+ }
+}