Skip to content

Commit

Permalink
feat(alby): add to apps section #632
Browse files Browse the repository at this point in the history
  • Loading branch information
escapedcat committed Jul 16, 2023
1 parent e1a4a09 commit 6906bff
Show file tree
Hide file tree
Showing 11 changed files with 330 additions and 84 deletions.
Binary file added public/assets/apps/logos/alby.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/apps/preview/alby/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/apps/preview/alby/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/apps/preview/alby/3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 14 additions & 10 deletions src/i18n/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -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</0>",
"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",
Expand Down
4 changes: 4 additions & 0 deletions src/i18n/langs/en_appInfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -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..."
}
}
}
Expand Down
109 changes: 109 additions & 0 deletions src/pages/Apps/AppCardAlby.tsx
Original file line number Diff line number Diff line change
@@ -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<Props> = ({ 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 (
<div className="bd-card transition-colors dark:bg-gray-800">
<div className="relative mt-2 flex h-4/6 w-full flex-row items-center">
{/* Icon */}
<div className="mt-4 flex w-1/4 items-center justify-center p-2">
<AppIcon appId={id} className="max-h-12" />
</div>
{/* Content */}
<div className="mt-4 flex w-3/4 flex-col items-start justify-center text-xl">
<h4>{name}</h4>
<p className="overflow-ellipsis text-base text-gray-500 dark:text-gray-200">
{t(`appInfo.${id}.shortDescription`)}
</p>
</div>
</div>
<div className="flex flex-row gap-2 py-4">
{window.alby && (
<button
className="bd-button flex w-1/2 items-center justify-center p-2 disabled:pointer-events-none"
onClick={addAlbyAccountHandler}
>
<PlusIcon className="inline h-6 w-6" />
&nbsp;{t(`appInfo.${id}.action.addAccount`)}
</button>
)}

{!window.alby && (
<a
className="bd-button flex w-1/2 items-center justify-center p-2 disabled:pointer-events-none"
target="_blank"
rel="noreferrer"
href="https://getalby.com"
>
{t(`appInfo.${id}.action.install`)}
</a>
)}

<button
className="flex w-1/2 items-center justify-center rounded p-2 shadow-md hover:bg-gray-300 dark:bg-gray-500 dark:hover:bg-gray-300 dark:hover:text-black"
onClick={() => onOpenDetails(appInfo)}
>
<InformationCircleIcon className="inline h-6 w-6" />
&nbsp;{t("apps.info")}
</button>
</div>
</div>
);
};

export default AppCard;
168 changes: 168 additions & 0 deletions src/pages/Apps/AppInfoAlby.tsx
Original file line number Diff line number Diff line change
@@ -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<Props> = ({ app, onClose }) => {
const { t } = useTranslation();
const [isLoading, setIsLoading] = useState(true);
const [imgs, setImgs] = useState<string[]>([]);
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 (
<main className="page-container content-container flex w-full items-center justify-center bg-gray-100 dark:bg-gray-700 dark:text-white">
<LoadingSpinner />
</main>
);
}

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 (
<main className="page-container content-container w-full bg-gray-100 dark:bg-gray-700 dark:text-white">
{/* Back Button */}
<section className="w-full px-5 py-9 dark:text-gray-200">
<button
onClick={onClose}
className="flex items-center text-xl font-bold outline-none"
>
<ChevronLeftIcon className="inline-block h-5 w-5" />
{t("navigation.back")}
</button>
</section>

{/* Image box with title */}
<section className="mb-5 flex w-full flex-wrap items-center justify-center">
<AppIcon appId={id} className="max-h-12" />
<h1 className="px-5 text-2xl dark:text-white">{name}</h1>
{window.alby && (
<button
className="bd-button flex rounded p-2 disabled:pointer-events-none"
onClick={addAlbyAccountHandler}
>
<PlusIcon className="inline h-6 w-6" />
&nbsp;{t(`appInfo.${id}.action.addAccount`)}
</button>
)}

{!window.alby && (
<a
className="bd-button flex rounded p-2 disabled:pointer-events-none"
target="_blank"
rel="noreferrer"
href="https://getalby.com"
>
{t(`appInfo.${id}.action.install`)}
</a>
)}
</section>

<section className="text-center">
{!isLoading && <ImageCarousel imgs={imgs} />}
</section>

{/* App Description */}
<section className="flex w-full items-center justify-center p-5">
<article className="bd-card w-full">
<h3 className="text-lg">{name}</h3>
<h4 className="my-2 text-gray-500 dark:text-gray-300">
{t("apps.about")}
</h4>
<p>{t(`appInfo.${id}.about`)}</p>
<h4 className="my-2 text-gray-500 dark:text-gray-300">
{t("apps.author")}
</h4>
<p>{author}</p>
<h4 className="my-2 text-gray-500 dark:text-gray-300">
{t("apps.source")}
</h4>
<a
href={repository}
className="text-blue-400 underline dark:text-blue-300"
>
{repository}
</a>
</article>
</section>
</main>
);
};

export default AppInfo;
36 changes: 25 additions & 11 deletions src/pages/Apps/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"]);
Expand Down Expand Up @@ -59,17 +61,22 @@ export const Apps: FC = () => {
};

if (showDetails && app) {
const appInfos = appStatus.find((a) => a.id === app.id)!;
return (
<AppInfo
app={app}
appStatusInfo={appInfos}
installingApp={installingApp}
onInstall={() => installHandler(app.id)}
onUninstall={() => uninstallHandler(app.id)}
onClose={closeDetailsHandler}
/>
);
let appInfos;
if (app.id === "alby") {
return <AppInfoAlby app={app} onClose={closeDetailsHandler} />;
} else {
appInfos = appStatus.find((a) => a.id === app.id)!;
return (
<AppInfo
app={app}
appStatusInfo={appInfos}
installingApp={installingApp}
onInstall={() => installHandler(app.id)}
onUninstall={() => uninstallHandler(app.id)}
onClose={closeDetailsHandler}
/>
);
}
}

return (
Expand Down Expand Up @@ -121,6 +128,13 @@ export const Apps: FC = () => {
</article>
);
})}

<article>
<AppCardAlby
appInfo={availableApps.get("alby")!}
onOpenDetails={openDetailsHandler}
/>
</article>
</div>
</section>
</>
Expand Down
Loading

0 comments on commit 6906bff

Please sign in to comment.