diff --git a/public/assets/apps/logos/alby.png b/public/assets/apps/logos/alby.png new file mode 100644 index 00000000..8a9b5ef3 Binary files /dev/null and b/public/assets/apps/logos/alby.png differ diff --git a/src/assets/apps/preview/alby/1.png b/src/assets/apps/preview/alby/1.png new file mode 100644 index 00000000..14fffb34 Binary files /dev/null and b/src/assets/apps/preview/alby/1.png differ diff --git a/src/assets/apps/preview/alby/2.png b/src/assets/apps/preview/alby/2.png new file mode 100644 index 00000000..4271db8f Binary files /dev/null and b/src/assets/apps/preview/alby/2.png differ diff --git a/src/assets/apps/preview/alby/3.png b/src/assets/apps/preview/alby/3.png new file mode 100644 index 00000000..7dffc266 Binary files /dev/null and b/src/assets/apps/preview/alby/3.png differ diff --git a/src/i18n/langs/en.json b/src/i18n/langs/en.json index d887f493..00dbd99f 100644 --- a/src/i18n/langs/en.json +++ b/src/i18n/langs/en.json @@ -32,6 +32,19 @@ "jam": { "about": "Jam is a web interface for JoinMarket focusing on user-friendliness and ease-of-use. It aims to provide sensible defaults and be easy to use for beginners while still having the features advanced users expect.", "shortDescription": "Jam - A web interface for JoinMarket" + }, + "alby": { + "about": "Alby is a browser extension...MORE TEXT", + "shortDescription": "Alby - A browser extension FOR...", + "action": { + "install": "Install Alby", + "addAccount": "Connect", + "connection": { + "hint": "Please install the Alby extension first", + "success": "Raspiblitz account was added to Alby", + "error": "Adding Raspiblitz to Alby didn't work, please try again" + } + } } }, "apps": { @@ -196,16 +209,7 @@ "generating": "Generating", "generating_debug_report": "Generating Debug Report. This can take some time.", "debug_report_done": "Debug Report created!", - "connection_info_error": "Could not get connection info", - "alby": { - "title": "Add LND account to <0>Alby", - "label": "Add account", - "connection": { - "hint": "Please install the Alby extension first", - "success": "Raspiblitz account was added to Alby", - "error": "Adding Raspiblitz to Alby didn't work, please try again" - } - } + "connection_info_error": "Could not get connection info" }, "setup": { "blockchain_delete": "Delete Blockchain", diff --git a/src/i18n/langs/en_appInfo.json b/src/i18n/langs/en_appInfo.json index d497007e..5d06ee5c 100644 --- a/src/i18n/langs/en_appInfo.json +++ b/src/i18n/langs/en_appInfo.json @@ -32,6 +32,10 @@ "jam": { "about": "Jam is a web interface for JoinMarket focusing on user-friendliness and ease-of-use. It aims to provide sensible defaults and be easy to use for beginners while still having the features advanced users expect.", "shortDescription": "Jam - A web interface for JoinMarket" + }, + "alby": { + "about": "Alby is a browser extension...MORE TEXT", + "shortDescription": "Alby - A browser extension FOR..." } } } diff --git a/src/pages/Apps/AppCardAlby.tsx b/src/pages/Apps/AppCardAlby.tsx new file mode 100644 index 00000000..d9a6321b --- /dev/null +++ b/src/pages/Apps/AppCardAlby.tsx @@ -0,0 +1,109 @@ +import { InformationCircleIcon, PlusIcon } from "@heroicons/react/24/outline"; +import { FC } from "react"; +import { useTranslation } from "react-i18next"; +import { AppStatus } from "../..//models/app-status"; +import { App } from "../..//models/app.model"; +import AppIcon from "../../components/AppIcon"; +import { toast } from "react-toastify"; +import { instance } from "../../utils/interceptor"; + +export type Props = { + appInfo: App; + appStatusInfo: AppStatus; + onOpenDetails: (app: App) => void; +}; + +export const AppCard: FC = ({ appInfo, onOpenDetails }) => { + const { id, name } = appInfo; + const { t } = useTranslation(); + + const addAlbyAccountHandler = async () => { + const resp = await instance.get("/system/connection-info"); + + if ( + resp.status !== 200 || + !resp.data.lnd_admin_macaroon || + !resp.data.lnd_rest_onion + ) { + toast.error(t(`appInfo.${id}.action.connection_info_error`)); + return; + } + + const { lnd_admin_macaroon, lnd_rest_onion } = resp.data; + + if (!window.alby) { + toast.error(t(`appInfo.${id}.action.connection.hint`)); + } + + try { + await window.alby.enable(); + + const result = await window.alby.addAccount({ + name: "⚡️ Raspiblitz", + connector: "lnd", + config: { + adminkey: lnd_admin_macaroon, + url: lnd_rest_onion, + }, + }); + + if (result.success) { + toast.success(t(`appInfo.${id}.action.connection.success`)); + } else { + toast.error(t(`appInfo.${id}.action.connection.error`)); + } + } catch (e) { + toast.error(t(`appInfo.${id}.action.connection.error`)); + } + }; + + return ( +
+
+ {/* Icon */} +
+ +
+ {/* Content */} +
+

{name}

+

+ {t(`appInfo.${id}.shortDescription`)} +

+
+
+
+ {window.alby && ( + + )} + + {!window.alby && ( + + {t(`appInfo.${id}.action.install`)} + + )} + + +
+
+ ); +}; + +export default AppCard; diff --git a/src/pages/Apps/AppInfoAlby.tsx b/src/pages/Apps/AppInfoAlby.tsx new file mode 100644 index 00000000..dbbacf00 --- /dev/null +++ b/src/pages/Apps/AppInfoAlby.tsx @@ -0,0 +1,168 @@ +import { ChevronLeftIcon, PlusIcon } from "@heroicons/react/24/outline"; +import { FC, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import AppIcon from "../../components/AppIcon"; +import LoadingSpinner from "../../components/LoadingSpinner/LoadingSpinner"; +import { App } from "../../models/app.model"; +import { availableApps } from "../../utils/availableApps"; +import ImageCarousel from "./ImageCarousel"; +import { toast } from "react-toastify"; +import { instance } from "../../utils/interceptor"; + +export type Props = { + app: App; + onClose: () => void; +}; + +export const AppInfo: FC = ({ app, onClose }) => { + const { t } = useTranslation(); + const [isLoading, setIsLoading] = useState(true); + const [imgs, setImgs] = useState([]); + const { id, name } = app; + const { author, repository } = availableApps.get(id)!; + + useEffect(() => { + setIsLoading(true); + + async function loadAppImages() { + const promises = await Promise.allSettled([ + import(`../../assets/apps/preview/${id}/1.png`), + import(`../../assets/apps/preview/${id}/2.png`), + import(`../../assets/apps/preview/${id}/3.png`), + ]); + + promises.forEach((promise, i) => { + if (promise.status === "fulfilled") { + setImgs((prev) => { + prev[i] = promise.value.default; + return prev; + }); + } else { + // Ignore if image not available + } + }); + setIsLoading(false); + } + + loadAppImages(); + }, [id]); + + if (isLoading) { + return ( +
+ +
+ ); + } + + const addAlbyAccountHandler = async () => { + const resp = await instance.get("/system/connection-info"); + + if ( + resp.status !== 200 || + !resp.data.lnd_admin_macaroon || + !resp.data.lnd_rest_onion + ) { + toast.error(t(`appInfo.${id}.action.connection_info_error`)); + return; + } + + const { lnd_admin_macaroon, lnd_rest_onion } = resp.data; + + if (!window.alby) { + toast.error(t(`appInfo.${id}.action.connection.hint`)); + } + + try { + await window.alby.enable(); + + const result = await window.alby.addAccount({ + name: "⚡️ Raspiblitz", + connector: "lnd", + config: { + adminkey: lnd_admin_macaroon, + url: lnd_rest_onion, + }, + }); + + if (result.success) { + toast.success(t(`appInfo.${id}.action.connection.success`)); + } else { + toast.error(t(`appInfo.${id}.action.connection.error`)); + } + } catch (e) { + toast.error(t(`appInfo.${id}.action.connection.error`)); + } + }; + + return ( +
+ {/* Back Button */} +
+ +
+ + {/* Image box with title */} +
+ +

{name}

+ {window.alby && ( + + )} + + {!window.alby && ( + + {t(`appInfo.${id}.action.install`)} + + )} +
+ +
+ {!isLoading && } +
+ + {/* App Description */} +
+
+

{name}

+

+ {t("apps.about")} +

+

{t(`appInfo.${id}.about`)}

+

+ {t("apps.author")} +

+

{author}

+

+ {t("apps.source")} +

+ + {repository} + +
+
+
+ ); +}; + +export default AppInfo; diff --git a/src/pages/Apps/index.tsx b/src/pages/Apps/index.tsx index ff38e4c0..857f0cff 100644 --- a/src/pages/Apps/index.tsx +++ b/src/pages/Apps/index.tsx @@ -11,7 +11,9 @@ import { availableApps } from "../../utils/availableApps"; import { checkError } from "../../utils/checkError"; import { instance } from "../../utils/interceptor"; import AppCard from "./AppCard"; +import AppCardAlby from "./AppCardAlby"; import AppInfo from "./AppInfo"; +import AppInfoAlby from "./AppInfoAlby"; export const Apps: FC = () => { const { t } = useTranslation(["translation", "apps"]); @@ -59,17 +61,22 @@ export const Apps: FC = () => { }; if (showDetails && app) { - const appInfos = appStatus.find((a) => a.id === app.id)!; - return ( - installHandler(app.id)} - onUninstall={() => uninstallHandler(app.id)} - onClose={closeDetailsHandler} - /> - ); + let appInfos; + if (app.id === "alby") { + return ; + } else { + appInfos = appStatus.find((a) => a.id === app.id)!; + return ( + installHandler(app.id)} + onUninstall={() => uninstallHandler(app.id)} + onClose={closeDetailsHandler} + /> + ); + } } return ( @@ -121,6 +128,13 @@ export const Apps: FC = () => { ); })} + +
+ +
diff --git a/src/pages/Settings/index.tsx b/src/pages/Settings/index.tsx index ad26bd66..a0ddc348 100644 --- a/src/pages/Settings/index.tsx +++ b/src/pages/Settings/index.tsx @@ -1,5 +1,5 @@ import { FC, useEffect, useState } from "react"; -import { Trans, useTranslation } from "react-i18next"; +import { useTranslation } from "react-i18next"; import ConfirmModal from "../../components/ConfirmModal"; import useSSE from "../../hooks/use-sse"; import { enableGutter } from "../../utils"; @@ -8,8 +8,6 @@ import ChangePwModal from "./ChangePwModal"; import DebugLogBox from "./DebugLogBox"; import I18nBox from "./I18nBox"; import VersionBox from "./VersionBox"; -import { instance } from "utils/interceptor"; -import { toast } from "react-toastify"; const Settings: FC = () => { const { t } = useTranslation(); @@ -46,46 +44,6 @@ const Settings: FC = () => { setShowPwModal(false); }; - const addAlbyAccountHandler = async () => { - const resp = await instance.get("/system/connection-info"); - - if ( - resp.status !== 200 || - !resp.data.lnd_admin_macaroon || - !resp.data.lnd_rest_onion - ) { - toast.error(t("settings.connection_info_error")); - return; - } - - const { lnd_admin_macaroon, lnd_rest_onion } = resp.data; - - if (!window.alby) { - toast.error(t("settings.alby.connection.hint")); - } - - try { - await window.alby.enable(); - - const result = await window.alby.addAccount({ - name: "⚡️ Raspiblitz", - connector: "lnd", - config: { - adminkey: lnd_admin_macaroon, - url: lnd_rest_onion, - }, - }); - - if (result.success) { - toast.success(t("settings.alby.connection.success")); - } else { - toast.error(t("settings.alby.connection.error")); - } - } catch (e) { - toast.error(t("settings.alby.connection.error")); - } - }; - return (
@@ -124,26 +82,6 @@ const Settings: FC = () => { confirmEndpoint="/system/shutdown" /> )} - - , - ]} - /> - } - actionName={t("settings.alby.label")} - action={addAlbyAccountHandler} - />
); }; diff --git a/src/utils/availableApps.ts b/src/utils/availableApps.ts index 26e75f67..12adea17 100644 --- a/src/utils/availableApps.ts +++ b/src/utils/availableApps.ts @@ -74,4 +74,13 @@ export const availableApps: Map = new Map([ repository: "https://github.com/joinmarket-webui/jam", }, ], + [ + "alby", + { + id: "alby", + name: "Alby", + author: "Alby Team", + repository: "https://github.com/getAlby/lightning-browser-extension", + }, + ], ]);