Skip to content

Commit 59e959d

Browse files
fix(earn): disable fidelity bond actions while rescanning (#1373)
1 parent bce1df3 commit 59e959d

2 files changed

Lines changed: 121 additions & 18 deletions

File tree

src/components/earn/EarnPage.test.tsx

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -328,23 +328,109 @@ describe('EarnPage', () => {
328328
expect(screen.getByText('earn.alert_waiting_start_title')).toBeInTheDocument()
329329
})
330330

331-
it('opens fidelity bond actions', async () => {
332-
const user = userEvent.setup()
331+
it('display debug output in developer mode', () => {
333332
mocks.developerMode = true
334333
mocks.walletInfo.fidelityBondSummary = { fbOutputs: [expiredBond] }
335334

336335
render(<EarnPage walletFileName="wallet.jmdat" />)
337336

338-
expect(screen.getByText('fidelity-bond:bond-tx:0')).toBeInTheDocument()
339337
expect(screen.getByText(/walletInfo\.fidelityBondSummary\.fbOutputs/u)).toBeInTheDocument()
338+
})
339+
340+
it('enables creating a fidelity bond', async () => {
341+
const user = userEvent.setup()
342+
343+
expect(mocks.walletInfo.fidelityBondSummary.fbOutputs, 'sanity check').toHaveLength(0)
344+
345+
render(<EarnPage walletFileName="wallet.jmdat" />)
346+
347+
expect(screen.getByText('create-bond-dialog:false')).toBeInTheDocument()
348+
349+
const createFidelityBondButton = screen.getByRole('button', {
350+
name: 'earn.fidelity_bond.create_form.button_create',
351+
})
352+
expect(createFidelityBondButton).toBeEnabled()
353+
354+
await user.click(createFidelityBondButton)
355+
expect(screen.getByText('create-bond-dialog:true')).toBeInTheDocument()
356+
})
357+
358+
it('disables creating a fidelity bond while rescanning', async () => {
359+
const user = userEvent.setup()
360+
setSession({
361+
rescanning: true,
362+
})
363+
364+
expect(mocks.walletInfo.fidelityBondSummary.fbOutputs, 'sanity check').toHaveLength(0)
365+
366+
render(<EarnPage walletFileName="wallet.jmdat" />)
367+
368+
expect(screen.getByText('create-bond-dialog:false')).toBeInTheDocument()
369+
370+
const createFidelityBondButton = screen.getByRole('button', {
371+
name: 'earn.fidelity_bond.create_form.button_create',
372+
})
373+
expect(createFidelityBondButton).toBeDisabled()
374+
375+
await user.click(createFidelityBondButton)
376+
expect(screen.getByText('create-bond-dialog:false')).toBeInTheDocument()
377+
})
378+
379+
it('enables fidelity bond actions on existing fidelity bond', async () => {
380+
const user = userEvent.setup()
381+
mocks.walletInfo.fidelityBondSummary = { fbOutputs: [expiredBond] }
382+
383+
render(<EarnPage walletFileName="wallet.jmdat" />)
340384

341-
await user.click(screen.getByRole('button', { name: /earn\.fidelity_bond\.existing\.button_spend/u }))
385+
expect(screen.getByText('fidelity-bond:bond-tx:0')).toBeInTheDocument()
386+
387+
expect(screen.queryByText('move-to-jar-dialog:false')).not.toBeInTheDocument()
388+
expect(screen.queryByText('move-to-jar-dialog:true')).not.toBeInTheDocument()
389+
expect(screen.queryByText('renew-bond-dialog:false')).not.toBeInTheDocument()
390+
expect(screen.queryByText('renew-bond-dialog:true')).not.toBeInTheDocument()
391+
392+
const moveToJarButton = screen.getByRole('button', { name: /earn\.fidelity_bond\.existing\.button_spend/u })
393+
expect(moveToJarButton).toBeEnabled()
394+
395+
await user.click(moveToJarButton)
342396
expect(screen.getByText('move-to-jar-dialog:true')).toBeInTheDocument()
343397

344-
await user.click(screen.getByRole('button', { name: /earn\.fidelity_bond\.existing\.button_renew/u }))
398+
const renewButton = screen.getByRole('button', { name: /earn\.fidelity_bond\.existing\.button_renew/u })
399+
expect(renewButton).toBeEnabled()
400+
401+
await user.click(renewButton)
345402
expect(screen.getByText('renew-bond-dialog:true')).toBeInTheDocument()
346403
})
347404

405+
it('disables fidelity bond actions on existing fidelity bond when rescanning is active', async () => {
406+
const user = userEvent.setup()
407+
mocks.walletInfo.fidelityBondSummary = { fbOutputs: [expiredBond] }
408+
setSession({
409+
rescanning: true,
410+
})
411+
412+
render(<EarnPage walletFileName="wallet.jmdat" />)
413+
414+
expect(screen.queryByText('move-to-jar-dialog:false')).not.toBeInTheDocument()
415+
expect(screen.queryByText('move-to-jar-dialog:true')).not.toBeInTheDocument()
416+
expect(screen.queryByText('renew-bond-dialog:false')).not.toBeInTheDocument()
417+
expect(screen.queryByText('renew-bond-dialog:true')).not.toBeInTheDocument()
418+
419+
const moveToJarButton = screen.getByRole('button', { name: /earn\.fidelity_bond\.existing\.button_spend/u })
420+
expect(moveToJarButton).toBeDisabled()
421+
422+
await user.click(moveToJarButton)
423+
expect(screen.queryByText('move-to-jar-dialog:false')).not.toBeInTheDocument()
424+
expect(screen.queryByText('move-to-jar-dialog:true')).not.toBeInTheDocument()
425+
426+
const renewButton = screen.getByRole('button', { name: /earn\.fidelity_bond\.existing\.button_renew/u })
427+
expect(renewButton).toBeDisabled()
428+
429+
await user.click(renewButton)
430+
expect(screen.queryByText('renew-bond-dialog:false')).not.toBeInTheDocument()
431+
expect(screen.queryByText('renew-bond-dialog:true')).not.toBeInTheDocument()
432+
})
433+
348434
it('shows the coinjoin-in-progress alert', () => {
349435
setSession({ coinjoin_in_process: true })
350436
render(<EarnPage walletFileName="wallet.jmdat" />)

src/components/earn/EarnPage.tsx

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader,
2424
import { FeeConfigErrorAlert } from '@/components/ui/jam/FeeConfigErrorAlert'
2525
import { PageLoading } from '@/components/ui/jam/PageLoading'
2626
import PageTitle from '@/components/ui/jam/PageTitle'
27+
import { isDevMode } from '@/constants/debugFeatures'
2728
import * as JAM from '@/constants/jam'
2829
import { routes } from '@/constants/routes'
2930
import { useJamWalletInfoContext } from '@/context/JamWalletInfoContext'
@@ -106,6 +107,8 @@ const NoSpendableBalanceAlert = () => {
106107
)
107108
}
108109

110+
const JAM_EARN_CREATE_MULTIPLE_FIDELITY_BONDS_ENABLED = isDevMode()
111+
109112
export const EarnPage = ({ walletFileName }: EarnPageProps) => {
110113
const { t } = useTranslation()
111114
const client = useApiClient()
@@ -182,6 +185,19 @@ export const EarnPage = ({ walletFileName }: EarnPageProps) => {
182185
const waitingForMakerUpdate = isWaitingMakerStart || isWaitingMakerStop
183186
const waitingForOfferUpdate = makerRunning && !isCurrentOfferAvailable
184187

188+
const hasFidelityBond = walletInfo.fidelityBondSummary.fbOutputs.length > 0
189+
const isFidelityBondActionsEnabled =
190+
jmSession?.rescanning === false &&
191+
jmSession.maker_running === false &&
192+
jmSession.coinjoin_in_process === false &&
193+
!waitingForMakerUpdate &&
194+
!waitingForOfferUpdate &&
195+
!walletInfo.isFetching
196+
197+
const isCreateFidelityBondEnabled =
198+
isFidelityBondActionsEnabled && (!hasFidelityBond || JAM_EARN_CREATE_MULTIPLE_FIDELITY_BONDS_ENABLED)
199+
const showCreateAdditionalFidelityBond = JAM_EARN_CREATE_MULTIPLE_FIDELITY_BONDS_ENABLED
200+
185201
useRefreshSession({
186202
enabled: waitingForMakerUpdate || waitingForOfferUpdate,
187203
refetchInterval: WAIT_FOR_UPDATE_SESSION_POLLING_INTERVAL,
@@ -322,7 +338,7 @@ export const EarnPage = ({ walletFileName }: EarnPageProps) => {
322338
</Card>
323339

324340
{/* Fidelity Bonds */}
325-
{walletInfo.fidelityBondSummary.fbOutputs.length === 0 ? (
341+
{!hasFidelityBond ? (
326342
<div
327343
className={cn('mt-8', {
328344
hidden: jmSession.maker_running || waitingForMakerUpdate || waitingForOfferUpdate,
@@ -338,7 +354,11 @@ export const EarnPage = ({ walletFileName }: EarnPageProps) => {
338354
<CardAction></CardAction>
339355
</CardHeader>
340356
<CardFooter className="gap-2">
341-
<Button variant="default" onClick={() => setShowCreateFidelityBondDialog(true)}>
357+
<Button
358+
variant="default"
359+
disabled={!isCreateFidelityBondEnabled}
360+
onClick={() => setShowCreateFidelityBondDialog(true)}
361+
>
342362
{t('earn.fidelity_bond.create_form.button_create')}
343363
</Button>
344364
</CardFooter>
@@ -354,14 +374,7 @@ export const EarnPage = ({ walletFileName }: EarnPageProps) => {
354374
<div className="space-y-4">
355375
{walletInfo.fidelityBondSummary.fbOutputs.map((it) => {
356376
const isExpired = !fb.utxo.isLocked(it)
357-
const actionsEnabled =
358-
isExpired &&
359-
!jmSession.rescanning &&
360-
!jmSession.maker_running &&
361-
!jmSession.coinjoin_in_process &&
362-
!waitingForMakerUpdate &&
363-
!waitingForOfferUpdate &&
364-
!walletInfo.isFetching
377+
const actionsEnabled = isExpired && isFidelityBondActionsEnabled
365378
return (
366379
<FidelityBondCard value={it} key={it.utxo}>
367380
{isExpired && (
@@ -390,10 +403,14 @@ export const EarnPage = ({ walletFileName }: EarnPageProps) => {
390403
)
391404
})}
392405
</div>
393-
{/* Existing bonds hide the create button — expose it in developer mode to test multiple bonds */}
394-
{isDeveloperMode && (
406+
407+
{showCreateAdditionalFidelityBond && (
395408
<div className="mt-2 flex items-center gap-2">
396-
<Button variant="outline" onClick={() => setShowCreateFidelityBondDialog(true)}>
409+
<Button
410+
variant="outline"
411+
disabled={!isCreateFidelityBondEnabled}
412+
onClick={() => setShowCreateFidelityBondDialog(true)}
413+
>
397414
<PlusIcon />
398415
{t('earn.fidelity_bond.create_form.button_create')}
399416
</Button>

0 commit comments

Comments
 (0)