IsolatedAppTopupDialog.tsx raw
1 import React from "react";
2 import { toast } from "sonner";
3 import { LoadingButton } from "src/components/ui/custom/loading-button";
4 import {
5 Dialog,
6 DialogContent,
7 DialogDescription,
8 DialogFooter,
9 DialogHeader,
10 DialogTitle,
11 DialogTrigger,
12 } from "src/components/ui/dialog";
13 import { Input } from "src/components/ui/input";
14 import { Label } from "src/components/ui/label";
15 import { useApp } from "src/hooks/useApp";
16 import { handleRequestError } from "src/utils/handleRequestError";
17 import { request } from "src/utils/request";
18
19 type IsolatedAppTopupProps = {
20 appId: number;
21 };
22
23 export function IsolatedAppTopupDialog({
24 appId,
25 children,
26 }: React.PropsWithChildren<IsolatedAppTopupProps>) {
27 const { mutate: reloadApp } = useApp(appId);
28 const [amountSat, setAmountSat] = React.useState("");
29 const [description, setDescription] = React.useState("");
30 const [loading, setLoading] = React.useState(false);
31 const [open, setOpen] = React.useState(false);
32 async function onSubmit(e: React.FormEvent) {
33 e.preventDefault();
34 setLoading(true);
35 try {
36 await request(`/api/transfers`, {
37 method: "POST",
38 headers: {
39 "Content-Type": "application/json",
40 },
41 body: JSON.stringify({
42 toAppId: appId,
43 amountSat: +amountSat,
44 description,
45 }),
46 });
47 await reloadApp();
48 toast(`Successfully increased balance by ${+amountSat} sats`);
49 reset();
50 } catch (error) {
51 handleRequestError("Failed to increase sub-wallet balance", error);
52 }
53 setLoading(false);
54 }
55
56 function reset() {
57 setOpen(false);
58 setAmountSat("");
59 setDescription("");
60 }
61
62 return (
63 <Dialog open={open} onOpenChange={setOpen}>
64 <DialogTrigger asChild>{children}</DialogTrigger>
65 <DialogContent>
66 <form onSubmit={onSubmit}>
67 <DialogHeader>
68 <DialogTitle>Increase Balance</DialogTitle>
69 <DialogDescription>
70 Increase the balance of this sub-wallet. Make sure you always
71 maintain enough funds in your lightning balance to prevent
72 sub-wallets becoming unspendable.
73 </DialogDescription>
74 </DialogHeader>
75 <div className="grid gap-2 mt-5">
76 <Label htmlFor="amount">Amount (sats)</Label>
77 <Input
78 autoFocus
79 id="amount"
80 type="number"
81 required
82 value={amountSat}
83 onChange={(e) => {
84 setAmountSat(e.target.value.trim());
85 }}
86 />
87 </div>
88 <div className="grid gap-2 mt-3">
89 <Label htmlFor="description">Description (optional)</Label>
90 <Input
91 id="description"
92 type="text"
93 placeholder="transfer"
94 value={description}
95 onChange={(e) => {
96 setDescription(e.target.value);
97 }}
98 />
99 </div>
100 <DialogFooter className="mt-5">
101 <LoadingButton loading={loading}>Increase</LoadingButton>
102 </DialogFooter>
103 </form>
104 </DialogContent>
105 </Dialog>
106 );
107 }
108