diff --git a/src/App.tsx b/src/App.tsx
index e6f15c76d..ac1d8eb9c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,5 +1,4 @@
-import { lazy, Suspense, useEffect, useMemo, useState } from 'react'
-import type { PropsWithChildren } from 'react'
+import { lazy, Suspense, useCallback, useEffect, useState } from 'react'
import { lockwalletOptions } from '@joinmarket-webui/joinmarket-api-ts/@tanstack/react-query'
import { token } from '@joinmarket-webui/joinmarket-api-ts/jm'
import { QueryClientProvider, useMutation, useQuery } from '@tanstack/react-query'
@@ -13,6 +12,7 @@ import {
Outlet,
Route,
RouterProvider,
+ useOutletContext,
type NavigateFunction,
} from 'react-router-dom'
import { toast } from 'sonner'
@@ -66,8 +66,23 @@ const clearAuthAndQueryCache = () => {
queryClient.clear()
}
-const ProtectedRoute = ({ authenticated, children }: PropsWithChildren<{ authenticated: boolean }>) => {
- return authenticated ? <>{children}> :
+const useWalletFileName = () => useStore(authStore, (state) => state.state?.walletFileName)
+
+const useAuthenticated = () =>
+ useStore(authStore, (state) => state.state?.walletFileName !== undefined && state.state?.auth?.token !== undefined)
+
+const ProtectedRoute = () => {
+ const walletFileName = useWalletFileName()
+ return useAuthenticated() ? (
+
+
+ {walletFileName && }
+
+
+
+ ) : (
+
+ )
}
type LockWalletDialogContext = {
@@ -76,20 +91,50 @@ type LockWalletDialogContext = {
t: TFunction<'translation', undefined>
}
-function App() {
- const walletFileName = useStore(authStore, (state) => state.state?.walletFileName)
- const hasAuthToken = useStore(authStore, (state) => state.state?.auth?.token !== undefined)
- const { enabled: isDeveloperMode } = useDeveloperMode()
- const authenticated = useMemo(() => walletFileName !== undefined && hasAuthToken, [walletFileName, hasAuthToken])
+type LockWalletActionContextValue = {
+ onLockWallet: (navigate: NavigateFunction, t: TFunction<'translation', undefined>) => Promise
+}
- const jmSession = useStore(jmSessionStore, (state) => state.state)
+const LoginRoute = () => {
+ return useAuthenticated() ? :
+}
+
+const CreateWalletRoute = () => {
+ return useAuthenticated() ? :
+}
+
+const ImportWalletRoute = () => {
+ return useAuthenticated() ? :
+}
+
+const DevSetupRoute = () => {
+ return useDeveloperMode().enabled && isDebugFeatureEnabled('devSetupPage') ? (
+ }>
+
+
+ ) : (
+
+ )
+}
+
+const DevErrorExampleRoute = () => {
+ return useDeveloperMode().enabled && isDebugFeatureEnabled('devErrorExamplePage') ? (
+ }>
+
+
+ ) : (
+
+ )
+}
+const useWalletLockController = () => {
+ const walletFileName = useWalletFileName()
+ const jmSession = useStore(jmSessionStore, (state) => state.state)
const makerRunning = jmSession?.maker_running === true
const coinjoinInProgress = jmSession?.coinjoin_in_process === true || (jmSession?.schedule?.length || 0) > 0
-
const client = useApiClient()
- const lockWalletQuery = useQuery(
+ const { refetch: refetchLockWallet, isFetching: isLockingWallet } = useQuery(
{
...lockwalletOptions({
client,
@@ -99,12 +144,12 @@ function App() {
},
queryClient,
)
- const lockWalletMutation = useMutation(
+ const { mutateAsync: lockWalletMutateAsync } = useMutation(
{
scope: { id: 'lock-wallet' },
mutationFn: withMutationDelay(
async () => {
- return await lockWalletQuery.refetch({ throwOnError: true })
+ return await refetchLockWallet({ throwOnError: true })
},
{
throttle: 210,
@@ -117,137 +162,148 @@ function App() {
const [lockWalletDialogContext, setLockWalletDialogContext] = useState()
- const doOnLogout = async (navigate: NavigateFunction) => {
+ const doOnLogout = useCallback(async (navigate: NavigateFunction) => {
clearAuthAndQueryCache()
await navigate(routes.login)
+ }, [])
+
+ const doOnLockWalletConfirm = useCallback(
+ async (navigate: NavigateFunction, t: TFunction<'translation', undefined>) => {
+ if (!walletFileName) return
+
+ try {
+ await lockWalletMutateAsync()
+ toast.success(
+ t('wallets.wallet_preview.alert_wallet_locked_successfully', {
+ walletName: walletDisplayName(walletFileName),
+ }),
+ )
+ setLockWalletDialogContext(undefined)
+ await doOnLogout(navigate)
+ } catch (error: unknown) {
+ const reason = (error instanceof Error ? error.message : undefined) || t('global.errors.reason_unknown')
+ toast.error(t('global.errors.error_reloading_wallet_failed', { reason }))
+ console.error('Failed to lock wallet:', error)
+ }
+ },
+ [doOnLogout, lockWalletMutateAsync, walletFileName],
+ )
+
+ const doOnLockWallet = useCallback(
+ async (navigate: NavigateFunction, t: TFunction<'translation', undefined>) => {
+ if (!walletFileName) return
+
+ if (makerRunning || coinjoinInProgress) {
+ setLockWalletDialogContext({
+ open: true,
+ navigate,
+ t,
+ })
+ } else {
+ await doOnLockWalletConfirm(navigate, t)
+ }
+ },
+ [coinjoinInProgress, makerRunning, doOnLockWalletConfirm, walletFileName],
+ )
+
+ return {
+ walletFileName,
+ doOnLogout,
+ doOnLockWallet,
+ doOnLockWalletConfirm,
+ lockWalletDialogContext,
+ setLockWalletDialogContext,
+ isLockingWallet,
+ makerRunning,
+ coinjoinInProgress,
}
+}
- const doOnLockWallet = async (navigate: NavigateFunction, t: TFunction<'translation', undefined>) => {
- if (!walletFileName) return
+const ProtectedNavbarRoute = () => {
+ const controller = useWalletLockController()
- if (makerRunning || coinjoinInProgress) {
- setLockWalletDialogContext({
- open: true,
- navigate,
- t,
- })
- } else {
- await doOnLockWalletConfirm(navigate, t)
- }
+ if (!controller.walletFileName) {
+ return
}
- const doOnLockWalletConfirm = async (navigate: NavigateFunction, t: TFunction<'translation', undefined>) => {
- if (!walletFileName) return
- try {
- await lockWalletMutation.mutateAsync()
- toast.success(
- t('wallets.wallet_preview.alert_wallet_locked_successfully', { walletName: walletDisplayName(walletFileName) }),
- )
- setLockWalletDialogContext(undefined)
- await doOnLogout(navigate)
- } catch (error: unknown) {
- const reason = (error instanceof Error ? error.message : undefined) || t('global.errors.reason_unknown')
- toast.error(t('global.errors.error_reloading_wallet_failed', { reason }))
- console.error('Failed to lock wallet:', error)
- }
+ return (
+
+
+
+ )
+}
+
+const walletFileNameOrRedirect = (Component: React.FC<{ walletFileName: WalletFileName }>) => {
+ const Wrapped: React.FC = () => {
+ const walletFileName = useWalletFileName()
+ return walletFileName ? :
}
+ return
+}
- const router = createBrowserRouter(
- createRoutesFromElements(
- } errorElement={}>
- : } />
- : }
- />
- : }
- />
- {isDeveloperMode && isDebugFeatureEnabled('devSetupPage') && (
- }>
-
-
- }
- />
- )}
- {isDeveloperMode && isDebugFeatureEnabled('devErrorExamplePage') && (
- }>
-
-
- }
- />
- )}
-
-
-
- {walletFileName && (
- <>
-
- >
- )}
-
-
-
-
- }
- >
- }>
- } />
-
-
-
-
- }
- >
- } />
- } />
- } />
- } />
- } />
- } />
- }
- />
- } />
- } />
- } />
- }
- />
- {isDeveloperMode && isDebugFeatureEnabled('devPage') && (
- }>
-
-
- }
- />
- )}
-
-
- } />
- ,
- ),
+const SettingsRoute = () => {
+ const walletFileName = useWalletFileName()
+ const { onLockWallet } = useOutletContext()
+
+ return walletFileName ? (
+
+ ) : (
+
)
+}
+
+const DevPageRoute = () => {
+ const walletFileName = useWalletFileName()
+ return useDeveloperMode().enabled && isDebugFeatureEnabled('devPage') && walletFileName ? (
+ }>
+
+
+ ) : (
+
+ )
+}
+
+const router = createBrowserRouter(
+ createRoutesFromElements(
+ } errorElement={}>
+ } />
+ } />
+ } />
+ } />
+ } />
+ }>
+ }>
+
+
+ }>
+
+
+
+
+
+
+ } />
+ } />
+ } />
+
+
+ } />
+
+
+ } />
+ ,
+ ),
+)
+
+function App() {
+ const walletFileName = useWalletFileName()
+ const controller = useWalletLockController()
+ const lockWalletDialogContext = controller.lockWalletDialogContext
+
return (
@@ -255,18 +311,17 @@ function App() {
- {walletFileName && (
- <>
-
- >
- )}
+
+ {walletFileName && }
{lockWalletDialogContext && (
setLockWalletDialogContext(undefined)}
- onConfirm={() => doOnLockWalletConfirm(lockWalletDialogContext?.navigate, lockWalletDialogContext?.t)}
- makerRunning={makerRunning}
- coinjoinInProgress={coinjoinInProgress}
+ open={lockWalletDialogContext.open}
+ onOpenChange={() => controller.setLockWalletDialogContext(undefined)}
+ onConfirm={() =>
+ controller.doOnLockWalletConfirm(lockWalletDialogContext.navigate, lockWalletDialogContext.t)
+ }
+ makerRunning={controller.makerRunning}
+ coinjoinInProgress={controller.coinjoinInProgress}
/>
)}