diff --git a/desktop/index.html b/desktop/index.html index 03abf55..512b5db 100644 --- a/desktop/index.html +++ b/desktop/index.html @@ -1,5 +1,5 @@ - + diff --git a/desktop/src-tauri/src/main.rs b/desktop/src-tauri/src/main.rs index 6e54b83..af1fdce 100644 --- a/desktop/src-tauri/src/main.rs +++ b/desktop/src-tauri/src/main.rs @@ -23,6 +23,12 @@ fn press(controller: State<'_, Controller>, key: &str) { "VOL_DN" => { controller.key_down(Key::VolumeDown); }, + "PG_UP" => { + controller.key_down(Key::PageUp); + }, + "PG_DN" => { + controller.key_down(Key::PageDown); + }, _ => {} } } diff --git a/desktop/src/App.tsx b/desktop/src/App.tsx index a5ed11c..c7be8f6 100644 --- a/desktop/src/App.tsx +++ b/desktop/src/App.tsx @@ -2,83 +2,141 @@ import { useEffect, useRef, useState } from "react"; import { invoke } from "@tauri-apps/api/tauri"; import { DataConnection, Peer } from "peerjs"; import QRCodeStyling from "qr-code-styling"; -import { createQR } from "./qr"; +import { createQR, updateQR } from "./qr"; import { BASE_URL } from "./config"; +import successSvg from "./assets/success.svg"; -const qrCode = new QRCodeStyling({ - width: 300, - height: 300, - image: - "https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg", - dotsOptions: { - color: "#4267b2", - type: "rounded" - }, - imageOptions: { - crossOrigin: "anonymous", - margin: 20 - } -}); +enum Status { + CONNECTED, + WAITING, +} enum Action { VOL_UP, - VOL_DN + VOL_DN, + PG_UP, + PG_DN, } + interface Message { - action: Action + action: Action; } function App() { - const [peer, setPeer] = useState(new Peer()); - const [id, setId] = useState('') - const [conn, setConn] = useState(null) + const [loading, setLoading] = useState(true); + const [status, setStatus] = useState(Status.WAITING); + const [peer, setPeer] = useState(new Peer({ pingInterval: 1000 })); + const [id, setId] = useState(""); + const [conn, setConn] = useState(null); const [name, setName] = useState(""); - const qrDiv = useRef() + const qrDiv = useRef(null); + const qrRef = useRef(null); + function onMessage(message: unknown) { + const data = JSON.parse(message as string) as Message; + invoke("press", { key: Action[data.action].toString() }); + } - function onMessage(message: unknown) { - const data = JSON.parse(message as string) as Message - switch (data.action) { - case Action.VOL_UP: { - invoke('press', {key: 'VOL_UP'}) - break; - } - case Action.VOL_DN: { - invoke('press', {key: 'VOL_DN'}) - break; - } - } - } - - useEffect(() => { - conn?.on('data', onMessage) - }, [conn]) - - + 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}`; + if (qrDiv.current) { + qrDiv.current.innerHTML = ""; + } + const newQR = createQR(url); - setConn(connection) + 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', id => { - setId(id) - console.log('creating qr') - const qr = createQR(`${BASE_URL}?id=${id}`) - qr.append(qrDiv.current) - }) - peer.on('connection', onConnect) - }, []) + peer.on("open", (id) => { + setId(id); + console.log("creating qr"); + const url = `${BASE_URL}?id=${id}`; + console.log("url => ", url); + renderQR() + }); + peer.on("connection", onConnect); + }, []); + + function copyURL() { + const url = `${BASE_URL}?id=${id}`; + navigator.clipboard.writeText(url); + } - + if (conn) { + return ( +
+ CONNECTED + +
+ ); + } return ( -
-
address is {id}
-
connection is from {conn?.connectionId}
+
+ Ready to connect
+ {loading && ( +
+ +
+ )} +
); } diff --git a/desktop/src/assets/success.svg b/desktop/src/assets/success.svg new file mode 100644 index 0000000..280e340 --- /dev/null +++ b/desktop/src/assets/success.svg @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/desktop/src/qr.ts b/desktop/src/qr.ts index 848d26d..e9caa8b 100644 --- a/desktop/src/qr.ts +++ b/desktop/src/qr.ts @@ -1,37 +1,49 @@ -import QRCodeStyling, {Options} from "qr-code-styling"; +import QRCodeStyling, { Options } from "qr-code-styling"; const options: Options = { - width: 300, - height: 300, - data: "", + width: 300, + height: 300, + data: "", + margin: 0, + qrOptions: { + mode: "Byte", + errorCorrectionLevel: "Q", + }, + imageOptions: { + hideBackgroundDots: true, + imageSize: 0.4, margin: 0, - qrOptions: { + }, + dotsOptions: { + type: "extra-rounded", + color: "#000000", + }, + backgroundOptions: { + color: "transparent", + }, + cornersSquareOptions: { + type: "extra-rounded", + color: "#1FB1E2", + }, + cornersDotOptions: { + type: "square", + color: "#1FB1E2", + }, +}; - mode: "Byte", - errorCorrectionLevel: "Q" - }, - imageOptions: { - hideBackgroundDots: true, - imageSize: 0.4, - margin: 0 - }, - dotsOptions: { - type: "extra-rounded", - color: "#2786ec" - }, - backgroundOptions: { - color: "#ffffff" - }, - cornersSquareOptions: { - type: "extra-rounded", - color: "#595959" - }, - cornersDotOptions: { - type: "square", - color: "#595959" - }, - } +function getOptions(url: string) { + const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches; + return { + ...options, + data: url, + dotsOptions: { type: "extra-rounded", color: isDark ? "white" : "black" }, + } as Options; +} - export function createQR(data: string) { - return new QRCodeStyling({...options, data}) - } \ No newline at end of file +export function createQR(data: string) { + return new QRCodeStyling(getOptions(data)); +} + +export function updateQR(url: string, qr: QRCodeStyling) { + qr.update(getOptions(url)); +} diff --git a/web/src/App.tsx b/web/src/App.tsx index d41e1e7..1320103 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,52 +1,131 @@ -import Peer, { DataConnection } from 'peerjs'; -import { useEffect, useState } from 'react'; - +import Peer, { DataConnection } from "peerjs"; +import { useEffect, useState } from "react"; enum Action { VOL_UP, - VOL_DN + VOL_DN, } interface Message { - action: Action + action: Action; } - // 8e25320d-7759-469e-b019-035b48593438 function App() { const params = new URLSearchParams(window.location.search); - + const [loading, setLoading] = useState(true) // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [peer,] = useState(new Peer()); - const [conn, setConn] = useState(null) + const [peer] = useState(new Peer({pingInterval: 1000})); + const [conn, setConn] = useState(null); function sendMessage(data: Message) { - conn?.send(JSON.stringify(data)) + conn?.send(JSON.stringify(data)); } - useEffect(() => { - const address = params.get('id') - console.log('connecting to ', address) - peer.on('open', () => { - const connection = peer.connect(address!) - setConn(connection) - }) - - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) + const address = params.get("id"); + console.log("connecting to ", address); + peer.on("open", () => { + const connection = peer.connect(address!); + setLoading(false) + setConn(connection); + }); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + if (loading) { + return ( +
+ CONNECTING + +
+ ) + } return ( - <> -
connected to {conn?.connectionId}
- - - - ) +
+
+
+ + +
+
+ + +
+
+
+ ); } -export default App +export default App;