>
+
+ >
);
}
diff --git a/frontend/src/components/Breadcrumb.tsx b/frontend/src/components/Breadcrumb.tsx
deleted file mode 100644
index 84c723c8..00000000
--- a/frontend/src/components/Breadcrumb.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-
-import { Link, useMatches } from "react-router-dom";
-import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbSeparator } from "src/components/ui/breadcrumb";
-
-
-function Breadcrumbs() {
- const matches = useMatches();
- const crumbs = matches
- // Skip the root item
- //.slice(1)
- // first get rid of any matches that don't have handle and crumb
- .filter((match) => Boolean(match.handle?.crumb))
- // now map them into an array of elements, passing the loader
- // data to each one
- //.map((match) => match.handle.crumb(match.data));
-
- //console.log(matches, crumbs);
-
- if (crumbs.length <= 1) {
- return <>no breadcrumb>;
- }
-
- const isIndexRoute = crumbs.length >= 2 && crumbs[crumbs.length - 1].pathname ? crumbs[crumbs.length - 1].pathname.slice(0, -1) == crumbs[crumbs.length - 2].pathname : false;
-
- // Remove the last item if it's an index route to prevent e.g. Wallet > Wallet
- const filteredCrumbs = isIndexRoute ? crumbs.slice(0, -1) : crumbs;
-
- //console.log("filtered", filteredCrumbs);
-
- return (
- <>
-
-
- {filteredCrumbs.map((crumb, index) => (
- <>
-
- {index + 1 < filteredCrumbs.length ?
-
- {crumb.handle.crumb()}
-
- :
- <>{crumb.handle.crumb()}>
- }
-
- {index + 1 < filteredCrumbs.length && }
- >
- ))}
-
-
-
- >
- );
-}
-
-export default Breadcrumbs;
diff --git a/frontend/src/components/Breadcrumbs.tsx b/frontend/src/components/Breadcrumbs.tsx
new file mode 100644
index 00000000..99df860f
--- /dev/null
+++ b/frontend/src/components/Breadcrumbs.tsx
@@ -0,0 +1,57 @@
+import { Link, useMatches } from "react-router-dom";
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbSeparator,
+} from "src/components/ui/breadcrumb";
+
+function Breadcrumbs() {
+ const matches = useMatches();
+ const crumbs = matches
+ // Skip the root item
+ .slice(1)
+ // first get rid of any matches that don't have handle and crumb
+ .filter((match) => Boolean(match.handle?.crumb));
+
+ // Compare pathnames of index routes to remove duplicates
+ const isIndexRoute =
+ crumbs.length >= 2 && crumbs[crumbs.length - 1].pathname
+ ? crumbs[crumbs.length - 1].pathname.slice(0, -1) ==
+ crumbs[crumbs.length - 2].pathname
+ : false;
+
+ // Remove the last item if it's an index route to prevent e.g. Wallet > Wallet
+ const filteredCrumbs = isIndexRoute ? crumbs.slice(0, -1) : crumbs;
+
+ // Don't render anything if there is only one item
+ if (filteredCrumbs.length < 2) {
+ return;
+ }
+
+ return (
+ <>
+
+
+ {filteredCrumbs.map((crumb, index) => (
+ <>
+
+ {index + 1 < filteredCrumbs.length ? (
+
+ {crumb.handle.crumb()}
+
+ ) : (
+ <>{crumb.handle.crumb()}>
+ )}
+
+ {index + 1 < filteredCrumbs.length && }
+ >
+ ))}
+
+
+ >
+ );
+}
+
+export default Breadcrumbs;
diff --git a/frontend/src/screens/apps/AppCreated.tsx b/frontend/src/screens/apps/AppCreated.tsx
new file mode 100644
index 00000000..79f0ec8f
--- /dev/null
+++ b/frontend/src/screens/apps/AppCreated.tsx
@@ -0,0 +1,147 @@
+import { CopyIcon } from "lucide-react";
+import { useEffect, useState } from "react";
+import { Link, Navigate, useLocation, useNavigate } from "react-router-dom";
+
+import AppHeader from "src/components/AppHeader";
+import ExternalLink from "src/components/ExternalLink";
+import Loading from "src/components/Loading";
+import QRCode from "src/components/QRCode";
+import { suggestedApps } from "src/components/SuggestedAppData";
+import { Button } from "src/components/ui/button";
+import {
+ Card,
+ CardContent,
+ CardHeader,
+ CardTitle,
+} from "src/components/ui/card";
+import { useToast } from "src/components/ui/use-toast";
+import { useApp } from "src/hooks/useApp";
+import { copyToClipboard } from "src/lib/clipboard";
+import { CreateAppResponse } from "src/types";
+
+export default function AppCreated() {
+ const { search, state } = useLocation();
+ const navigate = useNavigate();
+ const { toast } = useToast();
+
+ const queryParams = new URLSearchParams(search);
+ const appId = queryParams.get("app") ?? "";
+ const appstoreApp = suggestedApps.find((app) => app.id === appId);
+ console.info(appstoreApp, appId);
+
+ const [timeout, setTimeout] = useState(false);
+ const createAppResponse = state as CreateAppResponse;
+ const pairingUri = createAppResponse.pairingUri;
+ const { data: app } = useApp(createAppResponse.pairingPublicKey, true);
+
+ const copy = () => {
+ copyToClipboard(pairingUri);
+ toast({ title: "Copied to clipboard." });
+ };
+
+ useEffect(() => {
+ const timeoutId = window.setTimeout(() => {
+ setTimeout(true);
+ }, 10000);
+
+ return () => window.clearTimeout(timeoutId);
+ }, []);
+
+ useEffect(() => {
+ if (app?.lastEventAt) {
+ toast({
+ title: "Connection established!",
+ description: "You can now use the app with your Alby Hub.",
+ });
+ navigate("/apps");
+ }
+ }, [app?.lastEventAt, navigate, toast]);
+
+ useEffect(() => {
+ if (appstoreApp) {
+ return;
+ }
+ // dispatch a success event which can be listened to by the opener or by the app that embedded the webview
+ // this gives those apps the chance to know the user has enabled the connection
+ const nwcEvent = new CustomEvent("nwc:success", { detail: {} });
+ window.dispatchEvent(nwcEvent);
+
+ // notify the opener of the successful connection
+ if (window.opener) {
+ window.opener.postMessage(
+ {
+ type: "nwc:success",
+ payload: { success: true },
+ },
+ "*"
+ );
+ }
+ }, [appstoreApp]);
+
+ if (!createAppResponse) {
+ return ;
+ }
+
+ return (
+ <>
+
+
+
+
+ 1. Open{" "}
+ {appstoreApp ? (
+
+ {appstoreApp.title}
+
+ ) : (
+ "the app you wish to connect"
+ )}{" "}
+ and look for a way to attach a wallet (most apps provide this option
+ in settings)
+