diff --git a/src/app/router/connectorRoutes.tsx b/src/app/router/connectorRoutes.tsx index 6c7332c979..e9626b393b 100644 --- a/src/app/router/connectorRoutes.tsx +++ b/src/app/router/connectorRoutes.tsx @@ -1,5 +1,4 @@ import ChooseConnector from "@screens/connectors/ChooseConnector"; -import ConnectBtcpay from "@screens/connectors/ConnectBtcpay"; import ConnectCitadel from "@screens/connectors/ConnectCitadel"; import ConnectEclair from "@screens/connectors/ConnectEclair"; import ConnectGaloy, { galoyUrls } from "@screens/connectors/ConnectGaloy"; @@ -14,6 +13,8 @@ import ConnectUmbrel from "@screens/connectors/ConnectUmbrel"; import { Route } from "react-router-dom"; import i18n from "~/i18n/i18nConfig"; +import ConnectBtcpayCommando from "~/app/screens/connectors/ConnectBtcpayCommando"; +import ConnectBtcpayLND from "~/app/screens/connectors/ConnectBtcpayLND"; import ConnectNWC from "~/app/screens/connectors/ConnectNWC"; import ConnectVoltage from "~/app/screens/connectors/ConnectVoltage"; import ConnectCommando from "../screens/connectors/ConnectCommando"; @@ -81,6 +82,18 @@ const connectorMap: { [key: string]: ConnectorRoute } = { title: i18n.t("translation:choose_connector.umbrel_lightning_node.title"), logo: lightning_node, }, + "btcpay-lnd": { + path: "lnd", + element: , + title: i18n.t("translation:choose_connector.lnd.title"), + logo: lnd, + }, + "btcpay-commando": { + path: "commando", + element: , + title: i18n.t("translation:choose_connector.commando.title"), + logo: core_ln, + }, "raspiblitz-lnd": { path: "lnd", element: , @@ -153,12 +166,6 @@ const connectorMap: { [key: string]: ConnectorRoute } = { title: i18n.t("translation:choose_connector.bitcoin_jungle.title"), logo: galoyBitcoinJungle, }, - btcpay: { - path: "btcpay", - element: , - title: i18n.t("translation:choose_connector.btcpay.title"), - logo: btcpay, - }, voltage: { path: "voltage", element: , @@ -203,6 +210,10 @@ const distributionMap: { [key: string]: { logo: string; children: Route[] } } = connectorMap["lnbits"], ], }, + btcpay: { + logo: btcpay, + children: [connectorMap["btcpay-lnd"], connectorMap["btcpay-commando"]], + }, umbrel: { logo: umbrel, children: [ @@ -250,7 +261,7 @@ function getConnectorRoutes(): ConnectorRoute[] { connectorMap["lnbits"], connectorMap["lnd-hub-go"], connectorMap["eclair"], - connectorMap["btcpay"], + getDistribution("btcpay"), connectorMap["voltage"], connectorMap[galoyPaths.blink], connectorMap[galoyPaths.bitcoinJungle], diff --git a/src/app/screens/connectors/ConnectBtcpayCommando/index.tsx b/src/app/screens/connectors/ConnectBtcpayCommando/index.tsx new file mode 100644 index 0000000000..a7c1df15ea --- /dev/null +++ b/src/app/screens/connectors/ConnectBtcpayCommando/index.tsx @@ -0,0 +1,144 @@ +import CompanionDownloadInfo from "@components/CompanionDownloadInfo"; +import ConnectorForm from "@components/ConnectorForm"; +import TextField from "@components/form/TextField"; +import ConnectionErrorToast from "@components/toasts/ConnectionErrorToast"; +import fetchAdapter from "@vespaiach/axios-fetch-adapter"; +import axios from "axios"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; +import toast from "~/app/components/Toast"; +import msg from "~/common/lib/msg"; + +import logo from "/static/assets/icons/btcpay.svg"; + +const initialFormData = { + url: "", + macaroon: "", + name: "", +}; + +export default function ConnectBtcpayCommando() { + const navigate = useNavigate(); + const { t } = useTranslation("translation", { + keyPrefix: "choose_connector.btcpay_commando", + }); + const [formData, setFormData] = useState(initialFormData); + const [loading, setLoading] = useState(false); + const [hasTorSupport, setHasTorSupport] = useState(false); + + function getConfigUrl(data: string) { + const configUrl = data.trim().replace("config=", ""); + try { + return new URL(configUrl); + } catch (e) { + return null; + } + } + async function handleChange(event: React.ChangeEvent) { + const configUrl = getConfigUrl(event.target.value); + if (!configUrl) { + return; + } + const host = configUrl.host; + try { + const response = await axios.get<{ + configurations: [{ uri: string; adminMacaroon: string }]; + }>(configUrl.toString(), { adapter: fetchAdapter }); + + if (response.data.configurations[0].uri) { + setFormData({ + url: response.data.configurations[0].uri, + macaroon: response.data.configurations[0].adminMacaroon, + name: host, + }); + } + } catch (e) { + console.error(e); + toast.error(t("errors.connection_failed")); + } + } + + function getConnectorType() { + if (formData.url.match(/\.onion/i) && !hasTorSupport) { + return "nativelnd"; + } + // default to LND + return "lnd"; + } + + async function handleSubmit(event: React.FormEvent) { + event.preventDefault(); + setLoading(true); + const { url, macaroon, name } = formData; + const account = { + name: name || "LND", + config: { + macaroon, + url, + }, + connector: getConnectorType(), + }; + + try { + let validation; + // TODO: for native connectors we currently skip the validation because it is too slow (booting up Tor etc.) + if (account.connector === "nativelnd") { + validation = { valid: true, error: "" }; + } else { + validation = await msg.request("validateAccount", account); + } + + if (validation.valid) { + const addResult = await msg.request("addAccount", account); + if (addResult.accountId) { + await msg.request("selectAccount", { + id: addResult.accountId, + }); + navigate("/test-connection"); + } + } else { + toast.error( + + ); + } + } catch (e) { + console.error(e); + let message = ""; + if (e instanceof Error) { + message += `${e.message}`; + } + toast.error(); + } + setLoading(false); + } + + return ( + + + {formData.url.match(/\.onion/i) && ( +
+ { + setHasTorSupport(hasTor); + }} + /> +
+ )} +
+ ); +} diff --git a/src/app/screens/connectors/ConnectBtcpay/index.tsx b/src/app/screens/connectors/ConnectBtcpayLND/index.tsx similarity index 97% rename from src/app/screens/connectors/ConnectBtcpay/index.tsx rename to src/app/screens/connectors/ConnectBtcpayLND/index.tsx index 5efb6d0376..5ece6082ef 100644 --- a/src/app/screens/connectors/ConnectBtcpay/index.tsx +++ b/src/app/screens/connectors/ConnectBtcpayLND/index.tsx @@ -18,10 +18,10 @@ const initialFormData = { name: "", }; -export default function ConnectBtcpay() { +export default function ConnectBtcpayLND() { const navigate = useNavigate(); const { t } = useTranslation("translation", { - keyPrefix: "choose_connector.btcpay", + keyPrefix: "choose_connector.btcpay_lnd", }); const [formData, setFormData] = useState(initialFormData); const [loading, setLoading] = useState(false); diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 144b6e7e6b..91bc23a8ac 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -258,10 +258,25 @@ } }, "btcpay": { - "title": "BTCPay Server", + "title": "BTCPay Server" + }, + "btcpay_lnd": { + "page": { + "title": "Connect to BTCPay LND", + "instructions": "Navigate to your BTCPayServer and log in as an admin. Go to Server Settings > Services > LND REST - See information. Then Click \"See QR Code information\" and copy the QR Code data. Paste it below:" + }, + "config": { + "label": "Config data", + "placeholder": "config=https://your-btc-pay.org/lnd-config/212121/lnd.co" + }, + "errors": { + "connection_failed": "Connection failed. Is the BTCPay connection URL correct and accessible?" + } + }, + "btcpay_commando": { "page": { - "title": "Connect to your BTCPay LND node", - "instructions": "Navigate to your BTCPayServer and log in as an admin. Go to Server Settings > Services > LND Rest - See information. Then Click \"See QR Code information\" and copy the QR Code data. Paste it below:" + "title": "Connect to BTCPay CLN", + "instructions": "Navigate to your BTCPayServer and log in as an admin. Go to Server Settings > Services > C- Lightning REST - See information. Then Click \"See QR Code information\" and copy the QR Code data. Paste it below:" }, "config": { "label": "Config data",