feat: alby pro subscriptions (#1153)

* feat: draft

* fix: replace alert dialog

* fix: ordering of features

* feat: new components & helpers

* fix: make yearly plan the default

* fix: cleanup

* fix: update guides link

* fix: upgrade

* fix: add to home

* fix: upgrade prompts

* fix: remove support page

* fix: external link button

* fix: minify

* fix: conditionally show backup feature

* fix: onboarding & account linking

* fix: replace alby pro icon

* chore: use plan code instead of buzz, fix incorrect components briefly rendering

* fix: badge

* fix: change badge type

---------

Co-authored-by: Roland Bewick <roland.bewick@gmail.com>
This commit is contained in:
René Aaron 2025-03-17 11:23:07 +01:00 committed by GitHub
parent 7c93bff201
commit d3a3b615cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 428 additions and 177 deletions

View file

@ -76,8 +76,7 @@ type AlbyMeHub struct {
}
type AlbyMeSubscription struct {
// PlanCode string `json:"plan_code"`
Buzz bool `json:"buzz"`
PlanCode string `json:"plan_code"`
}
type AlbyMe struct {

View file

@ -0,0 +1,28 @@
import { SparklesIcon } from "lucide-react";
import { Button } from "src/components/ui/button";
import { UpgradeDialog } from "src/components/UpgradeDialog";
interface Props {
title: string;
description: string;
}
const UpgradeCard: React.FC<Props> = ({
title: message,
description: subMessage,
}) => {
return (
<div className="flex flex-1 items-center justify-center rounded-lg border border-dashed shadow-sm p-8">
<div className="flex flex-col items-center gap-1 text-center max-w-sm">
<SparklesIcon className="w-12 h-12" />
<h3 className="mt-4 text-lg font-semibold">{message}</h3>
<p className="text-sm text-muted-foreground mb-5">{subMessage}</p>
<UpgradeDialog>
<Button variant="premium">Upgrade</Button>
</UpgradeDialog>
</div>
</div>
);
};
export default UpgradeCard;

View file

@ -0,0 +1,124 @@
import {
LifeBuoy,
Mail,
RefreshCw,
SparklesIcon,
Users,
Zap,
} from "lucide-react";
import { ReactNode } from "react";
import { ExternalLinkButton } from "src/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "src/components/ui/dialog";
import { useAlbyMe } from "src/hooks/useAlbyMe";
import { useInfo } from "src/hooks/useInfo";
interface UpgradeDialogProps {
children: ReactNode;
}
export const UpgradeDialog = ({ children }: UpgradeDialogProps) => {
const { data: albyMe } = useAlbyMe();
const { data: info } = useInfo();
if (!info || (info.albyAccountConnected && !albyMe)) {
// still loading - make sure button does not incorrectly show
return;
}
if (albyMe?.subscription.plan_code) {
return null;
}
return (
<Dialog>
<DialogTrigger>{children}</DialogTrigger>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle className="flex flex-row gap-2 items-center">
<SparklesIcon className="w-6 h-6" />
Unlock Alby Pro
</DialogTitle>
<DialogDescription>
Take your Alby Hub experience to the next level
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-6 my-4">
<div className="space-y-5">
<h3 className="font-medium text-lg">Pro Features Include:</h3>
<ul className="space-y-3">
<li className="flex items-center">
<Users className="w-5 h-5 mr-3" />
<div>
<span className="font-medium">Unlimited Sub-wallets</span>
<p className="text-sm text-muted-foreground">
Share with friends, family, coworkers
</p>
</div>
</li>
{info?.backendType === "LDK" && (
<li className="flex items-center">
<RefreshCw className="w-5 h-5 mr-3" />
<div>
<span className="font-medium">
Encrypted Remote Backups
</span>
<p className="text-sm text-muted-foreground">
Secure wallet backups in the cloud
</p>
</div>
</li>
)}
<li className="flex items-center">
<LifeBuoy className="w-5 h-5 mr-3" />
<div>
<span className="font-medium">Priority Support</span>
<p className="text-sm text-muted-foreground">
Get help faster when you need it
</p>
</div>
</li>
<li className="flex items-center">
<Zap className="w-5 h-5 mr-3" />
<div>
<span className="font-medium">Custom Lightning Address</span>
<p className="text-sm text-muted-foreground">
Create your personalized address
</p>
</div>
</li>
<li className="flex items-center">
<Mail className="w-5 h-5 mr-3" />
<div>
<span className="font-medium">Email Notifications</span>
<p className="text-sm text-muted-foreground">
Stay updated on important activity
</p>
</div>
</li>
</ul>
</div>
</div>
<div className="flex-col gap-1">
<ExternalLinkButton
variant="premium"
size="lg"
className="w-full"
to="https://www.getalby.com/subscription/new"
>
Upgrade Now
</ExternalLinkButton>
<p className="text-xs text-center text-muted-foreground mt-2">
30 day money back guarantee Cancel anytime
</p>
</div>
</DialogContent>
</Dialog>
);
};

View file

@ -1,7 +1,6 @@
import { compare } from "compare-versions";
import {
CircleHelp,
Cloud,
EllipsisVertical,
Home,
LayoutGrid,
@ -12,6 +11,7 @@ import {
Settings,
ShieldAlertIcon,
ShieldCheckIcon,
SparklesIcon,
User2,
Wallet,
} from "lucide-react";
@ -46,6 +46,7 @@ import {
} from "src/components/ui/tooltip";
import { useAlbyMe } from "src/hooks/useAlbyMe";
import { UpgradeDialog } from "src/components/UpgradeDialog";
import { useAlbyInfo } from "src/hooks/useAlbyInfo";
import { useHealthCheck } from "src/hooks/useHealthCheck";
import { useInfo } from "src/hooks/useInfo";
@ -171,6 +172,7 @@ export default function AppLayout() {
<MenuItem
to="/"
onClick={(e) => {
openLink("https://support.getalby.com");
openLink("https://support.getalby.com");
e.preventDefault();
}}
@ -178,18 +180,12 @@ export default function AppLayout() {
<CircleHelp className="h-4 w-4" />
Help
</MenuItem>
{!albyMe?.hub.name && info?.albyAccountConnected && (
<MenuItem
to="/"
onClick={(e) => {
openLink("https://getalby.com/subscription/new");
e.preventDefault();
}}
>
<Cloud className="h-4 w-4" />
Alby Cloud
</MenuItem>
)}
<UpgradeDialog>
<div className="flex items-center gap-3 rounded-lg px-3 py-2 text-muted-foreground transition-all hover:text-accent-foreground">
<SparklesIcon className="h-4 w-4" />
Upgrade
</div>
</UpgradeDialog>
</nav>
);
}

View file

@ -23,6 +23,8 @@ const buttonVariants = cva(
link: "text-foreground hover:text-accent-foreground underline underline-offset-4 hover:no-underline",
positive:
"bg-positive text-positive-foreground shadow-sm hover:bg-positive/90",
premium:
"text-black shadow-md shadow-amber-500/20 bg-gradient-to-r from-amber-500 to-amber-300 hover:from-amber-400 hover:to-amber-200 transition-all duration-200 hover:shadow-lg hover:shadow-amber-500/30",
},
size: {
default: "h-9 px-4 py-2",

View file

@ -55,9 +55,12 @@ export const useOnboardingData = (): UseOnboardingDataResponse => {
const hasCustomApp =
apps && apps.find((x) => x.name !== "getalby.com") !== undefined;
const hasTransaction = transactions.totalCount > 0;
const hasSetupSupportPayment =
apps &&
apps.find((x) => x.name === SUPPORT_ALBY_CONNECTION_NAME) !== undefined;
const hasSetupSupportPayment = !!(
(apps &&
apps.find((x) => x.name === SUPPORT_ALBY_CONNECTION_NAME) !==
undefined) ||
albyMe?.subscription.plan_code
);
const checklistItems: Omit<ChecklistItem, "disabled">[] = [
...(hasChannelManagement
@ -110,9 +113,9 @@ export const useOnboardingData = (): UseOnboardingDataResponse => {
...(!info.oauthRedirect
? [
{
title: "Support Alby Hub",
title: "Support Alby",
description:
"Setup a recurring payment to support the development of Alby Hub",
"Set up a monthly contribution to help us build the future of digital payments.",
checked: hasSetupSupportPayment,
to: "/support-alby",
},

View file

@ -4,10 +4,13 @@ import {
Headphones,
LifeBuoy,
Mail,
SparklesIcon,
Zap,
} from "lucide-react";
import Container from "src/components/Container";
import ExternalLink from "src/components/ExternalLink";
import TwoColumnLayoutHeader from "src/components/TwoColumnLayoutHeader";
import { Badge } from "src/components/ui/badge";
import { LinkButton } from "src/components/ui/button";
import {
Card,
@ -26,40 +29,52 @@ export function ConnectAlbyAccount({ connectUrl }: ConnectAlbyAccountProps) {
<Container>
<TwoColumnLayoutHeader
title="Connect Your Alby Account"
description="Alby Account brings several benefits to your Alby Hub"
description="Your Alby Account brings several benefits to your Alby Hub"
/>
<div className="grid grid-cols-1 md:grid-cols-2 w-full gap-2 mt-5">
<Card className="w-full">
<Card className="w-full relative">
<CardHeader className="flex flex-col justify-center items-center text-center p-4">
<Zap className="w-6 h-6" />
<CardTitle className="text-sm">Lightning Address</CardTitle>
<CardTitle className="text-sm">
Lightning Address
<AlbyProIcon />
</CardTitle>
<CardDescription className="text-xs">
Personalized lightning address to receive payments
</CardDescription>
</CardHeader>
</Card>
<Card className="w-full">
<Card className="w-full relative">
<CardHeader className="flex flex-col justify-center items-center text-center p-4">
<Mail className="w-6 h-6" />
<CardTitle className="text-sm">Email Notifications</CardTitle>
<CardTitle className="text-sm">
Email Notifications
<AlbyProIcon />
</CardTitle>
<CardDescription className="text-xs">
Instant notifications about incoming transactions and more
</CardDescription>
</CardHeader>
</Card>
<Card className="w-full">
<Card className="w-full relative">
<CardHeader className="flex flex-col justify-center items-center text-center p-4">
<DatabaseBackup className="w-6 h-6" />
<CardTitle className="text-sm">Encrypted Backups</CardTitle>
<CardTitle className="text-sm">
Encrypted Backups
<AlbyProIcon />
</CardTitle>
<CardDescription className="text-xs">
Ensures you can always recover funds from lightning channels
</CardDescription>
</CardHeader>
</Card>
<Card className="w-full">
<Card className="w-full relative">
<CardHeader className="flex flex-col justify-center items-center text-center p-4">
<LifeBuoy className="w-6 h-6" />
<CardTitle className="text-sm">Support</CardTitle>
<CardTitle className="text-sm">
Support
<AlbyProIcon />
</CardTitle>
<CardDescription className="text-xs">
Human support via live chat when you need a helping hand
</CardDescription>
@ -84,15 +99,9 @@ export function ConnectAlbyAccount({ connectUrl }: ConnectAlbyAccountProps) {
</CardHeader>
</Card>
</div>
<div className="flex flex-col justify-center items-center text-center p-4 mt-4">
<CardTitle className="text-sm">and there's more...</CardTitle>
<CardDescription className="text-xs">
Claim your Nostr address, discover apps, etc
</CardDescription>
</div>
<div className="flex flex-col items-center justify-center gap-2">
<div className="flex flex-col items-center justify-center gap-2 mt-10">
<LinkButton to={connectUrl || "/alby/auth"} size="lg">
Connect now
Connect
</LinkButton>
<LinkButton
size="sm"
@ -103,7 +112,29 @@ export function ConnectAlbyAccount({ connectUrl }: ConnectAlbyAccountProps) {
Maybe later
</LinkButton>
</div>
<div className="text-muted-foreground flex flex-col items-center text-xs gap-2 mt-10">
<Badge title="Alby Pro">
<SparklesIcon className="w-3 h-3" />
</Badge>
<div>
Unlock additional features with{" "}
<ExternalLink
to="https://getalby.com/pricing"
className="underline"
>
Alby Pro
</ExternalLink>
</div>
</div>
</Container>
</div>
);
function AlbyProIcon() {
return (
<div className="absolute right-2 top-2" title="Alby Pro">
<SparklesIcon className="w-3 h-3" />
</div>
);
}
}

View file

@ -28,6 +28,7 @@ import { LightningMessageboardWidget } from "src/components/home/widgets/Lightni
import { NodeStatusWidget } from "src/components/home/widgets/NodeStatusWidget";
import { OnchainFeesWidget } from "src/components/home/widgets/OnchainFeesWidget";
import { WhatsNewWidget } from "src/components/home/widgets/WhatsNewWidget";
import { UpgradeDialog } from "src/components/UpgradeDialog";
function getGreeting(name: string | undefined) {
const hours = new Date().getHours();
@ -59,7 +60,15 @@ function Home() {
return (
<>
<AppHeader title={getGreeting(albyMe?.name)} description="" />
<AppHeader
title={getGreeting(albyMe?.name)}
description=""
contentRight={
<UpgradeDialog>
<Button variant="premium">Upgrade</Button>
</UpgradeDialog>
}
/>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5 items-start justify-start">
{/* LEFT */}
<div className="grid gap-5">

View file

@ -1,28 +1,31 @@
import { Code, PlusCircle, RefreshCw } from "lucide-react";
import { Code, PlusCircle, RefreshCw, SparklesIcon } from "lucide-react";
import React from "react";
import { useNavigate } from "react-router-dom";
import ExternalLink from "src/components/ExternalLink";
import {
AlertDialog,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "src/components/ui/alert-dialog";
import { Badge } from "src/components/ui/badge";
import { Button, LinkButton } from "src/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "src/components/ui/card";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "src/components/ui/dialog";
import { Input } from "src/components/ui/input";
import { Label } from "src/components/ui/label";
import { LoadingButton } from "src/components/ui/loading-button";
import { useToast } from "src/components/ui/use-toast";
import { UpgradeDialog } from "src/components/UpgradeDialog";
import {
SUPPORT_ALBY_CONNECTION_NAME,
SUPPORT_ALBY_LIGHTNING_ADDRESS,
@ -157,11 +160,11 @@ function SupportAlby() {
return (
<>
<div className="h-full w-full max-w-screen-sm mx-auto flex flex-col justify-center">
<div className="h-full w-full max-w-screen-md mx-auto flex flex-col justify-center">
<div className="flex flex-col items-center justify-center gap-6">
<section className="text-center">
<h2 className="text-3xl font-semibold mb-4 max-sm:mt-8">
Your Support Matters
Your Support Matters
</h2>
<p className="text-muted-foreground text-balance">
We are committed to elevating the Bitcoin ecosystem by offering
@ -176,13 +179,13 @@ function SupportAlby() {
<CardTitle>Why Your Contribution Is Important</CardTitle>
</CardHeader>
<CardContent>
<ul className="flex flex-col gap-5">
<ul className="flex flex-col gap-5 text-sm">
<li className="flex flex-col">
<div className="flex flex-row items-center">
<PlusCircle className="w-4 h-4 mr-2" />
Unlock New Features
</div>
<div className="text-muted-foreground text-sm">
<div className="text-muted-foreground text-xs">
Your support empowers us to design and implement
cutting-edge{" "}
<ExternalLink
@ -200,7 +203,7 @@ function SupportAlby() {
<RefreshCw className="w-4 h-4 mr-2" />
Ensure Continuous Improvement
</div>
<div className="text-muted-foreground text-sm">
<div className="text-muted-foreground text-xs">
With your contributions, we can provide{" "}
<ExternalLink
className="underline"
@ -217,7 +220,7 @@ function SupportAlby() {
<Code className="w-4 h-4 mr-2" />
Support Open-Source Freedom
</div>
<div className="text-muted-foreground text-sm">
<div className="text-muted-foreground text-xs">
Your support helps us keep Alby Hub true to the principles
of{" "}
<ExternalLink
@ -233,101 +236,131 @@ function SupportAlby() {
</ul>
</CardContent>
</Card>
<AlertDialog open={open} onOpenChange={setOpen}>
<div className="flex flex-col items-center justify-center gap-2">
<AlertDialogTrigger asChild>
<Button size="lg">Become a Supporter</Button>
</AlertDialogTrigger>
<LinkButton
size="sm"
variant="link"
to="/"
className="text-muted-foreground"
>
Maybe later
</LinkButton>
</div>
<AlertDialogContent>
<form onSubmit={handleSubmit}>
<AlertDialogHeader>
<AlertDialogTitle>Become a Supporter</AlertDialogTitle>
<AlertDialogDescription>
A new app connection will be established to facilitate
monthly payments to Alby. You can cancel it anytime through
the connections page.
</AlertDialogDescription>
</AlertDialogHeader>
<div className="flex flex-col gap-3 my-5">
<div className="grid grid-cols-4 gap-4">
<Label htmlFor="amount" className="text-right mt-2">
Amount <br></br>
<span className="font-normal text-muted-foreground">
(sats / month)
</span>
</Label>
<div className="col-span-3">
<Input
id="amount"
value={amount}
required
onChange={(e) => setAmount(e.target.value)}
/>
<div className="grid grid-cols-3 gap-1 mt-1">
<Button
type="button"
variant="outline"
onClick={() => setAmount("3000")}
>
🙏 3000
</Button>
<Button
type="button"
variant="outline"
onClick={() => setAmount("6000")}
>
💪 6000
</Button>
<Button
type="button"
variant="outline"
onClick={() => setAmount("10000")}
>
10000
</Button>
<div className="grid grid-cols-2 gap-3 w-full">
<Card className="w-full">
<CardHeader>
<CardTitle className="flex items-center justify-between">
<div className="flex gap-2">
<SparklesIcon className="h-4 w-4" />
<div>Alby Pro</div>
</div>
<Badge variant="outline">ACCOUNT REQUIRED</Badge>
</CardTitle>
<CardDescription>
Upgrade your Alby Account to Alby Pro, support Alby and enjoy
additional perks.
</CardDescription>
</CardHeader>
<CardFooter className="flex justify-center">
<UpgradeDialog>
<Button variant="premium">Unlock Alby Pro</Button>
</UpgradeDialog>
</CardFooter>
</Card>
<Card className="w-full">
<CardHeader>
<CardTitle>Become a Supporter</CardTitle>
<CardDescription>
Support Alby with recurring #value4value payments.
</CardDescription>
</CardHeader>
<CardFooter className="flex justify-center">
<Dialog open={open} onOpenChange={setOpen}>
<div className="flex flex-col items-center justify-center gap-2">
<DialogTrigger asChild>
<Button>Become a Supporter</Button>
</DialogTrigger>
</div>
<DialogContent>
<form onSubmit={handleSubmit}>
<DialogHeader>
<DialogTitle>Become a Supporter</DialogTitle>
<DialogDescription>
A new app connection will be established to facilitate
monthly payments to Alby. You can cancel it anytime
through the connections page.
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-3 my-5">
<div className="grid grid-cols-4 gap-4">
<Label htmlFor="amount" className="text-right mt-2">
Amount <br></br>
<span className="font-normal text-muted-foreground">
(sats / month)
</span>
</Label>
<div className="col-span-3">
<Input
id="amount"
value={amount}
required
onChange={(e) => setAmount(e.target.value)}
/>
<div className="grid grid-cols-3 gap-1 mt-1">
<Button
type="button"
variant="outline"
onClick={() => setAmount("3000")}
>
🙏 3000
</Button>
<Button
type="button"
variant="outline"
onClick={() => setAmount("6000")}
>
💪 6000
</Button>
<Button
type="button"
variant="outline"
onClick={() => setAmount("10000")}
>
10000
</Button>
</div>
</div>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="comment" className="text-right">
Name{" "}
<span className="font-normal text-muted-foreground">
(optional)
</span>
</Label>
<Input
id="sender-name"
value={senderName}
onChange={(e) => setSenderName(e.target.value)}
placeholder={`Nickname, npub, @twitter, etc.`}
className="col-span-3"
/>
</div>
</div>
</div>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="comment" className="text-right">
Name{" "}
<span className="font-normal text-muted-foreground">
(optional)
</span>
</Label>
<Input
id="sender-name"
value={senderName}
onChange={(e) => setSenderName(e.target.value)}
placeholder={`Nickname, npub, @twitter, etc.`}
className="col-span-3"
/>
</div>
</div>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<LoadingButton
type="submit"
disabled={!!isSubmitting}
loading={isSubmitting}
>
Complete Setup
</LoadingButton>
</AlertDialogFooter>
</form>
</AlertDialogContent>
</AlertDialog>
<DialogFooter>
<LoadingButton
type="submit"
disabled={!!isSubmitting}
loading={isSubmitting}
>
Complete Setup
</LoadingButton>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
</CardFooter>
</Card>
</div>
<LinkButton
size="sm"
variant="link"
to="/"
className="text-muted-foreground"
>
Maybe later
</LinkButton>
</div>
</div>
</>

View file

@ -4,6 +4,7 @@ import AppHeader from "src/components/AppHeader";
import AppCard from "src/components/connections/AppCard";
import ExternalLink from "src/components/ExternalLink";
import { IsolatedAppTopupDialog } from "src/components/IsolatedAppTopupDialog";
import Loading from "src/components/Loading";
import {
Accordion,
AccordionContent,
@ -17,8 +18,12 @@ import { Label } from "src/components/ui/label";
import { LoadingButton } from "src/components/ui/loading-button";
import { Textarea } from "src/components/ui/textarea";
import { useToast } from "src/components/ui/use-toast";
import UpgradeCard from "src/components/UpgradeCard";
import { UpgradeDialog } from "src/components/UpgradeDialog";
import { useAlbyMe } from "src/hooks/useAlbyMe";
import { useApp } from "src/hooks/useApp";
import { useApps } from "src/hooks/useApps";
import { useInfo } from "src/hooks/useInfo";
import { useNodeConnectionInfo } from "src/hooks/useNodeConnectionInfo";
import { copyToClipboard } from "src/lib/clipboard";
import { createApp } from "src/requests/createApp";
@ -35,6 +40,8 @@ export function UncleJim() {
const { data: nodeConnectionInfo } = useNodeConnectionInfo();
const { toast } = useToast();
const [isLoading, setLoading] = React.useState(false);
const { data: info } = useInfo();
const { data: albyMe } = useAlbyMe();
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
@ -79,42 +86,61 @@ export function UncleJim() {
(app) => app.metadata?.app_store_app_id === "uncle-jim"
);
const showForm =
albyMe?.subscription.plan_code ||
(onboardedApps && onboardedApps?.length < 3);
if (!info || (info.albyAccountConnected && !albyMe)) {
// make sure to not render the incorrect component
return <Loading />;
}
return (
<div className="grid gap-5">
<AppHeader
title="Friends & Family"
description="Create sub-wallets for your friends and family powered by your Hub"
contentRight={
<UpgradeDialog>
<Button variant="premium">Upgrade</Button>
</UpgradeDialog>
}
/>
{!connectionSecret && (
<>
<form
onSubmit={handleSubmit}
className="flex flex-col items-start gap-5 max-w-lg"
>
<div className="w-full grid gap-1.5">
<Label htmlFor="name">Name of friend or family member</Label>
<Input
autoFocus
type="text"
name="name"
value={name}
id="name"
onChange={(e) => setName(e.target.value)}
required
autoComplete="off"
{showForm ? (
<form
onSubmit={handleSubmit}
className="flex flex-col items-start gap-5 max-w-lg"
>
<div className="w-full grid gap-1.5">
<Label htmlFor="name">Name of friend or family member</Label>
<Input
autoFocus
type="text"
name="name"
value={name}
id="name"
onChange={(e) => setName(e.target.value)}
required
autoComplete="off"
/>
</div>
<LoadingButton loading={isLoading} type="submit">
Create Sub-wallet
</LoadingButton>
</form>
) : (
<>
<UpgradeCard
title="Need more Sub-wallets?"
description="Unlock unlimited sub-wallets by upgrading to Alby Pro"
/>
</div>
<LoadingButton loading={isLoading} type="submit">
Create Sub-wallet
</LoadingButton>
</form>
</>
)}
{!!onboardedApps?.length && (
<>
<p className="text-sm text-muted-foreground">
Great job! You've onboarded {onboardedApps.length} friends and
family members so far.
</p>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 items-stretch app-list">
{onboardedApps.map((app, index) => (
<AppCard key={index} app={app} />

View file

@ -102,10 +102,10 @@ export function AlbyAccount() {
)}
</p>
)}
{!me.subscription.buzz && (
{!me.subscription.plan_code && (
<p>VSS is only available to Alby users with a paid subscription.</p>
)}
{!info.ldkVssEnabled && me.subscription.buzz && (
{!info.ldkVssEnabled && me.subscription.plan_code && (
<AlertDialog>
<AlertDialogTrigger asChild>
{

View file

@ -59,7 +59,7 @@ export function SetupSecurity() {
</div>
<span className="text-sm text-muted-foreground">
Access to your Alby Hub is protected by an unlock password you
set.
set. It cannot be recovered or reset.
</span>
</div>
{store.nodeInfo.backendType === "LND" ||

View file

@ -387,7 +387,7 @@ export type AlbyMe = {
name?: string;
};
subscription: {
buzz: boolean;
plan_code: string;
};
};

View file

@ -413,7 +413,7 @@ func (svc *service) requestVssToken(ctx context.Context) (string, error) {
return "", err
}
// only activate VSS for Alby paid subscribers
if me.Subscription.Buzz {
if me.Subscription.PlanCode != "" {
svc.cfg.SetUpdate("LdkVssEnabled", "true", "")
}
}