From 5974271aeb700ae3f206ce422e7511d7acf292f6 Mon Sep 17 00:00:00 2001 From: pavanjoshi914 Date: Fri, 27 Dec 2024 18:59:50 +0530 Subject: [PATCH 01/14] feat: update settings pages --- frontend/src/components/SidebarHint.tsx | 33 ++++++- .../src/components/layouts/SettingsLayout.tsx | 85 ++----------------- frontend/src/routes.tsx | 5 ++ frontend/src/screens/BackupMnemonic.tsx | 54 +++++++----- .../screens/settings/ChangeUnlockPassword.tsx | 28 ++++-- frontend/src/screens/settings/Shutdown.tsx | 80 +++++++++++++++++ 6 files changed, 174 insertions(+), 111 deletions(-) create mode 100644 frontend/src/screens/settings/Shutdown.tsx diff --git a/frontend/src/components/SidebarHint.tsx b/frontend/src/components/SidebarHint.tsx index 059ec548b..8eaf29658 100644 --- a/frontend/src/components/SidebarHint.tsx +++ b/frontend/src/components/SidebarHint.tsx @@ -1,3 +1,6 @@ +import { UpdateIcon } from "@radix-ui/react-icons"; +import { IconProps } from "@radix-ui/react-icons/dist/types"; +import { compare } from "compare-versions"; import { ListTodo, LucideIcon, Zap } from "lucide-react"; import { ReactElement } from "react"; import { Link, useLocation } from "react-router-dom"; @@ -9,6 +12,8 @@ import { CardTitle, } from "src/components/ui/card"; import { Progress } from "src/components/ui/progress"; +import { useAlbyInfo } from "src/hooks/useAlbyInfo"; +import { useInfo } from "src/hooks/useInfo"; import { useOnboardingData } from "src/hooks/useOnboardingData"; import useChannelOrderStore from "src/state/ChannelOrderStore"; @@ -16,6 +21,8 @@ function SidebarHint() { const { isLoading, checklistItems } = useOnboardingData(); const { order } = useChannelOrderStore(); const location = useLocation(); + const { data: albyInfo } = useAlbyInfo(); + const { data: info } = useInfo(); // User has a channel order if ( @@ -67,14 +74,36 @@ function SidebarHint() { /> ); } -} + if (info && albyInfo) { + const upToDate = + info.version && + info.version.startsWith("v") && + compare(info.version.substring(1), albyInfo.hub.latestVersion, ">="); + + if (!upToDate) { + return ( + + ); + } + } +} type SidebarHintCardProps = { title: string; description: string | ReactElement; buttonText: string; buttonLink: string; - icon: LucideIcon; + icon: + | LucideIcon + | React.ForwardRefExoticComponent< + IconProps & React.RefAttributes + >; }; function SidebarHintCard({ title, diff --git a/frontend/src/components/layouts/SettingsLayout.tsx b/frontend/src/components/layouts/SettingsLayout.tsx index 4d69eb17b..7b5041a60 100644 --- a/frontend/src/components/layouts/SettingsLayout.tsx +++ b/frontend/src/components/layouts/SettingsLayout.tsx @@ -1,60 +1,14 @@ -import { Power } from "lucide-react"; -import React, { useState } from "react"; -import { NavLink, Outlet, useNavigate } from "react-router-dom"; +import React from "react"; +import { NavLink, Outlet } from "react-router-dom"; import AppHeader from "src/components/AppHeader"; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "src/components/ui/alert-dialog"; import { buttonVariants } from "src/components/ui/button"; -import { LoadingButton } from "src/components/ui/loading-button"; -import { useToast } from "src/components/ui/use-toast"; import { useInfo } from "src/hooks/useInfo"; import { cn } from "src/lib/utils"; -import { request } from "src/utils/request"; export default function SettingsLayout() { - const { - data: info, - mutate: refetchInfo, - hasMnemonic, - hasNodeBackup, - } = useInfo(); - const navigate = useNavigate(); - const { toast } = useToast(); - const [shuttingDown, setShuttingDown] = useState(false); - - const shutdown = React.useCallback(async () => { - setShuttingDown(true); - try { - await request("/api/stop", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - }); - - await refetchInfo(); - setShuttingDown(false); - navigate("/", { replace: true }); - toast({ title: "Your node has been turned off." }); - } catch (error) { - console.error(error); - toast({ - title: "Failed to shutdown node: " + error, - variant: "destructive", - }); - } - }, [navigate, refetchInfo, toast]); + const { data: info, hasMnemonic, hasNodeBackup } = useInfo(); return ( <> @@ -63,35 +17,9 @@ export default function SettingsLayout() { description="Manage your Alby Hub settings." breadcrumb={false} contentRight={ - - - - {!shuttingDown && } - - - - - - Do you want to turn off Alby Hub? - - - This will turn off your Alby Hub and make your node offline. - You won't be able to send or receive bitcoin until you unlock - it. - - - - Cancel - - Continue - - - - + info?.version && ( +

{info.version}

+ ) } />
@@ -113,6 +41,7 @@ export default function SettingsLayout() { )} Developer Debug Tools + Shutdown
diff --git a/frontend/src/routes.tsx b/frontend/src/routes.tsx index 3ec6aeb1c..411a45ced 100644 --- a/frontend/src/routes.tsx +++ b/frontend/src/routes.tsx @@ -46,6 +46,7 @@ import { ChangeUnlockPassword } from "src/screens/settings/ChangeUnlockPassword" import DebugTools from "src/screens/settings/DebugTools"; import DeveloperSettings from "src/screens/settings/DeveloperSettings"; import Settings from "src/screens/settings/Settings"; +import Shutdown from "src/screens/settings/Shutdown"; import { ImportMnemonic } from "src/screens/setup/ImportMnemonic"; import { RestoreNode } from "src/screens/setup/RestoreNode"; import { SetupAdvanced } from "src/screens/setup/SetupAdvanced"; @@ -194,6 +195,10 @@ const routes = [ path: "debug-tools", element: , }, + { + path: "shutdown", + element: , + }, ], }, ], diff --git a/frontend/src/screens/BackupMnemonic.tsx b/frontend/src/screens/BackupMnemonic.tsx index f086ed6ed..26529d5b2 100644 --- a/frontend/src/screens/BackupMnemonic.tsx +++ b/frontend/src/screens/BackupMnemonic.tsx @@ -7,7 +7,6 @@ import { import React, { useState } from "react"; import { useNavigate } from "react-router-dom"; -import Container from "src/components/Container"; import ExternalLink from "src/components/ExternalLink"; import MnemonicInputs from "src/components/MnemonicInputs"; import SettingsHeader from "src/components/SettingsHeader"; @@ -92,34 +91,43 @@ export function BackupMnemonic() { return ( <> {!decryptedMnemonic ? ( - -

Please confirm it's you

-

- Enter your unlock password to continue -

+
+
+

Wallet Keys Backup

+

+ Key recovery phrase is a group of 12 random words that are the + only way to recover access to your wallet on another machine or + when you lose your unlock password. +

+
- <> -
- - setUnlockPassword(e.target.value)} - value={unlockPassword} - placeholder="Password" - /> -
- Continue - +
+ + setUnlockPassword(e.target.value)} + value={unlockPassword} + placeholder="Password" + /> +

+ Enter your unlock password to view your recovery phrase. +

+
+
+ + View Recovery Phrase + +
- +
) : (
- - +
+ + +
+ Important! +
+
+ + Password can't be reset or recovered. Make sure to back it up! + +
+
- Change Password +
+ Change Password +
- +
); } diff --git a/frontend/src/screens/settings/Shutdown.tsx b/frontend/src/screens/settings/Shutdown.tsx new file mode 100644 index 000000000..5b76f266f --- /dev/null +++ b/frontend/src/screens/settings/Shutdown.tsx @@ -0,0 +1,80 @@ +import { Power } from "lucide-react"; +import React, { useState } from "react"; +import Lottie from "react-lottie"; +import { useNavigate } from "react-router-dom"; +import animationData from "src/assets/lotties/loading.json"; +import SettingsHeader from "src/components/SettingsHeader"; +import { LoadingButton } from "src/components/ui/loading-button"; +import { useToast } from "src/components/ui/use-toast"; +import { useInfo } from "src/hooks/useInfo"; +import { request } from "src/utils/request"; + +function Shutdown() { + const [shuttingDown, setShuttingDown] = useState(false); + const { mutate: refetchInfo } = useInfo(); + const navigate = useNavigate(); + const { toast } = useToast(); + + const defaultOptions = { + loop: true, + autoplay: true, + animationData: animationData, + rendererSettings: { + preserveAspectRatio: "xMidYMid slice", + }, + }; + + const shutdown = React.useCallback(async () => { + setShuttingDown(true); + try { + await request("/api/stop", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }); + + await refetchInfo(); + setShuttingDown(false); + navigate("/", { replace: true }); + toast({ title: "Your node has been turned off." }); + } catch (error) { + console.error(error); + toast({ + title: "Failed to shutdown node: " + error, + variant: "destructive", + }); + } + }, [navigate, refetchInfo, toast]); + + return ( + <> + + {shuttingDown ? ( +
+ +

+ Shutting down... +

+
+ ) : ( + <> +
+
+ +
+ Shutdown +
+
+
+
+ + )} + + ); +} + +export default Shutdown; From 30c6be1fb1a9c0e49c9a659475ebdcb07e1f0b4c Mon Sep 17 00:00:00 2001 From: pavanjoshi914 Date: Fri, 3 Jan 2025 02:01:13 +0530 Subject: [PATCH 02/14] feat: backup screen --- frontend/src/components/MnemonicInputs.tsx | 31 ++--- .../src/components/layouts/SettingsLayout.tsx | 5 +- frontend/src/routes.tsx | 8 +- frontend/src/screens/BackupMnemonic.tsx | 53 ++++---- frontend/src/screens/settings/Backup.tsx | 114 ++++++++++++++++++ frontend/src/screens/setup/ImportMnemonic.tsx | 2 +- 6 files changed, 163 insertions(+), 50 deletions(-) create mode 100644 frontend/src/screens/settings/Backup.tsx diff --git a/frontend/src/components/MnemonicInputs.tsx b/frontend/src/components/MnemonicInputs.tsx index ed786cea6..0d312bdee 100644 --- a/frontend/src/components/MnemonicInputs.tsx +++ b/frontend/src/components/MnemonicInputs.tsx @@ -1,8 +1,8 @@ import { wordlist } from "@scure/bip39/wordlists/english"; -import { useState } from "react"; import { Card, CardContent, + CardDescription, CardHeader, CardTitle, } from "src/components/ui/card"; @@ -12,6 +12,7 @@ type MnemonicInputsProps = { mnemonic?: string; setMnemonic?(mnemonic: string): void; readOnly?: boolean; + description?: string; }; export default function MnemonicInputs({ @@ -19,11 +20,8 @@ export default function MnemonicInputs({ setMnemonic, readOnly, children, + description, }: React.PropsWithChildren) { - const [revealedIndex, setRevealedIndex] = useState( - undefined - ); - const words = mnemonic?.split(" ") || []; while (words.length < 12) { words.push(""); @@ -37,32 +35,29 @@ export default function MnemonicInputs({ <> - Recovery phrase to your wallet + + Wallet Recovery Phrase + + + {description} + -
+
{words.map((word, i) => { - const isRevealed = revealedIndex === i; const inputId = `mnemonic-word-${i}`; return (
- - {i + 1}. - + {i + 1}.
setRevealedIndex(i)} - onBlur={() => setRevealedIndex(undefined)} readOnly={readOnly} - className="w-32 text-center" + className="w-32 text-center bg-muted border-zinc-200 text-muted-foreground" list={readOnly ? undefined : "wordlist"} - value={isRevealed ? word : word.length ? "•••••" : ""} + value={word} onChange={(e) => { - if (revealedIndex !== i) { - return; - } words[i] = e.target.value; setMnemonic?.( words diff --git a/frontend/src/components/layouts/SettingsLayout.tsx b/frontend/src/components/layouts/SettingsLayout.tsx index 7b5041a60..d024f54eb 100644 --- a/frontend/src/components/layouts/SettingsLayout.tsx +++ b/frontend/src/components/layouts/SettingsLayout.tsx @@ -29,9 +29,8 @@ export default function SettingsLayout() { Unlock Password - {hasMnemonic && Backup} - {hasNodeBackup && ( - Migrate Node + {(hasMnemonic || hasNodeBackup) && ( + Backup )} {info?.albyAccountConnected && ( Your Alby Account diff --git a/frontend/src/routes.tsx b/frontend/src/routes.tsx index 411a45ced..c535c92bd 100644 --- a/frontend/src/routes.tsx +++ b/frontend/src/routes.tsx @@ -42,6 +42,7 @@ import DepositBitcoin from "src/screens/onchain/DepositBitcoin"; import ConnectPeer from "src/screens/peers/ConnectPeer"; import Peers from "src/screens/peers/Peers"; import { AlbyAccount } from "src/screens/settings/AlbyAccount"; +import Backup from "src/screens/settings/Backup"; import { ChangeUnlockPassword } from "src/screens/settings/ChangeUnlockPassword"; import DebugTools from "src/screens/settings/DebugTools"; import DeveloperSettings from "src/screens/settings/DeveloperSettings"; @@ -176,9 +177,14 @@ const routes = [ }, { path: "backup", - element: , + element: , handle: { crumb: () => "Backup" }, }, + { + path: "mnemonic-backup", + element: , + handle: { crumb: () => "Key Backup" }, + }, { path: "node-backup", element: , diff --git a/frontend/src/screens/BackupMnemonic.tsx b/frontend/src/screens/BackupMnemonic.tsx index 26529d5b2..10ed400d4 100644 --- a/frontend/src/screens/BackupMnemonic.tsx +++ b/frontend/src/screens/BackupMnemonic.tsx @@ -1,8 +1,8 @@ import { + CopyIcon, ExternalLinkIcon, LifeBuoy, ShieldAlert, - ShieldCheck, } from "lucide-react"; import React, { useState } from "react"; import { useNavigate } from "react-router-dom"; @@ -17,6 +17,7 @@ import { Label } from "src/components/ui/label"; import { LoadingButton } from "src/components/ui/loading-button"; import { useToast } from "src/components/ui/use-toast"; import { useInfo } from "src/hooks/useInfo"; +import { copyToClipboard } from "src/lib/clipboard"; import { MnemonicResponse } from "src/types"; import { handleRequestError } from "src/utils/handleRequestError"; import { request } from "src/utils/request"; @@ -91,19 +92,14 @@ export function BackupMnemonic() { return ( <> {!decryptedMnemonic ? (
-
-

Wallet Keys Backup

-

- Key recovery phrase is a group of 12 random words that are the - only way to recover access to your wallet on another machine or - when you lose your unlock password. -

-
+

+ Enter your unlock password to view your recovery phrase. +

-

- Enter your unlock password to view your recovery phrase. -

- + View Recovery Phrase
@@ -131,7 +124,7 @@ export function BackupMnemonic() { ) : (
@@ -156,14 +149,6 @@ export function BackupMnemonic() { )}
-
-
- -
- - Make sure to write them down somewhere safe and private. - -
@@ -188,8 +173,13 @@ export function BackupMnemonic() {
- -
+ +
{backedUp && !info?.albyAccountConnected && ( -
+
)} +
+ +
+ )} + + {!hasMnemonic && !hasNodeBackup && ( +

+ No wallet recovery phrase or channel state backup present. +

+ )} + + ); +} diff --git a/frontend/src/screens/setup/ImportMnemonic.tsx b/frontend/src/screens/setup/ImportMnemonic.tsx index 10ad6470b..75edb493a 100644 --- a/frontend/src/screens/setup/ImportMnemonic.tsx +++ b/frontend/src/screens/setup/ImportMnemonic.tsx @@ -62,7 +62,7 @@ export function ImportMnemonic() { <>
Date: Wed, 22 Jan 2025 17:48:12 +0530 Subject: [PATCH 03/14] feat: update settings page design for theme and unlock passwords --- frontend/src/components/PasswordAdornment.tsx | 4 +- frontend/src/components/SettingsHeader.tsx | 5 +- .../src/components/layouts/SettingsLayout.tsx | 2 +- frontend/src/components/ui/input.tsx | 30 +++-- frontend/src/screens/settings/Backup.tsx | 117 +++++++++++------- .../screens/settings/ChangeUnlockPassword.tsx | 43 ++++++- 6 files changed, 138 insertions(+), 63 deletions(-) diff --git a/frontend/src/components/PasswordAdornment.tsx b/frontend/src/components/PasswordAdornment.tsx index 244647689..12eee66d9 100644 --- a/frontend/src/components/PasswordAdornment.tsx +++ b/frontend/src/components/PasswordAdornment.tsx @@ -27,9 +27,9 @@ export default function PasswordViewAdornment({ onChange, isRevealed }: Props) { }} > {_isRevealed ? ( - + ) : ( - + )} ); diff --git a/frontend/src/components/SettingsHeader.tsx b/frontend/src/components/SettingsHeader.tsx index 7d6876a82..398ffc908 100644 --- a/frontend/src/components/SettingsHeader.tsx +++ b/frontend/src/components/SettingsHeader.tsx @@ -1,8 +1,9 @@ +import React from "react"; import { Separator } from "src/components/ui/separator"; type Props = { title: string; - description: string; + description: string | React.ReactNode; }; function SettingsHeader({ title, description }: Props) { @@ -11,7 +12,7 @@ function SettingsHeader({ title, description }: Props) {

{title}

-

{description}

+

{description}

diff --git a/frontend/src/components/layouts/SettingsLayout.tsx b/frontend/src/components/layouts/SettingsLayout.tsx index d024f54eb..a070f4fde 100644 --- a/frontend/src/components/layouts/SettingsLayout.tsx +++ b/frontend/src/components/layouts/SettingsLayout.tsx @@ -44,7 +44,7 @@ export default function SettingsLayout() {
-
+
diff --git a/frontend/src/components/ui/input.tsx b/frontend/src/components/ui/input.tsx index 13b67e42e..1afdaae6b 100644 --- a/frontend/src/components/ui/input.tsx +++ b/frontend/src/components/ui/input.tsx @@ -3,23 +3,33 @@ import * as React from "react"; import { cn } from "src/lib/utils"; export interface InputProps - extends React.InputHTMLAttributes {} + extends React.InputHTMLAttributes { + endAdornment?: React.ReactNode; +} const Input = React.forwardRef( - ({ className, type, ...props }, ref) => { + ({ className, type, endAdornment, ...props }, ref) => { return ( - + + {endAdornment && ( + + {endAdornment} + )} - ref={ref} - {...props} - /> +
); } ); + Input.displayName = "Input"; export { Input }; diff --git a/frontend/src/screens/settings/Backup.tsx b/frontend/src/screens/settings/Backup.tsx index f27c43eca..f8e10354c 100644 --- a/frontend/src/screens/settings/Backup.tsx +++ b/frontend/src/screens/settings/Backup.tsx @@ -5,6 +5,7 @@ import { Button } from "src/components/ui/button"; import { Input } from "src/components/ui/input"; import { Label } from "src/components/ui/label"; import { LoadingButton } from "src/components/ui/loading-button"; +import { Separator } from "src/components/ui/separator"; import { useToast } from "src/components/ui/use-toast"; import { useInfo } from "src/hooks/useInfo"; import { MnemonicResponse } from "src/types"; @@ -52,58 +53,86 @@ export default function Backup() { <> + + Backup your wallet recovery phrase and or your channel states in + order to migrate your node.{" "} + + + {" "} + Learn more about backups + + + } /> {hasMnemonic && ( -
-
-

Wallet Keys Backup

-

- Key recovery phrase is a group of 12 random words that are the - only way to recover access to your wallet on another machine or - when you lose your unlock password. -

-
- -
- - setUnlockPassword(e.target.value)} - value={unlockPassword} - placeholder="Password" - /> + <> +
+
+

Wallet Keys Backup

- Enter your unlock password to view your recovery phrase. + Key recovery phrase is a group of 12 random words that back up + your wallet on-chain balance. Using them is the only way to + recover access to your wallet on another machine or when you + loose your unlock password.

-
- - View Recovery Phrase - -
- -
- )} - {hasNodeBackup && ( -
-

Channels Backup

-

- If you’d like to import or host your Hub on another host or machine, - you’ll need your channels’ backup file to import your channels - state. This instance of Hub will be stopped. -

- - - -
+

+ If you loose access to your Hub and do not have your recovery + phrase, you will loose access to your funds. +

+
+
+ + setUnlockPassword(e.target.value)} + value={unlockPassword} + placeholder="Password" + /> +

+ Enter your unlock password to view your recovery phrase. +

+
+
+ + View Recovery Phase + +
+
+
+ + )} +
+

Channels Backup

+ {hasNodeBackup && ( +
+

Migrate Alby Hub

+

+ If you’d like to import or migrate your Hub onto another device or + server, you’ll need your channels’ backup file to import your + channels state. This instance of Hub will be stopped. +

+ + + +
+ )} +
+ {!hasMnemonic && !hasNodeBackup && (

No wallet recovery phrase or channel state backup present. diff --git a/frontend/src/screens/settings/ChangeUnlockPassword.tsx b/frontend/src/screens/settings/ChangeUnlockPassword.tsx index bed32f9dc..d5e234f6c 100644 --- a/frontend/src/screens/settings/ChangeUnlockPassword.tsx +++ b/frontend/src/screens/settings/ChangeUnlockPassword.tsx @@ -1,5 +1,6 @@ import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; import React from "react"; +import PasswordViewAdornment from "src/components/PasswordAdornment"; import SettingsHeader from "src/components/SettingsHeader"; import { Alert, AlertDescription, AlertTitle } from "src/components/ui/alert"; @@ -19,6 +20,13 @@ export function ChangeUnlockPassword() { const [newUnlockPassword, setNewUnlockPassword] = React.useState(""); const [confirmNewUnlockPassword, setConfirmNewUnlockPassword] = React.useState(""); + const [currentUnlockPasswordVisible, setCurrentUnlockPasswordVisible] = + React.useState(false); + const [newUnlockPasswordVisible, setNewUnlockPasswordVisible] = + React.useState(false); + const [confirmNewUnlockPasswordVisible, setConfirmNewUnlockPasswordVisible] = + React.useState(false); + const [loading, setLoading] = React.useState(false); const onSubmit = async (e: React.FormEvent) => { @@ -72,38 +80,65 @@ export function ChangeUnlockPassword() { Password can't be reset or recovered. Make sure to back it up! -

+
setCurrentUnlockPassword(e.target.value)} value={currentUnlockPassword} placeholder="Password" + endAdornment={ + + setCurrentUnlockPasswordVisible(passwordView) + } + /> + } />
setNewUnlockPassword(e.target.value)} value={newUnlockPassword} placeholder="Password" + endAdornment={ + + setNewUnlockPasswordVisible(passwordView) + } + /> + } />
setConfirmNewUnlockPassword(e.target.value)} value={confirmNewUnlockPassword} placeholder="Password" + endAdornment={ + + setConfirmNewUnlockPasswordVisible(passwordView) + } + /> + } />
From 8966ffdefa30de61c25a37184e41ffa293508a19 Mon Sep 17 00:00:00 2001 From: pavanjoshi914 Date: Wed, 22 Jan 2025 19:41:44 +0530 Subject: [PATCH 04/14] feat: revamp backup page --- .../src/components/layouts/SettingsLayout.tsx | 5 +- frontend/src/screens/settings/AlbyAccount.tsx | 76 +-------- frontend/src/screens/settings/Backup.tsx | 153 +++++++++++++++--- 3 files changed, 135 insertions(+), 99 deletions(-) diff --git a/frontend/src/components/layouts/SettingsLayout.tsx b/frontend/src/components/layouts/SettingsLayout.tsx index b67dc693c..83d2977f6 100644 --- a/frontend/src/components/layouts/SettingsLayout.tsx +++ b/frontend/src/components/layouts/SettingsLayout.tsx @@ -14,7 +14,7 @@ export default function SettingsLayout() { <>
-
+
diff --git a/frontend/src/components/ui/button.tsx b/frontend/src/components/ui/button.tsx index d533b7387..9beef3bcc 100644 --- a/frontend/src/components/ui/button.tsx +++ b/frontend/src/components/ui/button.tsx @@ -14,7 +14,7 @@ const buttonVariants = cva( default: "bg-primary text-primary-foreground shadow hover:bg-primary/90", destructive: - "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", + "border border-destructive text-destructive shadow-sm hover:text-destructive/90", outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", secondary: diff --git a/frontend/src/screens/BackupMnemonic.tsx b/frontend/src/screens/BackupMnemonic.tsx index 10ed400d4..d4182e415 100644 --- a/frontend/src/screens/BackupMnemonic.tsx +++ b/frontend/src/screens/BackupMnemonic.tsx @@ -1,14 +1,9 @@ -import { - CopyIcon, - ExternalLinkIcon, - LifeBuoy, - ShieldAlert, -} from "lucide-react"; +import { CopyIcon } from "lucide-react"; import React, { useState } from "react"; import { useNavigate } from "react-router-dom"; -import ExternalLink from "src/components/ExternalLink"; import MnemonicInputs from "src/components/MnemonicInputs"; +import PasswordViewAdornment from "src/components/PasswordAdornment"; import SettingsHeader from "src/components/SettingsHeader"; import { Button } from "src/components/ui/button"; import { Checkbox } from "src/components/ui/checkbox"; @@ -29,6 +24,8 @@ export function BackupMnemonic() { const { mutate: refetchInfo } = useInfo(); const [unlockPassword, setUnlockPassword] = React.useState(""); + const [unlockPasswordVisible, setUnlockPasswordVisible] = + React.useState(false); const [decryptedMnemonic, setDecryptedMnemonic] = React.useState(""); const [loading, setLoading] = React.useState(false); const [backedUp, setIsBackedUp] = useState(false); @@ -104,19 +101,27 @@ export function BackupMnemonic() { onSubmit={onSubmitPassword} className="max-w-md flex flex-col gap-3 mt-8" > -
+
setUnlockPassword(e.target.value)} value={unlockPassword} placeholder="Password" + endAdornment={ + + setUnlockPasswordVisible(passwordView) + } + /> + } />
- - View Recovery Phrase + + View Recovery Phase
@@ -124,55 +129,8 @@ export function BackupMnemonic() { ) : (
-
-
-
- -
- - Your recovery phrase is a set of 12 words that{" "} - backs up your wallet on-chain balance.  - {info?.albyAccountConnected && ( - <> - Channel backups are saved automatically to your Alby - Account, encrypted with your recovery phrase. - - )} - {!info?.albyAccountConnected && ( - <> - Make sure to also backup your data directory as this - is required to recover funds on your channels. You can also - connect your Alby Account for automatic encrypted backups. - - )} - -
-
-
- -
- - If you lose access to your hub and do not have your{" "} - recovery phrase - {!info?.albyAccountConnected && ( - <> or do not backup your data directory - )} - , you will lose access to your funds. - -
-
-
- - Learn more about backups - - -
- setIsBackedUp2(!backedUp2)} /> -

From 8437353599e968f069851196c2de8c3a0de69847 Mon Sep 17 00:00:00 2001 From: pavanjoshi914 Date: Wed, 22 Jan 2025 20:59:21 +0530 Subject: [PATCH 06/14] chore: backup page improvements --- frontend/src/screens/BackupNode.tsx | 26 +++-- frontend/src/screens/settings/Backup.tsx | 131 +++++++++++------------ 2 files changed, 77 insertions(+), 80 deletions(-) diff --git a/frontend/src/screens/BackupNode.tsx b/frontend/src/screens/BackupNode.tsx index 74c51f58a..c63984a85 100644 --- a/frontend/src/screens/BackupNode.tsx +++ b/frontend/src/screens/BackupNode.tsx @@ -79,15 +79,16 @@ export function BackupNode() { return ( <> - Do not run your node on multiple devices + Do not run your Alby Hub on multiple devices - Your node maintains channel state with your channel partners. After - you create this backup, do not restart Alby Hub on this device. + After creating this backup file, do not restart Alby Hub on this + device, as this will cause problems and may cause force channel + closures. @@ -100,13 +101,11 @@ export function BackupNode() { - What Happens Next + What happens next? - You'll need to enter your unlock password to encrypt and download a - backup of your Alby Hub data. After your encrypted backup is - downloaded, we'll give you instructions on how to import the backup - file on another host or machine. Your unlock password will be needed - again to restore your backup. + After typing your unlock password, you’ll be able to to download a + backup of your Alby Hub data. Then you’ll see instructions on how to + import the backup file into another device or server. {showPasswordScreen ? ( @@ -135,15 +134,14 @@ export function BackupNode() { ) : ( -

+
)} diff --git a/frontend/src/screens/settings/Backup.tsx b/frontend/src/screens/settings/Backup.tsx index 638136fab..9598ac5b8 100644 --- a/frontend/src/screens/settings/Backup.tsx +++ b/frontend/src/screens/settings/Backup.tsx @@ -92,76 +92,74 @@ export default function Backup() { /> {hasMnemonic && ( - <> -
-
-

Wallet Keys Backup

+
+
+

Wallet Keys Backup

+

+ Key recovery phrase is a group of 12 random words that back up + your wallet on-chain balance. Using them is the only way to + recover access to your wallet on another machine or when you loose + your unlock password. .  + {info?.albyAccountConnected && ( + <> + Channel backups are saved automatically to your Alby Account, + encrypted with your recovery phrase. + + )} + {!info?.albyAccountConnected && ( + <> + Make sure to also backup your data directory as this is + required to recover funds on your channels. You can also + connect your Alby Account for automatic encrypted backups. + + )} +

+
+

+ If you loose access to your Hub and do not have your recovery + phrase, you will loose access to your funds. +

+
+
+ + setUnlockPassword(e.target.value)} + value={unlockPassword} + placeholder="Password" + endAdornment={ + + setUnlockPasswordVisible(passwordView) + } + /> + } + />

- Key recovery phrase is a group of 12 random words that back up - your wallet on-chain balance. Using them is the only way to - recover access to your wallet on another machine or when you - loose your unlock password. .  - {info?.albyAccountConnected && ( - <> - Channel backups are saved automatically to your Alby - Account, encrypted with your recovery phrase. - - )} - {!info?.albyAccountConnected && ( - <> - Make sure to also backup your data directory as this - is required to recover funds on your channels. You can also - connect your Alby Account for automatic encrypted backups. - - )} + Enter your unlock password to view your recovery phrase.

-

- If you loose access to your Hub and do not have your recovery - phrase, you will loose access to your funds. -

- -
- - setUnlockPassword(e.target.value)} - value={unlockPassword} - placeholder="Password" - endAdornment={ - - setUnlockPasswordVisible(passwordView) - } - /> - } - /> -

- Enter your unlock password to view your recovery phrase. -

-
-
- - View Recovery Phase - -
-
-
- - +
+ + View Recovery Phase + +
+ +
)} - {info?.vssSupported || - (hasNodeBackup && ( + {(info?.vssSupported || hasNodeBackup) && ( + <> +

Channels Backup

@@ -257,7 +255,8 @@ export default function Backup() {
)}
- ))} + + )} {!hasMnemonic && !hasNodeBackup && !info?.vssSupported && (

From d1c5f4bbea8e0161754f6389d2fae7dd80dbb4b2 Mon Sep 17 00:00:00 2001 From: pavanjoshi914 Date: Wed, 22 Jan 2025 22:54:05 +0530 Subject: [PATCH 07/14] feat: revamp developers settings + debug tools revert shutdown button to top right --- .../src/components/layouts/SettingsLayout.tsx | 85 +++++++- frontend/src/routes.tsx | 6 +- frontend/src/screens/settings/DebugTools.tsx | 36 +++- .../screens/settings/DeveloperSettings.tsx | 196 ++++++++++-------- frontend/src/screens/settings/Shutdown.tsx | 80 ------- 5 files changed, 215 insertions(+), 188 deletions(-) delete mode 100644 frontend/src/screens/settings/Shutdown.tsx diff --git a/frontend/src/components/layouts/SettingsLayout.tsx b/frontend/src/components/layouts/SettingsLayout.tsx index f906ad755..c7ca362b2 100644 --- a/frontend/src/components/layouts/SettingsLayout.tsx +++ b/frontend/src/components/layouts/SettingsLayout.tsx @@ -1,14 +1,60 @@ -import React from "react"; -import { NavLink, Outlet } from "react-router-dom"; +import React, { useState } from "react"; +import { NavLink, Outlet, useNavigate } from "react-router-dom"; import AppHeader from "src/components/AppHeader"; import { buttonVariants } from "src/components/ui/button"; import { useInfo } from "src/hooks/useInfo"; +import { Power } from "lucide-react"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "src/components/ui/alert-dialog"; +import { LoadingButton } from "src/components/ui/loading-button"; +import { useToast } from "src/components/ui/use-toast"; import { cn } from "src/lib/utils"; +import { request } from "src/utils/request"; export default function SettingsLayout() { - const { data: info, hasMnemonic, hasNodeBackup } = useInfo(); + const { + data: info, + mutate: refetchInfo, + hasMnemonic, + hasNodeBackup, + } = useInfo(); + const navigate = useNavigate(); + const { toast } = useToast(); + const [shuttingDown, setShuttingDown] = useState(false); + + const shutdown = React.useCallback(async () => { + setShuttingDown(true); + try { + await request("/api/stop", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }); + + await refetchInfo(); + setShuttingDown(false); + navigate("/", { replace: true }); + toast({ title: "Your node has been turned off." }); + } catch (error) { + console.error(error); + toast({ + title: "Failed to shutdown node: " + error, + variant: "destructive", + }); + } + }, [navigate, refetchInfo, toast]); return ( <> @@ -17,11 +63,38 @@ export default function SettingsLayout() { description="" breadcrumb={false} contentRight={ - info?.version && ( -

{info.version}

- ) + + + + {!shuttingDown && } + + + + + + Do you want to turn off your Alby Hub? + + + This will turn off your Alby Hub and make your node offline. + You won’t be able to send or receive bitcoin until you unlock + it. + + + + Cancel + + Continue + + + + } /> +
diff --git a/frontend/src/screens/BackupMnemonic.tsx b/frontend/src/screens/BackupMnemonic.tsx index d4182e415..530c240d4 100644 --- a/frontend/src/screens/BackupMnemonic.tsx +++ b/frontend/src/screens/BackupMnemonic.tsx @@ -120,7 +120,11 @@ export function BackupMnemonic() { />
- + View Recovery Phase
diff --git a/frontend/src/screens/settings/AlbyAccount.tsx b/frontend/src/screens/settings/AlbyAccount.tsx index 8ffa976a6..8e116047c 100644 --- a/frontend/src/screens/settings/AlbyAccount.tsx +++ b/frontend/src/screens/settings/AlbyAccount.tsx @@ -1,15 +1,9 @@ -import { ExitIcon } from "@radix-ui/react-icons"; -import { ExternalLinkIcon } from "lucide-react"; +import { Link2Off, RefreshCcw, SquareArrowOutUpRight } from "lucide-react"; -import ExternalLink from "src/components/ExternalLink"; import Loading from "src/components/Loading"; import SettingsHeader from "src/components/SettingsHeader"; -import { - Card, - CardDescription, - CardHeader, - CardTitle, -} from "src/components/ui/card"; +import { Button, ExternalLinkButton } from "src/components/ui/button"; +import { Separator } from "src/components/ui/separator"; import { UnlinkAlbyAccount } from "src/components/UnlinkAlbyAccount"; import { useAlbyMe } from "src/hooks/useAlbyMe"; import { useInfo } from "src/hooks/useInfo"; @@ -26,48 +20,73 @@ export function AlbyAccount() { <> - - - - Your Alby Account - - Manage your Alby Account - Settings such as your lightning address on getalby.com - - - - - - - - Change Alby Account - - Link your Hub to a different Alby - Account - - - - - - - - - Disconnect Alby Account - - Use Alby Hub without an Alby - Account - - - - +
+
+
+

Manage Alby Account

+

+ Manage your Alby Account settings such as lightning address or + notifications on getalby.com +

+
+
+ + Alby Account Settings{" "} + + +
+
+ +
+
+

Change Alby Account

+

+ Link your Hub to a different Alby Account +

+
+
+ + + +
+
+ +
+
+

Unlink Alby Account

+

+ Use your Alby Hub without an Alby Account. +

+
+
+ + + +
+
+
); } diff --git a/frontend/src/screens/settings/Backup.tsx b/frontend/src/screens/settings/Backup.tsx index 9598ac5b8..24cc80d58 100644 --- a/frontend/src/screens/settings/Backup.tsx +++ b/frontend/src/screens/settings/Backup.tsx @@ -148,6 +148,7 @@ export default function Backup() { View Recovery Phase @@ -192,6 +193,7 @@ export default function Backup() { variant="secondary" loading={isMigratingStorage} disabled={info.ldkVssEnabled} + size={"lg"} > {info.ldkVssEnabled ? "Automated Backups Enabled" @@ -250,7 +252,9 @@ export default function Backup() { stopped.

- +
)} diff --git a/frontend/src/screens/settings/DebugTools.tsx b/frontend/src/screens/settings/DebugTools.tsx index 79089e208..aab6db835 100644 --- a/frontend/src/screens/settings/DebugTools.tsx +++ b/frontend/src/screens/settings/DebugTools.tsx @@ -256,7 +256,7 @@ export default function DebugTools() {
@@ -73,7 +73,7 @@ export default function DeveloperSettings() {