claim controller ui

This commit is contained in:
Daniel Bulant 2026-05-12 23:42:12 +02:00
parent a7a7eeac0c
commit ea1f1a3358
No known key found for this signature in database
3 changed files with 95 additions and 10 deletions

View file

@ -276,13 +276,29 @@ export const deviceSocketApp = new Elysia().group("/dev-socket", (app) =>
export const deviceClaimApp = new Elysia()
.use(betterAuthElysia)
.group("/devices", (app) =>
app.post(
"/:deviceId/connect",
async ({ user, params }) => {
await claimDeviceForUser(params.deviceId, user.id);
sendDeviceEvent(params.deviceId, { type: "device_connected" });
return { ok: true, deviceId: params.deviceId, userId: user.id };
},
{ auth: true },
),
app
.get(
"/mine",
async ({ user }) => {
const devices = await db
.select({
id: deviceConnection.id,
lastSeen: deviceConnection.lastSeen,
})
.from(deviceConnection)
.where(eq(deviceConnection.userId, user.id));
return { devices };
},
{ auth: true },
)
.post(
"/:deviceId/connect",
async ({ user, params }) => {
await claimDeviceForUser(params.deviceId, user.id);
sendDeviceEvent(params.deviceId, { type: "device_connected" });
return { ok: true, deviceId: params.deviceId, userId: user.id };
},
{ auth: true },
),
);

View file

@ -0,0 +1,67 @@
import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { Button } from "#/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "#/components/ui/card";
import { useParty } from "#/hooks/use-party";
import { useUser } from "#/hooks/user";
import { client } from "#/lib/eden";
const CONTROLLER_DEVICE_ID = "esp32-1";
export function DeviceChoice() {
const { user } = useUser();
const { party } = useParty();
const [dismissed, setDismissed] = useState(false);
const [isClaiming, setIsClaiming] = useState(false);
const { data, isLoading } = useQuery({
queryKey: ["devices", "mine"],
queryFn: () => client.api.devices.mine.get(),
enabled: Boolean(user && !party && !dismissed),
});
if (!user || party || dismissed || isLoading) return null;
const devices = data?.data?.devices ?? [];
if (devices.length > 0) return null;
return (
<Card>
<CardHeader>
<CardTitle>Play with the controller?</CardTitle>
<CardDescription>
Claim {CONTROLLER_DEVICE_ID} for this session, or continue on this
device only.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-3 sm:grid-cols-2">
<Button
size="lg"
disabled={isClaiming}
onClick={async () => {
setIsClaiming(true);
try {
await client.api
.devices({ deviceId: CONTROLLER_DEVICE_ID })
.connect.post();
setDismissed(true);
} finally {
setIsClaiming(false);
}
}}
>
Play on controller
</Button>
<Button size="lg" variant="outline" onClick={() => setDismissed(true)}>
Play without controller
</Button>
</CardContent>
</Card>
);
}

View file

@ -1,4 +1,5 @@
import { createFileRoute } from "@tanstack/react-router";
import { DeviceChoice } from "#/components/device-choice";
import { PartyView } from "#/components/party/party-view";
import { PartyQr } from "#/components/party-qr";
import { StartParty } from "#/components/start-party";
@ -16,8 +17,9 @@ function App() {
return (
<MainContent>
<UserInfo />
<DeviceChoice />
{!user?.lastSyncAt && <SyncButton />}
{user && party?.data?.status != "running" && <PartyQr />}
{user && party?.data?.status !== "running" && <PartyQr />}
{party && !party.data && members.length > 1 && <StartParty />}
{party?.data && <PartyView />}
</MainContent>