diff --git a/src/client/whatsapp.ts b/src/client/whatsapp.ts index 6661c35..3155677 100644 --- a/src/client/whatsapp.ts +++ b/src/client/whatsapp.ts @@ -89,13 +89,17 @@ export class WhatsAppClient { }); } - private request(method: string, params?: unknown): Promise { + private request( + method: string, + params?: unknown, + devId?: string | null, + ): Promise { if (!this.isConnected || !this.socket) { throw new Error("WhatsApp is not connected"); } const id = crypto.randomUUID(); - const deviceId = this.deviceId; + const deviceId = devId || this.deviceId; return new Promise((resolve, reject) => { const timer = setTimeout(() => { @@ -175,7 +179,7 @@ export class WhatsAppClient { return this.request("getMedia", msgId); } - exec(cmd: string) { - return this.request("exec", cmd); + exec(cmd: string, deviceId?: string | null) { + return this.request("exec", cmd, deviceId); } } diff --git a/ui/src/app/devices.tsx b/ui/src/app/devices.tsx index 568ba04..3fe5db5 100644 --- a/ui/src/app/devices.tsx +++ b/ui/src/app/devices.tsx @@ -1,6 +1,6 @@ -import { useEffect, useMemo } from "react"; +import { useEffect, useMemo, useRef } from "react"; import { useWAClient } from "./context"; -import { useQuery } from "@tanstack/react-query"; +import { useMutation, useQuery } from "@tanstack/react-query"; import { Select, SelectContent, @@ -8,12 +8,27 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { + Dialog, + DialogClose, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { createDisclosure } from "@/lib/utils"; +import { FileTerminalIcon } from "lucide-react"; +import { Textarea } from "@/components/ui/textarea"; +import { Badge } from "@/components/ui/badge"; type Props = { curDeviceId: string; setDeviceId: (id: string) => void; }; +const commandDlg = createDisclosure<{ deviceId: string }>(); + export default function DevicesSection({ curDeviceId, setDeviceId }: Props) { const { client, isReady } = useWAClient(); const { data: devices } = useQuery({ @@ -35,7 +50,7 @@ export default function DevicesSection({ curDeviceId, setDeviceId }: Props) { const isOnline = Date.now() - lastAlive <= 60_000; return { - label: i.name || "Unknown", + label: i.name || i.id || "Unknown", value: i.id || String(idx), lastAlive, isOnline, @@ -48,7 +63,7 @@ export default function DevicesSection({ curDeviceId, setDeviceId }: Props) { }, [deviceList, curDeviceId]); return ( -
+
+ + + +
); } + +const cmdPreset = [ + { title: "Status", command: "status" }, + { title: "Has Focus", command: "return document.hasFocus();" }, + { title: "Reload", command: "window.location.reload(); return true;" }, +]; + +function CommandModal() { + const { data, isOpen } = commandDlg.useStore(); + const { client } = useWAClient(); + const cmdTextRef = useRef(null); + + const cmd = useMutation({ + async mutationFn(cmd: string) { + if (cmd === "status") { + return client.status(); + } + return client.exec(cmd, data?.deviceId); + }, + }); + + const onSubmit = () => { + const command = cmdTextRef.current?.value || ""; + cmd.mutate(command); + }; + + return ( + + + + Execute Command + + +
+