claim controller ui
This commit is contained in:
parent
a7a7eeac0c
commit
ea1f1a3358
3 changed files with 95 additions and 10 deletions
|
|
@ -276,13 +276,29 @@ export const deviceSocketApp = new Elysia().group("/dev-socket", (app) =>
|
||||||
export const deviceClaimApp = new Elysia()
|
export const deviceClaimApp = new Elysia()
|
||||||
.use(betterAuthElysia)
|
.use(betterAuthElysia)
|
||||||
.group("/devices", (app) =>
|
.group("/devices", (app) =>
|
||||||
app.post(
|
app
|
||||||
"/:deviceId/connect",
|
.get(
|
||||||
async ({ user, params }) => {
|
"/mine",
|
||||||
await claimDeviceForUser(params.deviceId, user.id);
|
async ({ user }) => {
|
||||||
sendDeviceEvent(params.deviceId, { type: "device_connected" });
|
const devices = await db
|
||||||
return { ok: true, deviceId: params.deviceId, userId: user.id };
|
.select({
|
||||||
},
|
id: deviceConnection.id,
|
||||||
{ auth: true },
|
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 },
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
67
web/src/components/device-choice.tsx
Normal file
67
web/src/components/device-choice.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { DeviceChoice } from "#/components/device-choice";
|
||||||
import { PartyView } from "#/components/party/party-view";
|
import { PartyView } from "#/components/party/party-view";
|
||||||
import { PartyQr } from "#/components/party-qr";
|
import { PartyQr } from "#/components/party-qr";
|
||||||
import { StartParty } from "#/components/start-party";
|
import { StartParty } from "#/components/start-party";
|
||||||
|
|
@ -16,8 +17,9 @@ function App() {
|
||||||
return (
|
return (
|
||||||
<MainContent>
|
<MainContent>
|
||||||
<UserInfo />
|
<UserInfo />
|
||||||
|
<DeviceChoice />
|
||||||
{!user?.lastSyncAt && <SyncButton />}
|
{!user?.lastSyncAt && <SyncButton />}
|
||||||
{user && party?.data?.status != "running" && <PartyQr />}
|
{user && party?.data?.status !== "running" && <PartyQr />}
|
||||||
{party && !party.data && members.length > 1 && <StartParty />}
|
{party && !party.data && members.length > 1 && <StartParty />}
|
||||||
{party?.data && <PartyView />}
|
{party?.data && <PartyView />}
|
||||||
</MainContent>
|
</MainContent>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue