Tictactoe.tsx raw
1 import { AlertTriangleIcon, CopyIcon, ExternalLinkIcon } from "lucide-react";
2 import React, { useEffect } from "react";
3 import { toast } from "sonner";
4 import AppHeader from "src/components/AppHeader";
5 import AppCard from "src/components/connections/AppCard";
6 import { appStoreApps } from "src/components/connections/SuggestedAppData";
7 import QRCode from "src/components/QRCode";
8 import { Alert, AlertDescription, AlertTitle } from "src/components/ui/alert";
9 import { Button } from "src/components/ui/button";
10 import {
11 Card,
12 CardContent,
13 CardHeader,
14 CardTitle,
15 } from "src/components/ui/card";
16 import { LoadingButton } from "src/components/ui/custom/loading-button";
17 import { Input } from "src/components/ui/input";
18 import {
19 DEFAULT_APP_BUDGET_RENEWAL,
20 DEFAULT_APP_BUDGET_SATS,
21 } from "src/constants";
22 import { useApps } from "src/hooks/useApps";
23 import { copyToClipboard } from "src/lib/clipboard";
24 import { createApp } from "src/requests/createApp";
25 import { handleRequestError } from "src/utils/handleRequestError";
26 import { openLink } from "src/utils/openLink";
27
28 export function Tictactoe() {
29 const appId = "tictactoe";
30 const [isLoading, setLoading] = React.useState(false);
31 const [appLink, setAppLink] = React.useState("");
32 const { data: appsData, mutate: reloadApps } = useApps(undefined, undefined, {
33 appStoreAppId: appId,
34 });
35 const tictactoeApps = appsData?.apps;
36 const appStoreApp = appStoreApps.find((app) => app.id === appId)!;
37
38 useEffect(() => {
39 if (appLink) {
40 window.open(appLink, "_blank");
41 }
42 }, [appLink]);
43
44 const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
45 event.preventDefault();
46 setLoading(true);
47 (async () => {
48 try {
49 const createAppResponse = await createApp({
50 name: appStoreApp.title,
51 scopes: ["get_info", "lookup_invoice", "make_invoice", "pay_invoice"],
52 maxAmountSat: DEFAULT_APP_BUDGET_SATS,
53 budgetRenewal: DEFAULT_APP_BUDGET_RENEWAL,
54 metadata: {
55 app_store_app_id: appId,
56 },
57 });
58
59 setAppLink(
60 `https://lntictactoe.com/#nwc=${encodeURIComponent(createAppResponse.pairingUri)}`
61 );
62 toast("Tic Tac Toe connection created");
63 } catch (error) {
64 handleRequestError("Failed to create connection", error);
65 }
66 setLoading(false);
67 reloadApps();
68 })();
69 };
70
71 return (
72 <div className="grid gap-5">
73 <AppHeader
74 pageTitle={appStoreApp.title}
75 title={
76 <div className="flex flex-row items-center">
77 <img src={appStoreApp.logo} className="w-14 h-14 rounded-lg mr-4" />
78 <div className="flex flex-col">
79 <div>{appStoreApp.title}</div>
80 <div className="text-sm font-normal text-muted-foreground">
81 {appStoreApp.description}
82 </div>
83 </div>
84 </div>
85 }
86 />
87 {appLink ? (
88 <div className="max-w-lg flex flex-col gap-5">
89 <p>Open the link below to start playing.</p>
90 <Alert>
91 <AlertTriangleIcon />
92 <AlertTitle>
93 Save this link and add it to your home screen
94 </AlertTitle>
95 <AlertDescription>
96 This link will only be shown once and can't be retrieved
97 afterwards. Please make sure to keep it somewhere safe.
98 </AlertDescription>
99 </Alert>
100 <div
101 className="flex flex-col items-center relative cursor-pointer"
102 onClick={() => openLink(appLink)}
103 >
104 <QRCode value={appLink} />
105 <img
106 src={appStoreApp.logo}
107 className="absolute w-12 h-12 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-muted p-1 rounded-xl"
108 />
109 </div>
110 <div className="flex gap-2">
111 <Input disabled readOnly type="text" value={appLink} />
112 <Button onClick={() => copyToClipboard(appLink)} variant="outline">
113 <CopyIcon />
114 Copy
115 </Button>
116 <Button onClick={() => openLink(appLink)} variant="outline">
117 <ExternalLinkIcon />
118 Open
119 </Button>
120 </div>
121 </div>
122 ) : (
123 <Card className="max-w-lg">
124 <CardHeader>
125 <CardTitle className="text-2xl">About the App</CardTitle>
126 </CardHeader>
127 <CardContent className="flex flex-col gap-3">
128 <p className="text-muted-foreground">
129 By connecting Tic Tac Toe to your Alby Hub, you can play
130 <br />
131 tic tac toe with your friends and earn satoshis.
132 </p>
133 <div className="flex flex-col gap-5">
134 <form
135 onSubmit={handleSubmit}
136 className="flex flex-col items-start gap-5 max-w-lg"
137 >
138 <LoadingButton loading={isLoading} type="submit">
139 Start playing
140 </LoadingButton>
141 </form>
142 </div>
143 </CardContent>
144 </Card>
145 )}
146 {!!tictactoeApps?.length && (
147 <>
148 <h2 className="font-semibold text-xl">Tic Tac Toe connections</h2>
149 <div className="grid grid-cols-1 lg:grid-cols-2 gap-3 items-stretch">
150 {tictactoeApps.map((app, index) => (
151 <AppCard key={index} app={app} />
152 ))}
153 </div>
154 </>
155 )}
156 </div>
157 );
158 }
159