From 529763a55b6d209d52df7ada51450d3dc8889f6b Mon Sep 17 00:00:00 2001 From: kunal-595 Date: Tue, 20 Jan 2026 13:24:58 +0530 Subject: [PATCH] Add lock wallet confirmation --- src/components/layout/AppNavbar.tsx | 34 ++++++++- .../settings/LockWalletConfirmDialog.tsx | 76 +++++++++++++++++++ src/components/settings/SettingsPage.tsx | 27 ++++++- src/i18n/locales/en/translation.json | 1 + src/store/jamSettingsStore.ts | 2 + 5 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 src/components/settings/LockWalletConfirmDialog.tsx diff --git a/src/components/layout/AppNavbar.tsx b/src/components/layout/AppNavbar.tsx index 05ed23ead..befb8f06b 100644 --- a/src/components/layout/AppNavbar.tsx +++ b/src/components/layout/AppNavbar.tsx @@ -1,4 +1,4 @@ -import type { PropsWithChildren } from 'react' +import { useState, type PropsWithChildren } from 'react' import type { SessionResponse } from '@joinmarket-webui/joinmarket-api-ts/jm' import type { TFunction } from 'i18next' import { @@ -12,7 +12,9 @@ import { } from 'lucide-react' import { useTranslation } from 'react-i18next' import { Link, useNavigate, type NavigateFunction } from 'react-router-dom' +import { useStore } from 'zustand' import { DevBadge } from '@/components/dev/DevBadge' +import { LockWalletConfirmDialog } from '@/components/settings/LockWalletConfirmDialog' import { Button } from '@/components/ui/button' import { ThemeToggleButton } from '@/components/ui/jam/ThemeToggleButton' import { Skeleton } from '@/components/ui/skeleton' @@ -21,6 +23,7 @@ import { isDevMode } from '@/constants/debugFeatures' import { routes } from '@/constants/routes' import type { RescanInfo } from '@/context/JamSessionInfoContext' import { cn, shortenStringMiddle } from '@/lib/utils' +import { jamSettingsStore } from '@/store/jamSettingsStore' import type { AmountSats } from '@/types/global' const WithActivityIndicator = ({ active, children }: PropsWithChildren<{ active: boolean }>) => { @@ -137,6 +140,9 @@ export function AppNavbar({ }: AppNavbarProps) { const { t } = useTranslation() const navigate = useNavigate() + const showLockWalletConfirmation = useStore(jamSettingsStore, (state) => state.state.showLockWalletConfirmation) + const [showLockWalletDialog, setShowLockWalletDialog] = useState(false) + const [isLockingWallet, setIsLockingWallet] = useState(false) const isSidebarOpen = sidebarInfo === undefined ? false : sidebarInfo.isMobile ? sidebarInfo.openMobile : sidebarInfo.open @@ -155,6 +161,24 @@ export function AppNavbar({ const rescanningRoute = rescanInfo?.rescanning !== true ? undefined : routes.rescan + const handleLockWallet = async () => { + try { + setIsLockingWallet(true) + await onLockWallet(navigate, t) + } finally { + setIsLockingWallet(false) + setShowLockWalletDialog(false) + } + } + + const onClickLockWallet = () => { + if (showLockWalletConfirmation) { + setShowLockWalletDialog(true) + } else { + handleLockWallet() + } + } + return (
await onLockWallet(navigate, t)} + onClick={onClickLockWallet} aria-label={t('settings.button_lock_wallet')} title={t('settings.button_lock_wallet')} > @@ -246,6 +270,12 @@ export function AppNavbar({ {sidebarTrigger} +
) } diff --git a/src/components/settings/LockWalletConfirmDialog.tsx b/src/components/settings/LockWalletConfirmDialog.tsx new file mode 100644 index 000000000..86c173233 --- /dev/null +++ b/src/components/settings/LockWalletConfirmDialog.tsx @@ -0,0 +1,76 @@ +import type { ComponentProps } from 'react' +import { AlertTriangleIcon } from 'lucide-react' +import { useTranslation } from 'react-i18next' +import { useStore } from 'zustand' +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' +import { Button } from '@/components/ui/button' +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { jmSessionStore } from '@/store/jmSessionStore' +import type { WithRequiredProperty } from '@/types/global' + +type LockWalletConfirmDialogProps = WithRequiredProperty< + Omit, 'children'>, + 'open' | 'onOpenChange' +> & { + onConfirm: () => void + isLocking?: boolean +} + +export const LockWalletConfirmDialog = ({ + open, + onOpenChange, + onConfirm, + isLocking = false, +}: LockWalletConfirmDialogProps) => { + const { t } = useTranslation() + const session = useStore(jmSessionStore, (state) => state.state) + + const makerRunning = session?.maker_running === true + const coinjoinInProgress = session?.coinjoin_in_process === true + + return ( + + + + {t('wallets.wallet_preview.modal_lock_wallet_title')} + + {t('wallets.wallet_preview.modal_lock_wallet_alternative_action_text')} + + +
+ {makerRunning && ( + + + {t('wallets.wallet_preview.modal_lock_wallet_title')} + {t('wallets.wallet_preview.modal_lock_wallet_maker_running_text')} + + )} + {coinjoinInProgress && ( + + + {t('wallets.wallet_preview.modal_lock_wallet_title')} + + {t('wallets.wallet_preview.modal_lock_wallet_coinjoin_in_progress_text')} + + + )} +
+ + + + +
+
+ ) +} diff --git a/src/components/settings/SettingsPage.tsx b/src/components/settings/SettingsPage.tsx index 5ade75bc6..1483e66ab 100644 --- a/src/components/settings/SettingsPage.tsx +++ b/src/components/settings/SettingsPage.tsx @@ -15,6 +15,7 @@ import { ArrowLeftRightIcon, LockKeyholeIcon, BookKeyIcon, + ShieldAlertIcon, } from 'lucide-react' import { useTheme } from 'next-themes' import { useTranslation } from 'react-i18next' @@ -34,6 +35,7 @@ import { jamSettingsStore } from '@/store/jamSettingsStore' import { AccountXpubsDialog } from './AccountXpubsDialog' import { FeeLimitDialog } from './FeeLimitDialog' import { LanguageSelector } from './LanguageSelector' +import { LockWalletConfirmDialog } from './LockWalletConfirmDialog' import { SeedPhraseDialog } from './SeedPhraseDialog' import { SettingItem, SettingsLink, SettingSwitch } from './SettingsItem' @@ -52,6 +54,7 @@ export const SettingsPage = ({ walletFileName, onLockWallet }: SettingPageProps) const [showSeedDialog, setShowSeedDialog] = useState(false) const [showXpubsDialog, setShowXpubsDialog] = useState(false) const [showFeeLimitDialog, setShowFeeLimitDialog] = useState(false) + const [showLockWalletDialog, setShowLockWalletDialog] = useState(false) const hashedPassword = useStore(authStore, (state) => state.state?.hashed_password) const { isLogsEnabled } = useFeatures() const [isLockingWallet, setIsLockingWallet] = useState(false) @@ -62,6 +65,15 @@ export const SettingsPage = ({ walletFileName, onLockWallet }: SettingPageProps) await onLockWallet(navigate, t) } finally { setIsLockingWallet(false) + setShowLockWalletDialog(false) + } + } + + const onClickLockWallet = async () => { + if (jamSettings.state.showLockWalletConfirmation) { + setShowLockWalletDialog(true) + } else { + await handleLockWallet() } } @@ -144,10 +156,17 @@ export const SettingsPage = ({ walletFileName, onLockWallet }: SettingPageProps) + jamSettings.update({ showLockWalletConfirmation: checked })} + /> + @@ -255,6 +274,12 @@ export const SettingsPage = ({ walletFileName, onLockWallet }: SettingPageProps) )} + {hashedPassword && ( <> ()(