diff --git a/desktop/src/App.tsx b/desktop/src/App.tsx index df7adcf..a86154d 100644 --- a/desktop/src/App.tsx +++ b/desktop/src/App.tsx @@ -1,53 +1,19 @@ import { invoke } from "@tauri-apps/api/tauri"; import { useLocalStorage } from "@uidotdev/usehooks"; -import { DataConnection, Peer } from "peerjs"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef } from "react"; import { v4 as uuidv4 } from 'uuid'; import successSvg from "./assets/success.svg"; -import { BASE_URL, PEERJS_OPTIONS } from "./config"; +import { BASE_URL } from "./config"; import { createQR } from "./qr"; - - -enum Action { - VOL_UP, - VOL_DN, - PG_UP, - PG_DN, - F5, - ESC -} - -interface Message { - action: Action; -} - +import { Action, usePeer } from "./usePeer"; function App() { const [id, ] = useLocalStorage('id', uuidv4()) console.log('localstorage id => ', id) - const [loading, setLoading] = useState(true); - const [peer, ] = useState(new Peer(id, PEERJS_OPTIONS)); - const [conn, setConn] = useState(null); + const {message, status} = usePeer(id, true) const qrDiv = useRef(null); - - function onMessage(message: unknown) { - const data = JSON.parse(message as string) as Message; - invoke("press", { key: Action[data.action].toString() }); - } - - useEffect(() => { - conn?.on("data", onMessage); - }, [conn]); - - function onConnect(connection: DataConnection) { - connection.on("iceStateChanged", (state) => { - if (state === "disconnected" || state == "closed") { - onDisconnect(); - } - }); - setConn(connection); - } + async function renderQR() { const url = `${BASE_URL}?id=${id}`; @@ -57,35 +23,34 @@ function App() { const newQR = createQR(url); const element = await newQR._getElement(); - setLoading(false); if (element) { qrDiv.current?.appendChild(element); } } - async function onDisconnect() { - console.log("disconnected"); - renderQR() - - // qrRef?.current?.update({data: url}) - setConn(null); - } useEffect(() => { - peer.on("open", () => { + console.log('status => ', status) + if (status === 'READY') { console.log("creating qr"); const url = `${BASE_URL}?id=${id}`; console.log("url => ", url); renderQR() - }); - peer.on("connection", onConnect); - }, []); + } + }, [status]) + + useEffect(() => { + if (message) { + invoke("press", { key: Action[message.action].toString() }); + } + }, [message]) + function copyURL() { const url = `${BASE_URL}?id=${id}`; navigator.clipboard.writeText(url); } - if (conn) { + if (status === 'CONNECTED') { return (
CONNECTED @@ -98,7 +63,7 @@ function App() {
Ready to connect
- {loading && ( + {status === 'INIT' && (
diff --git a/desktop/src/config.ts b/desktop/src/config.ts index cdd8260..74e1f23 100644 --- a/desktop/src/config.ts +++ b/desktop/src/config.ts @@ -1,5 +1,7 @@ import {PeerOptions} from 'peerjs' -export const BASE_URL = 'https://thewh1teagle.github.io/mobslide/' + + +export const BASE_URL = import.meta.env.MODE === 'development' ? 'http://localhost:5173/mobslide/' : 'https://thewh1teagle.github.io/mobslide/' export const PEERJS_OPTIONS: PeerOptions = { host: 'mobslide-signaling.fly.dev', port: 443, diff --git a/desktop/src/usePeer.ts b/desktop/src/usePeer.ts new file mode 100644 index 0000000..3e9688a --- /dev/null +++ b/desktop/src/usePeer.ts @@ -0,0 +1,153 @@ +import { useRef, useState, useEffect } from "react"; +import Peer, { DataConnection } from "peerjs"; +import { PEERJS_OPTIONS } from "./config"; + +export enum Action { + VOL_UP, + VOL_DN, + PG_UP, + PG_DN, + F5, + ESC, +} +export interface Message { + action: Action; +} + +type Status = "CONNECTING" | "CONNECTED" | "DISCONNECTED" | "INIT" | "READY"; + +export function usePeer(id?: string, listener?: boolean) { + const peerRef = useRef(); + const connRef = useRef(); + const addressRef = useRef(""); + const checkConnectionIntervalRef = useRef(null); + const connectIntervalRef = useRef(null); +const [message, setMessage] = useState() + + const [status, setStatus] = useState("INIT"); + + + function onMessage(message: unknown) { + const data = JSON.parse(message as string) as Message | null; + setMessage(data) + } + + function reconnect() { + if (checkConnectionIntervalRef.current) { + clearInterval(checkConnectionIntervalRef.current); + } + if (connectIntervalRef.current) { + clearInterval(connectIntervalRef.current); + } + // reconnect + peerRef.current?.destroy(); + connRef.current?.close(); + connRef.current = undefined; + peerRef.current = undefined; + setStatus("CONNECTING"); + if (checkConnectionIntervalRef.current) { + clearInterval(checkConnectionIntervalRef.current); + } + connectIntervalRef.current = setInterval(() => connect(), 5000); + } + + function checkConnection() { + if (!connRef.current?.peerConnection) { + // reconnect + reconnect(); + } + } + + async function createConnection() { + setStatus("CONNECTING"); + return new Promise((resolve, reject) => { + console.log("connecting to ", addressRef.current); + if (id) { + peerRef.current = new Peer(id, PEERJS_OPTIONS); + } else { + peerRef.current = new Peer(PEERJS_OPTIONS); + } + + peerRef.current.on("open", () => { + setStatus('READY') + if (addressRef.current) { + peerRef?.current?.on("error", (error) => reject(error)); + connRef.current = peerRef.current?.connect(addressRef.current); + connRef.current?.on("open", () => resolve()); + } + }); + + }); + } + + function listen() { + if (id) { + peerRef.current = new Peer(id, PEERJS_OPTIONS); + } else { + peerRef.current = new Peer(PEERJS_OPTIONS); + } + + peerRef.current?.on("open", () => { + setStatus('READY') + }); + peerRef.current.on('connection', conn => { + setStatus('CONNECTED') + connRef.current = conn + connRef.current?.on('data', onMessage) + connRef.current.on('iceStateChanged', (state) => { + if (state === 'disconnected' || state === 'closed') { + peerRef.current?.destroy(); + connRef.current?.close(); + connRef.current = undefined; + peerRef.current = undefined; + listen() + setStatus('DISCONNECTED') + return + } + }) + }) + } + + if (listener) { + useEffect(() => { + listen() + }, []) + } + + async function connect() { + try { + await createConnection(); + connRef.current?.on('data', onMessage) + connRef.current?.once("iceStateChanged", (state) => { + if ( + state === "disconnected" || + state == "closed" || + state == "failed" + ) { + reconnect(); + } + }); + if (connectIntervalRef.current) { + clearInterval(connectIntervalRef.current!); + } + checkConnectionIntervalRef.current = setInterval(checkConnection, 1000); + setStatus("CONNECTED"); + } catch (e) { + console.log(e); + } + } + + function sendMessage(data: Message) { + console.log("sending => ", data); + navigator.vibrate(60); + + connRef.current?.send(JSON.stringify(data)); + } + + function connectWrapper(address: string) { + addressRef.current = address; + reconnect(); + } + return { connectWrapper, sendMessage, status, message }; +} + diff --git a/web/src/App.tsx b/web/src/App.tsx index 5757eb2..c3728ef 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,137 +1,32 @@ import NoSleep from "nosleep.js"; -import { useEffect, useRef, useState } from "react"; +import { useEffect } from "react"; import { useLongPress } from "use-long-press"; -import Peer, { DataConnection } from "peerjs"; -import { PEERJS_OPTIONS } from "./config"; +import { Action, usePeer } from "./usePeer"; const noSleep = new NoSleep(); -enum Action { - VOL_UP, - VOL_DN, - PG_UP, - PG_DN, - F5, - ESC, -} -interface Message { - action: Action; + +if (!noSleep.isEnabled) { + console.log("No sleep enabled"); + noSleep.enable(); } function App() { - const params = new URLSearchParams(window.location.search); - - const peerRef = useRef(); - const connRef = useRef(); - const addressRef = useRef(""); - const checkConnectionIntervalRef = useRef(null); - const connectIntervalRef = useRef(null); - const [loading, setLoading] = useState(true); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - - function sendMessage(data: Message) { - console.log("sending => ", data); - navigator.vibrate(60); - if (!noSleep.isEnabled) { - console.log("No sleep enabled"); - noSleep.enable(); - } - - connRef.current?.send(JSON.stringify(data)); - } - - function reconnect() { - if (checkConnectionIntervalRef.current) { - clearInterval(checkConnectionIntervalRef.current); - } - if (connectIntervalRef.current) { - clearInterval(connectIntervalRef.current); - } - // reconnect - peerRef.current?.destroy(); - connRef.current?.close(); - connRef.current = undefined; - peerRef.current = undefined; - setLoading(true); - if (checkConnectionIntervalRef.current) { - clearInterval(checkConnectionIntervalRef.current); - } - connectIntervalRef.current = setInterval(() => connect(), 5000); - } - - function checkConnection() { - if (!connRef.current?.peerConnection) { - // reconnect - reconnect(); - } - } - - async function createConnection() { - return new Promise((resolve, reject) => { - console.log("connecting to ", addressRef.current); - peerRef.current = new Peer(PEERJS_OPTIONS); - peerRef.current.on("open", () => { - if (addressRef.current) { - peerRef?.current?.on("error", (error) => reject(error)); - connRef.current = peerRef.current?.connect(addressRef.current); - connRef.current?.on("open", () => resolve()); - } - }); - }); - } - - async function connect() { - try { - await createConnection(); - - connRef.current?.once("iceStateChanged", (state) => { - if ( - state === "disconnected" || - state == "closed" || - state == "failed" - ) { - reconnect(); - } - }); - if (connectIntervalRef.current) { - clearInterval(connectIntervalRef.current!); - } - checkConnectionIntervalRef.current = setInterval(checkConnection, 1000); - setLoading(false); - } catch (e) { - console.log(e); - } - } - - useEffect(() => { - function onKeyDown(event: KeyboardEvent) { - switch (event.key) { - case "VolumeDown": { - sendMessage({ action: Action.PG_UP }); // previous - break; - } - case "VolumeUp": { - sendMessage({ action: Action.PG_DN }); // next - break; - } - } - } - window.addEventListener("keydown", onKeyDown); - return () => window.removeEventListener("keydown", onKeyDown); - }, []); + + const { connectWrapper, sendMessage, status } = usePeer(); useEffect(() => { async function init() { - addressRef.current = params.get("id"); - if (!addressRef.current) { + const params = new URLSearchParams(window.location.search); + const address = params.get("id"); + if (!address) { alert("Wrong address, please scan again."); - return; + } else { + connectWrapper(address); } - await reconnect(); } init(); - - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const nextButtonBind = useLongPress(() => { @@ -142,10 +37,10 @@ function App() { sendMessage({ action: Action.ESC }); }); - if (loading) { + if (status !== 'CONNECTED') { return (
- CONNECTING + {status}
); diff --git a/web/src/usePeer.ts b/web/src/usePeer.ts new file mode 100644 index 0000000..3e9688a --- /dev/null +++ b/web/src/usePeer.ts @@ -0,0 +1,153 @@ +import { useRef, useState, useEffect } from "react"; +import Peer, { DataConnection } from "peerjs"; +import { PEERJS_OPTIONS } from "./config"; + +export enum Action { + VOL_UP, + VOL_DN, + PG_UP, + PG_DN, + F5, + ESC, +} +export interface Message { + action: Action; +} + +type Status = "CONNECTING" | "CONNECTED" | "DISCONNECTED" | "INIT" | "READY"; + +export function usePeer(id?: string, listener?: boolean) { + const peerRef = useRef(); + const connRef = useRef(); + const addressRef = useRef(""); + const checkConnectionIntervalRef = useRef(null); + const connectIntervalRef = useRef(null); +const [message, setMessage] = useState() + + const [status, setStatus] = useState("INIT"); + + + function onMessage(message: unknown) { + const data = JSON.parse(message as string) as Message | null; + setMessage(data) + } + + function reconnect() { + if (checkConnectionIntervalRef.current) { + clearInterval(checkConnectionIntervalRef.current); + } + if (connectIntervalRef.current) { + clearInterval(connectIntervalRef.current); + } + // reconnect + peerRef.current?.destroy(); + connRef.current?.close(); + connRef.current = undefined; + peerRef.current = undefined; + setStatus("CONNECTING"); + if (checkConnectionIntervalRef.current) { + clearInterval(checkConnectionIntervalRef.current); + } + connectIntervalRef.current = setInterval(() => connect(), 5000); + } + + function checkConnection() { + if (!connRef.current?.peerConnection) { + // reconnect + reconnect(); + } + } + + async function createConnection() { + setStatus("CONNECTING"); + return new Promise((resolve, reject) => { + console.log("connecting to ", addressRef.current); + if (id) { + peerRef.current = new Peer(id, PEERJS_OPTIONS); + } else { + peerRef.current = new Peer(PEERJS_OPTIONS); + } + + peerRef.current.on("open", () => { + setStatus('READY') + if (addressRef.current) { + peerRef?.current?.on("error", (error) => reject(error)); + connRef.current = peerRef.current?.connect(addressRef.current); + connRef.current?.on("open", () => resolve()); + } + }); + + }); + } + + function listen() { + if (id) { + peerRef.current = new Peer(id, PEERJS_OPTIONS); + } else { + peerRef.current = new Peer(PEERJS_OPTIONS); + } + + peerRef.current?.on("open", () => { + setStatus('READY') + }); + peerRef.current.on('connection', conn => { + setStatus('CONNECTED') + connRef.current = conn + connRef.current?.on('data', onMessage) + connRef.current.on('iceStateChanged', (state) => { + if (state === 'disconnected' || state === 'closed') { + peerRef.current?.destroy(); + connRef.current?.close(); + connRef.current = undefined; + peerRef.current = undefined; + listen() + setStatus('DISCONNECTED') + return + } + }) + }) + } + + if (listener) { + useEffect(() => { + listen() + }, []) + } + + async function connect() { + try { + await createConnection(); + connRef.current?.on('data', onMessage) + connRef.current?.once("iceStateChanged", (state) => { + if ( + state === "disconnected" || + state == "closed" || + state == "failed" + ) { + reconnect(); + } + }); + if (connectIntervalRef.current) { + clearInterval(connectIntervalRef.current!); + } + checkConnectionIntervalRef.current = setInterval(checkConnection, 1000); + setStatus("CONNECTED"); + } catch (e) { + console.log(e); + } + } + + function sendMessage(data: Message) { + console.log("sending => ", data); + navigator.vibrate(60); + + connRef.current?.send(JSON.stringify(data)); + } + + function connectWrapper(address: string) { + addressRef.current = address; + reconnect(); + } + return { connectWrapper, sendMessage, status, message }; +} +