import {
ClipboardPasteIcon,
ExternalLinkIcon,
MoveRightIcon,
RefreshCwIcon,
} from "lucide-react";
import { useEffect, useState } from "react";
import { useNavigate, useSearchParams } from "react-router";
import { toast } from "sonner";
import { AnchorReserveAlert } from "src/components/AnchorReserveAlert";
import AppHeader from "src/components/AppHeader";
import { CurrencyInputField } from "src/components/CurrencyInputField";
import { FixedFloatButton } from "src/components/FixedFloatButton";
import { FixedFloatSwapInFlow } from "src/components/FixedFloatSwapInFlow";
import Loading from "src/components/Loading";
import LowReceivingCapacityAlert from "src/components/LowReceivingCapacityAlert";
import ResponsiveLinkButton from "src/components/ResponsiveLinkButton";
import { Button } from "src/components/ui/button";
import { LoadingButton } from "src/components/ui/custom/loading-button";
import { Input } from "src/components/ui/input";
import { Label } from "src/components/ui/label";
import { RadioGroup, RadioGroupItem } from "src/components/ui/radio-group";
import { Separator } from "src/components/ui/separator";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "src/components/ui/tabs";
import { useBalances } from "src/hooks/useBalances";
import { useInfo } from "src/hooks/useInfo";
import { useSwapInfo } from "src/hooks/useSwaps";
import {
CreateInvoiceRequest,
InitiateSwapRequest,
SwapResponse,
Transaction,
} from "src/types";
import { openLink } from "src/utils/openLink";
import { request } from "src/utils/request";
export default function Swap() {
const [searchParams, setSearchParams] = useSearchParams();
const [tab, setTab] = useState(searchParams.get("type") || "in");
useEffect(() => {
const newTabValue = searchParams.get("type");
if (newTabValue) {
setTab(newTabValue);
setSearchParams({}, { replace: true });
}
}, [searchParams, setSearchParams]);
return (
);
}
function SwapInForm() {
const [swapFrom, setSwapFrom] = useState<"internal" | "external" | "crypto">(
"external"
);
const { data: info, hasChannelManagement } = useInfo();
const { data: balances } = useBalances();
const { data: swapInfo } = useSwapInfo("in");
const navigate = useNavigate();
const [swapAmountSat, setSwapAmountSat] = useState("");
const [loading, setLoading] = useState(false);
const [cryptoTransaction, setCryptoTransaction] =
useState(null);
const onSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
setLoading(true);
if (swapFrom === "crypto") {
const tx = await request("/api/invoices", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
amountMsat: (parseInt(swapAmountSat) || 0) * 1000,
description: "Fixed Float swap",
} as CreateInvoiceRequest),
});
if (!tx?.invoice) {
throw new Error("Failed to create invoice");
}
setCryptoTransaction(tx);
openLink(
`https://ff.io/?to=BTCLN&address=${encodeURIComponent(tx.invoice)}&ref=qnnjvywb`
);
toast("Initiated swap");
return;
}
const payload: InitiateSwapRequest = {
swapAmountSat: parseInt(swapAmountSat),
};
const swapInResponse = await request("/api/swaps/in", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!swapInResponse) {
throw new Error("Error swapping in");
}
navigate(
`/wallet/swap/in/status/${swapInResponse.swapId}${swapFrom === "internal" ? "?internal=true" : ""}`
);
toast("Initiated swap");
} catch (error) {
toast.error("Failed to initiate swap", {
description: (error as Error).message,
});
} finally {
setLoading(false);
}
};
if (!info || !balances || !swapInfo) {
return ;
}
const spendableOnchainBalance = balances.onchain.spendableSat;
const isInternalSwap = swapFrom === "internal";
const isCryptoSwappingState =
swapFrom === "crypto" && cryptoTransaction !== null;
return (
);
}
function SwapOutForm() {
const { data: swapInfo } = useSwapInfo("out");
const navigate = useNavigate();
const { data: balances } = useBalances();
const [isInternalSwap, setInternalSwap] = useState(true);
const [swapAmountSat, setSwapAmountSat] = useState("");
const [destination, setDestination] = useState("");
const [loading, setLoading] = useState(false);
const onSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
setLoading(true);
const payload: InitiateSwapRequest = {
swapAmountSat: parseInt(swapAmountSat),
destination,
};
const swapOutResponse = await request("/api/swaps/out", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!swapOutResponse) {
throw new Error("Error swapping out");
}
navigate(`/wallet/swap/out/status/${swapOutResponse.swapId}`);
toast("Initiated swap");
} catch (error) {
toast.error("Failed to initiate swap", {
description: (error as Error).message,
});
} finally {
setLoading(false);
}
};
const paste = async () => {
const text = await navigator.clipboard.readText();
setDestination(text.trim());
};
if (!balances || !swapInfo) {
return ;
}
return (
);
}