mirror of
https://github.com/getAlby/hub.git
synced 2026-08-13 12:33:39 +02:00
feat: alby cloud enable vss banner (#1450)
* feat: alby cloud enable vss banner * fix: reload info after initiating storage migration
This commit is contained in:
parent
064de377a0
commit
2342b742a4
6 changed files with 106 additions and 42 deletions
67
frontend/src/components/Banner.tsx
Normal file
67
frontend/src/components/Banner.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { XIcon } from "lucide-react";
|
||||
import { Link } from "react-router-dom";
|
||||
import ExternalLink from "src/components/ExternalLink";
|
||||
|
||||
import { useAlbyInfo } from "src/hooks/useAlbyInfo";
|
||||
import { useAlbyMe } from "src/hooks/useAlbyMe";
|
||||
import { useInfo } from "src/hooks/useInfo";
|
||||
|
||||
export function Banner({ onDismiss }: { onDismiss: () => void }) {
|
||||
const { data: info } = useInfo();
|
||||
const { data: albyInfo } = useAlbyInfo();
|
||||
const { data: albyMe } = useAlbyMe();
|
||||
|
||||
if (!info || !albyInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// vss migration (alby cloud only)
|
||||
// TODO: remove after 2026-01-01
|
||||
const vssMigrationRequired =
|
||||
info.oauthRedirect &&
|
||||
!!albyMe?.subscription.plan_code.includes("buzz") &&
|
||||
info.vssSupported &&
|
||||
!info.ldkVssEnabled;
|
||||
|
||||
if (vssMigrationRequired) {
|
||||
return (
|
||||
<div className="fixed w-full bg-foreground text-background z-20 py-2 text-sm flex items-center justify-center">
|
||||
<Link
|
||||
to={`/settings/backup?dynamic=true`}
|
||||
className="w-full px-12 md:px-24"
|
||||
>
|
||||
<p className="line-clamp-2 md:block whitespace-normal md:whitespace-nowrap overflow-hidden text-ellipsis text-center">
|
||||
<span className="font-semibold mr-2">
|
||||
Enable Dynamic Channels Backup
|
||||
</span>
|
||||
<span>•</span>
|
||||
<span className="ml-2">Upgrade your channel backups now</span>
|
||||
</p>
|
||||
</Link>
|
||||
<XIcon
|
||||
className="absolute right-4 cursor-pointer w-4 text-background"
|
||||
onClick={onDismiss}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed w-full bg-foreground text-background z-20 py-2 text-sm flex items-center justify-center">
|
||||
<ExternalLink
|
||||
to={`https://getalby.com/update/hub?version=${info?.version}`}
|
||||
className="w-full px-12 md:px-24"
|
||||
>
|
||||
<p className="line-clamp-2 md:block whitespace-normal md:whitespace-nowrap overflow-hidden text-ellipsis text-center">
|
||||
<span className="font-semibold mr-2">Update Available</span>
|
||||
<span>•</span>
|
||||
<span className="ml-2">{albyInfo.hub.latestReleaseNotes}</span>
|
||||
</p>
|
||||
</ExternalLink>
|
||||
<XIcon
|
||||
className="absolute right-4 cursor-pointer w-4 text-background"
|
||||
onClick={onDismiss}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
import { XIcon } from "lucide-react";
|
||||
import ExternalLink from "src/components/ExternalLink";
|
||||
|
||||
import { useAlbyInfo } from "src/hooks/useAlbyInfo";
|
||||
import { useInfo } from "src/hooks/useInfo";
|
||||
|
||||
export function UpdateBanner({ onDismiss }: { onDismiss: () => void }) {
|
||||
const { data: info } = useInfo();
|
||||
const { data: albyInfo } = useAlbyInfo();
|
||||
|
||||
if (!info || !albyInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed w-full bg-foreground text-background z-20 py-2 text-sm flex items-center justify-center">
|
||||
<ExternalLink
|
||||
to={`https://getalby.com/update/hub?version=${info?.version}`}
|
||||
className="w-full px-12 md:px-24"
|
||||
>
|
||||
<p className="line-clamp-2 md:block whitespace-normal md:whitespace-nowrap overflow-hidden text-ellipsis text-center">
|
||||
<span className="font-semibold mr-2">Update Available</span>
|
||||
<span>•</span>
|
||||
<span className="ml-2">{albyInfo.hub.latestReleaseNotes}</span>
|
||||
</p>
|
||||
</ExternalLink>
|
||||
<XIcon
|
||||
className="absolute right-4 cursor-pointer w-4 text-background"
|
||||
onClick={onDismiss}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { Outlet } from "react-router-dom";
|
||||
|
||||
import { AppSidebar } from "src/components/AppSidebar";
|
||||
import { Banner } from "src/components/Banner";
|
||||
import { SidebarInset, SidebarProvider } from "src/components/ui/sidebar";
|
||||
import { UpdateBanner } from "src/components/UpdateBanner";
|
||||
import { useBanner } from "src/hooks/useBanner";
|
||||
import { useInfo } from "src/hooks/useInfo";
|
||||
import { useNotifyReceivedPayments } from "src/hooks/useNotifyReceivedPayments";
|
||||
|
|
@ -31,7 +31,7 @@ export default function AppLayout() {
|
|||
)}
|
||||
>
|
||||
<SidebarProvider className="flex flex-col">
|
||||
{showBanner && <UpdateBanner onDismiss={dismissBanner} />}
|
||||
{showBanner && <Banner onDismiss={dismissBanner} />}
|
||||
<div className="flex flex-1">
|
||||
<AppSidebar />
|
||||
<SidebarInset>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { compare } from "compare-versions";
|
||||
import React from "react";
|
||||
import { useAlbyInfo } from "src/hooks/useAlbyInfo";
|
||||
import { useAlbyMe } from "src/hooks/useAlbyMe";
|
||||
import { useInfo } from "src/hooks/useInfo";
|
||||
|
||||
export function useBanner() {
|
||||
const { data: info } = useInfo();
|
||||
const { data: albyInfo } = useAlbyInfo();
|
||||
const { data: albyMe } = useAlbyMe();
|
||||
const [showBanner, setShowBanner] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
|
@ -14,13 +16,21 @@ export function useBanner() {
|
|||
return;
|
||||
}
|
||||
|
||||
// vss migration (alby cloud only)
|
||||
// TODO: remove after 2026-01-01
|
||||
const vssMigrationRequired =
|
||||
info.oauthRedirect &&
|
||||
!!albyMe?.subscription.plan_code.includes("buzz") &&
|
||||
info.vssSupported &&
|
||||
!info.ldkVssEnabled;
|
||||
|
||||
const upToDate =
|
||||
Boolean(info.version) &&
|
||||
info.version.startsWith("v") &&
|
||||
compare(info.version.substring(1), albyInfo.hub.latestVersion, ">=");
|
||||
|
||||
setShowBanner(!upToDate);
|
||||
}, [info, albyInfo]);
|
||||
setShowBanner(!upToDate || vssMigrationRequired);
|
||||
}, [info, albyInfo, albyMe?.subscription.plan_code]);
|
||||
|
||||
const dismissBanner = () => {
|
||||
setShowBanner(false);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import React from "react";
|
||||
import { useToast } from "src/components/ui/use-toast";
|
||||
import { useInfo } from "src/hooks/useInfo";
|
||||
import { request } from "src/utils/request";
|
||||
|
||||
export function useMigrateLDKStorage() {
|
||||
const [isMigratingStorage, setMigratingStorage] = React.useState(false);
|
||||
const { toast } = useToast();
|
||||
const { mutate: reloadInfo } = useInfo();
|
||||
|
||||
const migrateLDKStorage = async (to: "VSS") => {
|
||||
try {
|
||||
|
|
@ -19,6 +21,7 @@ export function useMigrateLDKStorage() {
|
|||
to,
|
||||
}),
|
||||
});
|
||||
await reloadInfo();
|
||||
toast({
|
||||
title: "Please unlock your hub",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
} from "lucide-react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import ExternalLink from "src/components/ExternalLink";
|
||||
import Loading from "src/components/Loading";
|
||||
import MnemonicDialog from "src/components/mnemonic/MnemonicDialog";
|
||||
|
|
@ -201,7 +201,7 @@ export default function Backup() {
|
|||
{me?.subscription.plan_code && info.ldkVssEnabled ? (
|
||||
<Badge variant={"positive"}>Active</Badge>
|
||||
) : (
|
||||
<Badge>Alby Cloud</Badge>
|
||||
<Badge className="shrink-0">Alby Cloud</Badge>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
|
|
@ -284,10 +284,19 @@ type Props = {
|
|||
};
|
||||
|
||||
function DynamicChannelsBackupDialog({ info }: Props) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (searchParams.get("dynamic") === "true") {
|
||||
setOpen(true);
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
const { isMigratingStorage, migrateLDKStorage } = useMigrateLDKStorage();
|
||||
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialog open={open} onOpenChange={setOpen}>
|
||||
<AlertDialogTrigger asChild>
|
||||
<LoadingButton
|
||||
variant="secondary"
|
||||
|
|
@ -304,8 +313,16 @@ function DynamicChannelsBackupDialog({ info }: Props) {
|
|||
<AlertDialogDescription>
|
||||
<div>
|
||||
<p>
|
||||
As part of enabling VSS your hub will be shut down, and you will
|
||||
need to enter your unlock password to start it again.
|
||||
By enabling dynamic channel backups, your channels state is
|
||||
dynamically updated and stored end-to-end encrypted by Alby's
|
||||
Versioned Storage Service. This allows you to recover your
|
||||
spending balance with your recovery phrase alone, without having
|
||||
to close your channels.
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
As part of enabling dynamic channels backup your hub will be
|
||||
shut down, and you will need to enter your unlock password to
|
||||
start it again.
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
Please ensure you have no pending payments or channel closures
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue