feat: add first channel step to onboarding

This commit is contained in:
saunter 2026-05-19 18:27:06 +02:00
parent ff27c7019e
commit 53d0b68800
7 changed files with 82 additions and 12 deletions

View file

@ -4,6 +4,7 @@ import { Outlet, useLocation, useNavigate } from "react-router";
import { AlbyHubLogo } from "src/components/icons/AlbyHubLogo";
import { Button } from "src/components/ui/button.tsx";
import { useInfo } from "src/hooks/useInfo";
import { cn } from "src/lib/utils";
import AntonopoulosSVG from "public/images/quotes/antonopoulos.svg";
import BackSVG from "public/images/quotes/back.svg";
@ -65,6 +66,7 @@ export default function TwoColumnFullScreenLayout() {
const { data: info } = useInfo();
const navigate = useNavigate();
const { pathname } = useLocation();
const anchorFirstChannelExpansion = pathname === "/setup/first-channel";
const [quote, setQuote] = useState(
// eslint-disable-next-line react-hooks/purity
quotes[Math.floor(Math.random() * quotes.length)]
@ -100,9 +102,15 @@ export default function TwoColumnFullScreenLayout() {
</div>
</div>
</div>
<div className="flex items-center justify-center py-12 text-foreground relative">
<div
className={cn(
"flex items-center justify-center py-12 text-foreground relative",
anchorFirstChannelExpansion && "overflow-y-auto"
)}
>
{pathname.startsWith("/setup") &&
!pathname.startsWith("/setup/finish") && (
!pathname.startsWith("/setup/finish") &&
!pathname.startsWith("/setup/first-channel") && (
// show the back button on setup pages, except the setup finish page
<Button
type="button"
@ -116,7 +124,13 @@ export default function TwoColumnFullScreenLayout() {
Back
</Button>
)}
<Outlet />
{anchorFirstChannelExpansion ? (
<div className="flex w-full justify-center lg:h-[35rem] lg:items-start">
<Outlet />
</div>
) : (
<Outlet />
)}
</div>
</div>
);

View file

@ -13,6 +13,13 @@ export function SetupRedirect() {
return;
}
if (info.setupCompleted && info.running) {
if (location.pathname.startsWith("/setup/first-channel")) {
return;
}
if (location.pathname.startsWith("/setup/finish")) {
navigate("/setup/first-channel", { replace: true });
return;
}
navigate("/");
return;
}

View file

@ -45,8 +45,12 @@ export const useOnboardingData = (): UseOnboardingDataResponse => {
!!albyMe &&
nodeConnectionInfo &&
albyMe?.keysend_pubkey === nodeConnectionInfo?.pubkey;
const hasChannel =
!hasChannelManagement || (hasChannelManagement && channels.length > 0);
const hasOpenedChannel =
!hasChannelManagement ||
channels.some(
(channel) =>
channel.active || ["online", "offline"].includes(channel.status)
);
const hasBackedUp =
hasMnemonic === true &&
info &&
@ -64,7 +68,7 @@ export const useOnboardingData = (): UseOnboardingDataResponse => {
title: "Open your first channel",
description:
"Establish a new Lightning channel to enable fast and low-fee Bitcoin transactions.",
checked: hasChannel,
checked: hasOpenedChannel,
to: "/channels/first",
},
]

View file

@ -575,6 +575,31 @@ const routes: RouteObject[] = [
path: "finish",
element: <SetupFinish />,
},
{
path: "first-channel",
handle: { crumb: () => "Your First Channel" },
children: [
{
index: true,
element: <FirstChannel variant="setup" />,
},
{
path: "opening",
element: (
<OpeningFirstChannel openedPath="/setup/first-channel/opened" />
),
},
{
path: "opened",
element: (
<OpenedFirstChannel
ctaLabel="Go to dashboard"
ctaTo="/home"
/>
),
},
],
},
],
},
],

View file

@ -2,7 +2,15 @@ import TickSVG from "public/images/illustrations/tick.svg";
import TwoColumnLayoutHeader from "src/components/TwoColumnLayoutHeader";
import { LinkButton } from "src/components/ui/custom/link-button";
export function OpenedFirstChannel() {
type OpenedFirstChannelProps = {
ctaLabel?: string;
ctaTo?: string;
};
export function OpenedFirstChannel({
ctaLabel = "Receive Your First Payment",
ctaTo = "/wallet/receive",
}: OpenedFirstChannelProps) {
return (
<div className="flex flex-col items-center justify-center gap-10 p-5 w-full max-w-md">
<TwoColumnLayoutHeader
@ -13,8 +21,8 @@ export function OpenedFirstChannel() {
<img src={TickSVG} className="w-48" />
<LinkButton to="/wallet/receive" className="flex w-full justify-center">
Receive Your First Payment
<LinkButton to={ctaTo} className="flex w-full justify-center">
{ctaLabel}
</LinkButton>
</div>
);

View file

@ -4,7 +4,13 @@ import { ChannelWaitingForConfirmations } from "src/components/channels/ChannelW
import { useChannels } from "src/hooks/useChannels";
import { useSyncWallet } from "src/hooks/useSyncWallet";
export function OpeningFirstChannel() {
type OpeningFirstChannelProps = {
openedPath?: string;
};
export function OpeningFirstChannel({
openedPath = "/channels/first/opened",
}: OpeningFirstChannelProps) {
useSyncWallet();
const { data: channels } = useChannels(true);
const navigate = useNavigate();
@ -13,9 +19,9 @@ export function OpeningFirstChannel() {
React.useEffect(() => {
if (firstChannel?.active) {
navigate("/channels/first/opened");
navigate(openedPath);
}
}, [firstChannel, navigate]);
}, [firstChannel, navigate, openedPath]);
return <ChannelWaitingForConfirmations channel={firstChannel} />;
}

View file

@ -57,6 +57,12 @@ export function SetupFinish() {
};
}, [loading]);
useEffect(() => {
if (info?.setupCompleted && info.running) {
navigate("/setup/first-channel", { replace: true });
}
}, [info?.setupCompleted, info?.running, navigate]);
useEffect(() => {
if (!info) {
return;