A Prediction Market on the AT Protocol
1import { ChartLine } from "lucide-react";
2import { DrawerTitle, Drawer, DrawerContent, DrawerHeader, DrawerTrigger, DrawerFooter } from "@/web/components/ui/drawer";
3import { Button } from "@/web/components/ui/button";
4import { Input } from "@/web/components/ui/input";
5import { useState } from "react";
6import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/web/components/ui/select";
7import { Spinner } from "@/web/components/ui/spinner";
8import { useAuth } from "@/web/hooks/useAuth";
9import { createMarket } from "@/core/atproto-api";
10import { toast } from "sonner";
11import { useQueryClient } from "@tanstack/react-query";
12
13export function AddMarket() {
14
15 const { client, profile } = useAuth();
16 const [open, setOpen] = useState(false);
17 const [question, setQuestion] = useState("");
18 const [closesAt, setClosesAt] = useState<string | undefined>(undefined);
19 const [liquidity, setLiquidity] = useState<10 | 50 | 200>(50);
20 const [loading, setLoading] = useState(false);
21 const queryClient = useQueryClient();
22
23 async function handleAddMarket() {
24 try {
25 setLoading(true);
26 const res = await createMarket(question, liquidity, new Date(closesAt!).toISOString(), profile.did, client)
27 if (res.uri) toast.success(<>Created Market <a target="_blank" href={`https://pdsls.dev/${res.uri}`}>{res.uri}</a></>)
28 queryClient.invalidateQueries({ queryKey: ['markets'] });
29 setOpen(false);
30 } catch (e) {
31 toast.error((e as any).message)
32 } finally {
33 setLoading(false);
34 }
35 }
36
37 return <Drawer open={open} onOpenChange={setOpen}>
38 <DrawerTrigger asChild>
39 <Button className="absolute bottom-8 right-8">
40 + <ChartLine />
41 </Button>
42 </DrawerTrigger>
43 <DrawerContent>
44 <DrawerHeader>
45 <DrawerTitle>Add a Question</DrawerTitle>
46 </DrawerHeader>
47 <div className="flex flex-col gap-2 px-4">
48 <Input type="text" placeholder="...?" value={question} onChange={(e) => setQuestion(e.target.value)} />
49 <Input type="date" placeholder="Closing Date" value={closesAt} onChange={(e) => setClosesAt(e.target.value)} />
50 <Select value={liquidity.toString()} onValueChange={(v) => setLiquidity(parseInt(v) as 10 | 50 | 200)}>
51 <SelectTrigger className="w-full"><SelectValue placeholder="Liquidity" /></SelectTrigger>
52 <SelectContent>
53 <SelectItem value="10">Small</SelectItem>
54 <SelectItem value="50">Medium</SelectItem>
55 <SelectItem value="200">Large</SelectItem>
56 </SelectContent>
57 </Select>
58 </div>
59 <DrawerFooter>
60 <Button disabled={loading || !question || !closesAt} onClick={handleAddMarket}>{loading && <Spinner />} Add</Button>
61 </DrawerFooter>
62 </DrawerContent>
63 </Drawer>
64}