diff --git a/api/src/routes/device-socket.ts b/api/src/routes/device-socket.ts index 6190c6a..b4dfb93 100644 --- a/api/src/routes/device-socket.ts +++ b/api/src/routes/device-socket.ts @@ -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 }, + ), ); diff --git a/web/src/components/device-choice.tsx b/web/src/components/device-choice.tsx new file mode 100644 index 0000000..5a5aa4f --- /dev/null +++ b/web/src/components/device-choice.tsx @@ -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 ( + + + Play with the controller? + + Claim {CONTROLLER_DEVICE_ID} for this session, or continue on this + device only. + + + + + + + + ); +} diff --git a/web/src/routes/index.tsx b/web/src/routes/index.tsx index 796be4c..35081f9 100644 --- a/web/src/routes/index.tsx +++ b/web/src/routes/index.tsx @@ -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 ( + {!user?.lastSyncAt && } - {user && party?.data?.status != "running" && } + {user && party?.data?.status !== "running" && } {party && !party.data && members.length > 1 && } {party?.data && }