Open a computer chip in the viewer instead of switching to it
A computer a bot names in a reply is now a button that opens it in the shared computer viewer modal, the bot's own computer included (sunken and checked, as before). Switching stays in the header's computers modal, so the chip has one job. A computer the bot can no longer use stays a plain name.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { renderToString } from "react-dom/server";
|
||||
import type { BUN_SQLITE_COBBLE_COMPUTERS } from "@/db/types/db";
|
||||
import type { BotComputers } from "../(hooks)/use-bot-computers";
|
||||
import { BotComputersContext } from "../(data)/bot-computers-context";
|
||||
import ComputerRefChip from "./computer-ref-chip";
|
||||
|
||||
const main: BUN_SQLITE_COBBLE_COMPUTERS = { id: 1, name: "Main computer" };
|
||||
const sandbox: BUN_SQLITE_COBBLE_COMPUTERS = { id: 2, name: "SEO box" };
|
||||
|
||||
type RenderProps = {
|
||||
computer_id: number;
|
||||
computers: BUN_SQLITE_COBBLE_COMPUTERS[];
|
||||
};
|
||||
|
||||
function render({ computer_id, computers }: RenderProps) {
|
||||
const value = makeComputers({ computers });
|
||||
|
||||
const html = renderToString(
|
||||
<BotComputersContext.Provider value={value}>
|
||||
<ComputerRefChip computer_id={computer_id} />
|
||||
</BotComputersContext.Provider>,
|
||||
);
|
||||
|
||||
return { html, value };
|
||||
}
|
||||
|
||||
type MakeComputersProps = {
|
||||
computers: BUN_SQLITE_COBBLE_COMPUTERS[];
|
||||
};
|
||||
|
||||
/**
|
||||
* A computers context recording what the chip asked for: the viewer it
|
||||
* opened and the switches it made.
|
||||
*/
|
||||
function makeComputers({ computers }: MakeComputersProps) {
|
||||
const switches: number[] = [];
|
||||
const opened: { computer_id: number; mode: string }[] = [];
|
||||
|
||||
const value: BotComputers = {
|
||||
computers,
|
||||
switchComputer: async ({ computer_id }) => {
|
||||
switches.push(computer_id);
|
||||
},
|
||||
openComputer: (params) => {
|
||||
opened.push(params);
|
||||
},
|
||||
closeComputer: () => {},
|
||||
};
|
||||
|
||||
return Object.assign(value, { switches, opened });
|
||||
}
|
||||
|
||||
describe("ComputerRefChip", () => {
|
||||
test("opens the viewer in its browser and switches nothing", async () => {
|
||||
const { html, value } = render({
|
||||
computer_id: 2,
|
||||
computers: [main, sandbox],
|
||||
});
|
||||
|
||||
expect(html).toContain("SEO box");
|
||||
// The chip's only job is opening, so no switch affordance survives.
|
||||
expect(html).not.toContain("Switch to");
|
||||
expect(value.switches).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("the computer the bot is on is still a chip, sunken with a check", () => {
|
||||
const { html } = render({ computer_id: 1, computers: [main, sandbox] });
|
||||
|
||||
expect(html).toContain("Main computer");
|
||||
expect(html).toContain("<button");
|
||||
expect(html).toContain("bg-sunken");
|
||||
expect(html).toContain('title="Open Main computer"');
|
||||
});
|
||||
|
||||
test("a computer the bot can't use stays a plain name", () => {
|
||||
const { html } = render({ computer_id: 9, computers: [main] });
|
||||
|
||||
expect(html).toContain("computer 9");
|
||||
expect(html).not.toContain("<button");
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import { useContext } from "react";
|
||||
import Button from "@moduletrace/twui/layout/Button";
|
||||
import Span from "@moduletrace/twui/layout/Span";
|
||||
import { Check, Monitor } from "lucide-react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { BotComputersContext } from "../(data)/bot-computers-context";
|
||||
|
||||
type Props = {
|
||||
@@ -9,33 +10,22 @@ type Props = {
|
||||
};
|
||||
|
||||
/**
|
||||
* A computer the bot named in a message (`computerRef`): a button that
|
||||
* switches the bot to it at once, or a checked chip when it's already on
|
||||
* it. A computer the bot can't use (any more) is shown by name only.
|
||||
* A computer the bot named in a message (`computerRef`): a chip that opens
|
||||
* it in the viewer, in its browser. The one the bot is already on says so,
|
||||
* sunken with a check. A computer the bot can't use (any more) is shown by
|
||||
* name only.
|
||||
*/
|
||||
export default function ComputerRefChip({ computer_id }: Props) {
|
||||
const { computers, switching_id, error, switchComputer } = useContext(BotComputersContext);
|
||||
const { computers, openComputer } = useContext(BotComputersContext);
|
||||
|
||||
const index = computers.findIndex((c) => c.id == computer_id);
|
||||
const computer = computers[index];
|
||||
const computer = computers.find((c) => c.id == computer_id);
|
||||
const name = computer?.name || `computer ${computer_id}`;
|
||||
|
||||
if (!computer) {
|
||||
if (!computer?.id) {
|
||||
return <Span className="font-bold">{name}</Span>;
|
||||
}
|
||||
|
||||
if (index == 0) {
|
||||
return (
|
||||
<Span
|
||||
title="The computer this bot is on"
|
||||
className="inline-flex h-6 gap-1.5 items-center px-2 align-middle rounded-default-xs bg-sunken text-[13px] font-bold leading-4 text-ink whitespace-nowrap"
|
||||
>
|
||||
<Monitor size={14} aria-hidden />
|
||||
{name}
|
||||
<Check size={14} aria-hidden />
|
||||
</Span>
|
||||
);
|
||||
}
|
||||
const current = computers[0]?.id == computer.id;
|
||||
|
||||
return (
|
||||
<Button
|
||||
@@ -43,14 +33,20 @@ export default function ComputerRefChip({ computer_id }: Props) {
|
||||
variant="outlined"
|
||||
color="gray"
|
||||
size="smaller"
|
||||
title={error && switching_id == undefined ? error : `Switch to ${name}`}
|
||||
loading={switching_id == computer_id}
|
||||
disabled={Boolean(switching_id)}
|
||||
onClick={() => switchComputer({ computer_id })}
|
||||
className="inline-flex h-6 gap-1.5! flex-nowrap align-middle px-2! text-[13px] font-bold text-ink whitespace-nowrap"
|
||||
title={`Open ${name}`}
|
||||
onClick={() =>
|
||||
openComputer({ computer_id: computer.id || 0, mode: "browser" })
|
||||
}
|
||||
className={twMerge(
|
||||
"inline-flex h-6 gap-1.5! flex-nowrap align-middle px-2! text-[13px] font-bold whitespace-nowrap text-ink dark:text-ink",
|
||||
current
|
||||
? "bg-sunken hover:bg-sunken outline-0"
|
||||
: "bg-transparent hover:bg-sunken",
|
||||
)}
|
||||
>
|
||||
<Monitor size={14} aria-hidden className="shrink-0" />
|
||||
{name}
|
||||
{current ? <Check size={14} aria-hidden className="shrink-0" /> : null}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user