feat: add device select & cmd exec
This commit is contained in:
+105
-4
@@ -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 (
|
||||
<div className="border-t p-4">
|
||||
<div className="border-t p-4 flex items-center gap-2">
|
||||
<Select
|
||||
items={deviceList}
|
||||
value={curDeviceId}
|
||||
@@ -86,6 +101,92 @@ export default function DevicesSection({ curDeviceId, setDeviceId }: Props) {
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Button
|
||||
onClick={() => commandDlg.onOpen({ deviceId: curDeviceId })}
|
||||
variant="outline"
|
||||
>
|
||||
<FileTerminalIcon />
|
||||
</Button>
|
||||
|
||||
<CommandModal />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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<HTMLTextAreaElement>(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 (
|
||||
<Dialog open={isOpen} onOpenChange={commandDlg.onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Execute Command</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div>
|
||||
<Textarea ref={cmdTextRef} placeholder="cmd..." />
|
||||
<div className="flex items-center gap-2 overflow-x-auto mt-2">
|
||||
{cmdPreset.map((i) => (
|
||||
<button
|
||||
key={i.command}
|
||||
onClick={() => (cmdTextRef.current!.value = i.command)}
|
||||
>
|
||||
<Badge className="cursor-pointer">{i.title}</Badge>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{cmd.error ? (
|
||||
<div className="mt-3">
|
||||
<p className="text-sm font-medium leading-tight">Error:</p>
|
||||
<pre className="text-sm text-destructive mt-1">
|
||||
{JSON.stringify(cmd.error, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{cmd.data !== undefined ? (
|
||||
<div className="mt-3">
|
||||
<p className="text-sm font-medium leading-tight">Result:</p>
|
||||
<pre className="text-sm mt-1">
|
||||
{JSON.stringify((cmd as any).data, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<DialogClose>
|
||||
<Button variant="ghost">Cancel</Button>
|
||||
</DialogClose>
|
||||
<Button onClick={onSubmit} disabled={cmd.isPending}>
|
||||
Execute
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user