fix: correct rescan progress on Rescan page (#1313)
Some checks are pending
Build / build (v26.3.0) (push) Waiting to run
CodeQL / Analyze (push) Waiting to run
Deploy Storybook / deploy (push) Waiting to run

* dev(regtest): remove unnecessary env vars

* fix: correct rescan progress on Rescan page

* Revert "dev(regtest): remove unnecessary env vars"

This reverts commit f6ca1cbd4c.

* chore: progress in percentage as string
This commit is contained in:
Thebora Kompanioni 2026-07-07 21:07:23 +02:00 committed by GitHub
parent 1b5d1cf6e6
commit 147d815f3b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 29 additions and 27 deletions

View file

@ -60,11 +60,11 @@ const WalletPreview = ({
<div className="flex min-h-5 min-w-0 items-center sm:min-w-[150px]">
{rescanInfo?.rescanning === true ? (
<div className="cursor-wait motion-safe:animate-pulse">
{rescanInfo.progress !== undefined
? t('navbar.text_rescan_in_progress_with_progress', {
progress: Math.floor(rescanInfo.progress * 100),
})
: t('navbar.text_rescan_in_progress')}
{rescanInfo.progressInPercentage === undefined
? t('navbar.text_rescan_in_progress')
: t('navbar.text_rescan_in_progress_with_progress', {
progress: rescanInfo.progressInPercentage,
})}
</div>
) : (
<>

View file

@ -87,7 +87,7 @@ const renderPage = async () => {
describe('RescanChainPage', () => {
beforeEach(() => {
vi.clearAllMocks()
rescanInfo = { updatedAt: 0, rescanning: false, progress: undefined }
rescanInfo = { updatedAt: 0, rescanning: false, progress: undefined, progressInPercentage: undefined }
})
it('renders the form and navigates back', async () => {
@ -105,7 +105,7 @@ describe('RescanChainPage', () => {
})
it('shows the in-progress alert with progress', async () => {
rescanInfo = { updatedAt: 0, rescanning: true, progress: 42 }
rescanInfo = { updatedAt: 0, rescanning: true, progress: 0.4221, progressInPercentage: '42.2' }
await renderPage()
expect(screen.getByText(/app.alert_rescan_in_progress_with_progress/)).toBeInTheDocument()
})
@ -113,15 +113,15 @@ describe('RescanChainPage', () => {
it('submits a valid block height through the mutation', async () => {
await renderPage()
const input = screen.getByPlaceholderText('rescan_chain.placeholder_blockheight')
fireEvent.change(input, { target: { value: '700000' } })
fireEvent.change(input, { target: { value: String(700_000) } })
fireEvent.submit(input.closest('form')!)
await waitFor(() => expect(mutateAsync).toHaveBeenCalledWith(700000))
await waitFor(() => expect(mutateAsync).toHaveBeenCalledWith(700_000))
})
it('mutationFn calls the rescan API and returns data', async () => {
await renderPage()
await expect(mutationConfig.mutationFn(700000)).resolves.toBe('ok')
await expect(mutationConfig.mutationFn(700_000)).resolves.toBe('ok')
expect(rescanblockchainMock).toHaveBeenCalled()
})

View file

@ -124,7 +124,6 @@ export const RescanChainPage = ({ walletFileName }: RescanChainProps) => {
setRescanInfo({
updatedAt: Date.now(),
rescanning: true,
progress: undefined,
})
},
onError: (error: unknown) => {
@ -133,7 +132,6 @@ export const RescanChainPage = ({ walletFileName }: RescanChainProps) => {
setRescanInfo({
updatedAt: Date.now(),
rescanning: false,
progress: undefined,
})
const reason = getErrorReason(error, t('global.errors.reason_unknown'))
@ -181,10 +179,10 @@ export const RescanChainPage = ({ walletFileName }: RescanChainProps) => {
<div className="flex min-w-0 items-start gap-2">
<RefreshCwIcon className="mt-0.5 h-4 w-4 shrink-0 animate-spin motion-reduce:hidden" />
<span className="min-w-0 text-sm break-words">
{rescanInfo?.progress === undefined
{rescanInfo?.progressInPercentage === undefined
? t('app.alert_rescan_in_progress')
: t('app.alert_rescan_in_progress_with_progress', {
progress: rescanInfo.progress,
progress: rescanInfo.progressInPercentage,
})}
</span>
</div>

View file

@ -1,11 +1,13 @@
import { createContext, useContext, type Dispatch, type SetStateAction } from 'react'
import type { SendFormValues } from '@/components/send/types'
import type { WalletFileName } from '@/lib/utils'
import type { Factor } from '@/types/global'
export interface RescanInfo {
updatedAt: number
rescanning: boolean
progress?: number
progress?: Factor
progressInPercentage?: string
}
export interface PaymentAttempt {

View file

@ -7,7 +7,7 @@ import { createJSONStorage, persist } from 'zustand/middleware'
import { JAM_RESCAN_PROGRESS_INTERVAL } from '@/constants/jam'
import { useApiClient } from '@/hooks/useApiClient'
import { withQueryDelay } from '@/lib/queryClient'
import type { WalletFileName } from '@/lib/utils'
import { factorToPercentage, type WalletFileName } from '@/lib/utils'
import { jmSessionStore } from '@/store/jmSessionStore'
import { JamSessionInfoContext } from './JamSessionInfoContext'
import type { PaymentAttempt, RescanInfo, TakerInfo } from './JamSessionInfoContext'
@ -92,10 +92,12 @@ export const JamSessionInfoContextProvider = ({
const isRescanning = getrescaninfoQuery.data.rescanning || state?.rescanning === true
const rescanningFinished = getrescaninfoQuery.data.rescanning === false && state?.rescanning === true
const progress = rescanningFinished ? 1 : getrescaninfoQuery.data.progress
setRescanInfo({
updatedAt: getrescaninfoQuery.dataUpdatedAt,
rescanning: isRescanning,
progress: rescanningFinished ? 1 : getrescaninfoQuery.data.progress,
progress: progress,
progressInPercentage: progress === undefined ? undefined : factorToPercentage(progress).toFixed(1),
})
}

View file

@ -1,7 +1,7 @@
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
import { JM_WALLET_FILE_EXTENSION, type OfferType } from '@/constants/jm'
import type { Milliseconds, MnemonicPhrase } from '@/types/global'
import type { Factor, Milliseconds, MnemonicPhrase } from '@/types/global'
const HORIZONTAL_ELLIPSIS = '\u2026' // Horizontal Ellipsis `…`
@ -145,14 +145,6 @@ export const BITCOIN_GENESIS_DATE = new Date('2009-01-03T18:15:05Z')
export const DUMMY_SEED_PHRASE: MnemonicPhrase =
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'.split(' ')
export const percentageToFactorString = (val: number, precision = 6) => {
return (val / 100).toFixed(precision)
}
export const percentageToFactor = (val: number, precision = 6) => {
return Number(percentageToFactorString(val, precision))
}
export const isValidNumber = (val: unknown): val is number => {
return Number.isFinite(val)
}
@ -215,13 +207,21 @@ export const formatSats = (value: number) => {
})
}
export const factorToPercentage = (val: number, precision = 6) => {
export const factorToPercentage = (val: Factor, precision = 6) => {
// Value cannot just be multiplied
// e.g. ✗ 0.000027 * 100 == 0.0026999999999999997
// but: ✓ Number((0.000027 * 100).toFixed(6)) = 0.0027
return Number((val * 100).toFixed(precision))
}
export const percentageToFactorString = (val: number, precision = 6) => {
return (val / 100).toFixed(precision)
}
export const percentageToFactor = (val: number, precision = 6): Factor => {
return Number(percentageToFactorString(val, precision))
}
export type SemanticVersion = { major: number; minor: number; patch: number; raw?: string }
export const UNKNOWN_VERSION: SemanticVersion = { major: 0, minor: 0, patch: 0, raw: 'unknown' }