Skip to content

Commit f68b8cf

Browse files
authored
Testing feedback Fixes (#346)
* fix: default values * fix: unlock warning intermittently * fix: cancel deep scan issues * fix: card sizing * fix: dropdown svgs * fix: app count mismatch * improvements * update deps and fix vitest
1 parent c5d8cda commit f68b8cf

File tree

9 files changed

+985
-1215
lines changed

9 files changed

+985
-1215
lines changed

components/sections/migrate/__tests__/deep-scan-modal.test.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,16 +154,16 @@ describe('DeepScanModal', () => {
154154
it('should generate correct path for single account + single address', () => {
155155
render(<DeepScanModal isOpen={true} onClose={mockOnClose} onScan={mockOnScan} />)
156156

157-
// Check initial derivation path (defaults: account 1, address 0)
158-
const pathDisplay = screen.getByText(/m\/44'\/354'\/1'\/0'\/0'/i)
157+
// Check initial derivation path (defaults: account 0, address 0)
158+
const pathDisplay = screen.getByText(/m\/44'\/354'\/0'\/0'\/0'/i)
159159
expect(pathDisplay).toBeInTheDocument()
160160
})
161161

162162
it('should update path when account index changes', () => {
163163
render(<DeepScanModal isOpen={true} onClose={mockOnClose} onScan={mockOnScan} />)
164164

165165
// Find and update account index input
166-
const accountInput = screen.getByDisplayValue('1') as HTMLInputElement
166+
const accountInput = screen.getByLabelText('Account Index') as HTMLInputElement
167167
fireEvent.change(accountInput, { target: { value: '5' } })
168168

169169
// Check updated derivation path
@@ -179,7 +179,7 @@ describe('DeepScanModal', () => {
179179
fireEvent.change(addressInput, { target: { value: '3' } })
180180

181181
// Check updated derivation path
182-
const pathDisplay = screen.getByText(/m\/44'\/354'\/1'\/0'\/3'/i)
182+
const pathDisplay = screen.getByText(/m\/44'\/354'\/0'\/0'\/3'/i)
183183
expect(pathDisplay).toBeInTheDocument()
184184
})
185185

@@ -199,7 +199,7 @@ describe('DeepScanModal', () => {
199199
// Wait for re-render
200200
await waitFor(() => {
201201
// Check for range notation in path
202-
expect(screen.getByText(/m\/44'\/354'\/\{1\.\.\.5\}'\/0'\/\{0\.\.\.5\}'/i)).toBeInTheDocument()
202+
expect(screen.getByText(/m\/44'\/354'\/\{0\.\.\.5\}'\/0'\/\{0\.\.\.5\}'/i)).toBeInTheDocument()
203203
})
204204
})
205205

@@ -361,7 +361,7 @@ describe('DeepScanModal', () => {
361361
fireEvent.click(addressRangeTab)
362362

363363
await waitFor(() => {
364-
const accountInput = screen.getByDisplayValue('1') as HTMLInputElement
364+
const accountInput = screen.getByLabelText('Account Index') as HTMLInputElement
365365
fireEvent.change(accountInput, { target: { value: '3' } })
366366

367367
const startInputs = screen.getAllByLabelText('Start Index')

components/sections/migrate/__tests__/ledger-unlock-reminder.test.tsx

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,40 +38,27 @@ describe('LedgerUnlockReminder', () => {
3838
expect(screen.getByText('Keep your Ledger device unlocked')).toBeInTheDocument()
3939
})
4040

41-
it('should hide reminder after display timeout', async () => {
41+
it('should keep reminder visible after it appears', async () => {
4242
render(<LedgerUnlockReminder isVisible={true} />)
4343

44-
// Fast-forward to when reminder appears
45-
act(() => {
46-
vi.advanceTimersByTime(50000) // 50 seconds
47-
})
48-
expect(screen.getByText('Keep your Ledger device unlocked')).toBeInTheDocument()
49-
50-
// Fast-forward by the display time (10 seconds)
51-
act(() => {
52-
vi.advanceTimersByTime(10000)
53-
})
44+
// Initially should not be visible
5445
expect(screen.queryByText('Keep your Ledger device unlocked')).not.toBeInTheDocument()
55-
})
56-
57-
it('should show reminder repeatedly at intervals', async () => {
58-
render(<LedgerUnlockReminder isVisible={true} />)
5946

60-
// First reminder at 50 seconds
47+
// Fast-forward to when reminder appears (50 seconds)
6148
act(() => {
6249
vi.advanceTimersByTime(50000)
6350
})
6451
expect(screen.getByText('Keep your Ledger device unlocked')).toBeInTheDocument()
6552

66-
// Hide after 10 seconds
53+
// Fast-forward by 10 seconds (old behavior would hide it here)
6754
act(() => {
6855
vi.advanceTimersByTime(10000)
6956
})
70-
expect(screen.queryByText('Keep your Ledger device unlocked')).not.toBeInTheDocument()
57+
expect(screen.getByText('Keep your Ledger device unlocked')).toBeInTheDocument()
7158

72-
// Second reminder should appear after another 60 seconds (total 120 seconds)
59+
// Fast-forward by much longer time - should still be visible
7360
act(() => {
74-
vi.advanceTimersByTime(60000)
61+
vi.advanceTimersByTime(120000) // 2 more minutes
7562
})
7663
expect(screen.getByText('Keep your Ledger device unlocked')).toBeInTheDocument()
7764
})

components/sections/migrate/deep-scan-modal.tsx

Lines changed: 30 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import { use$ } from '@legendapp/state/react'
44
import { AlertCircle, Loader2 } from 'lucide-react'
5-
import Image from 'next/image'
65
import { useMemo, useState } from 'react'
76
import { AppStatus } from 'state/ledger'
87
import { uiState$ } from 'state/ui'
@@ -15,15 +14,14 @@ import { Progress } from '@/components/ui/progress'
1514
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
1615
import type { AppId } from '@/config/apps'
1716
import { appsConfigs, getChainName, polkadotAppConfig } from '@/config/apps'
17+
import type { RangeField, ScanType } from '@/lib/types/scan'
18+
import { RangeFieldEnum, SCAN_LIMITS, ScanTypeEnum } from '@/lib/types/scan'
1819
import { cn, getAppTotalAccounts } from '@/lib/utils'
20+
import { formatIndexDisplay, parseIndexConfig, validateIndexConfig } from '@/lib/utils/scan-indices'
1921
import type { App } from '@/state/ledger'
2022
import { IndexInputSection } from './index-input-section'
2123
import { LedgerUnlockReminder } from './ledger-unlock-reminder'
2224

23-
import type { ScanType, RangeField } from '@/lib/types/scan'
24-
import { ScanTypeEnum, RangeFieldEnum } from '@/lib/types/scan'
25-
import { parseIndexConfig, validateIndexConfig, formatIndexDisplay } from '@/lib/utils/scan-indices'
26-
2725
interface DeepScanModalProps {
2826
isOpen: boolean
2927
onClose: () => void
@@ -66,14 +64,15 @@ export function DeepScanModal({
6664
onCancel,
6765
onDone,
6866
}: DeepScanModalProps) {
67+
const icons = use$(uiState$.icons)
6968
const [accountScanType, setAccountScanType] = useState<ScanType>(ScanTypeEnum.SINGLE)
7069
const [addressScanType, setAddressScanType] = useState<ScanType>(ScanTypeEnum.SINGLE)
71-
const [accountIndex, setAccountIndex] = useState<string>('1')
72-
const [accountStartIndex, setAccountStartIndex] = useState<string>('1')
73-
const [accountEndIndex, setAccountEndIndex] = useState<string>('5')
74-
const [addressIndex, setAddressIndex] = useState<string>('0')
75-
const [addressStartIndex, setAddressStartIndex] = useState<string>('0')
76-
const [addressEndIndex, setAddressEndIndex] = useState<string>('5')
70+
const [accountIndex, setAccountIndex] = useState<string>(SCAN_LIMITS.DEFAULT_SINGLE.toString())
71+
const [accountStartIndex, setAccountStartIndex] = useState<string>(SCAN_LIMITS.DEFAULT_RANGE_START.toString())
72+
const [accountEndIndex, setAccountEndIndex] = useState<string>(SCAN_LIMITS.DEFAULT_RANGE_END.toString())
73+
const [addressIndex, setAddressIndex] = useState<string>(SCAN_LIMITS.DEFAULT_SINGLE.toString())
74+
const [addressStartIndex, setAddressStartIndex] = useState<string>(SCAN_LIMITS.DEFAULT_RANGE_START.toString())
75+
const [addressEndIndex, setAddressEndIndex] = useState<string>(SCAN_LIMITS.DEFAULT_RANGE_END.toString())
7776
const [selectedChain, setSelectedChain] = useState<AppId | 'all'>('all')
7877

7978
// Get available chains from config
@@ -216,7 +215,7 @@ export function DeepScanModal({
216215
{/* Scanning Apps Grid */}
217216
{scanningApps && scanningApps.length > 0 && (
218217
<div className="pt-2 mb-4">
219-
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-3">
218+
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-3">
220219
{scanningApps.map(app => (
221220
<DeepScanAppItem key={app.id} app={app} />
222221
))}
@@ -274,18 +273,22 @@ export function DeepScanModal({
274273

275274
return (
276275
<CustomTooltip tooltipBody={statusText}>
277-
<div className={cn('flex flex-col items-center p-4 rounded-lg border transition-all min-h-[80px]', statusClass)}>
276+
<div className={cn('flex flex-col items-center p-3 rounded-lg border transition-all', statusClass)}>
278277
<div className="relative mb-2">
279278
<TokenIcon icon={icon} symbol={appName.substring(0, 3)} size="md" />
280279
{displayBadge && (
281280
<div className="absolute -right-2 -bottom-2">
282-
<Badge variant="outline" className="bg-white h-5 min-w-5 px-0 justify-center rounded-full text-xs">
281+
<Badge
282+
variant="outline"
283+
className="bg-white h-5 min-w-5 px-0 justify-center rounded-full text-xs"
284+
data-testid="app-sync-badge"
285+
>
283286
{statusIcon}
284287
</Badge>
285288
</div>
286289
)}
287290
</div>
288-
<span className="text-xs font-medium text-center leading-tight">{appName}</span>
291+
<span className="text-xs font-medium truncate max-w-full">{appName}</span>
289292
</div>
290293
</CustomTooltip>
291294
)
@@ -336,37 +339,18 @@ export function DeepScanModal({
336339
<span>All Chains</span>
337340
</div>
338341
</SelectItem>
339-
{availableChains.map(chain => (
340-
<SelectItem key={chain.id} value={chain.id}>
341-
<div className="flex items-center gap-2">
342-
{chain.token.logoId ? (
343-
<Image
344-
src={`/images/logos/${chain.token.logoId}.svg`}
345-
alt={chain.name}
346-
width={20}
347-
height={20}
348-
className="w-5 h-5"
349-
onError={e => {
350-
e.currentTarget.style.display = 'none'
351-
}}
352-
/>
353-
) : (
354-
<Image
355-
src={`/images/logos/${chain.id}.svg`}
356-
alt={chain.name}
357-
width={20}
358-
height={20}
359-
className="w-5 h-5"
360-
onError={e => {
361-
e.currentTarget.style.display = 'none'
362-
}}
363-
/>
364-
)}
365-
<span>{chain.name}</span>
366-
<span className="text-xs text-gray-500">({chain.token.symbol})</span>
367-
</div>
368-
</SelectItem>
369-
))}
342+
{availableChains.map(chain => {
343+
const icon = icons[chain.id]
344+
return (
345+
<SelectItem key={chain.id} value={chain.id}>
346+
<div className="flex items-center gap-2">
347+
<TokenIcon icon={icon} symbol={chain.name.substring(0, 3)} size="sm" />
348+
<span>{chain.name}</span>
349+
<span className="text-xs text-gray-500">({chain.token.symbol})</span>
350+
</div>
351+
</SelectItem>
352+
)
353+
})}
370354
</SelectContent>
371355
</Select>
372356
</div>

components/sections/migrate/ledger-unlock-reminder.tsx

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,38 +13,36 @@ const LEDGER_MINIMUM_UNLOCK_REMINDER_INTERVAL = 60000
1313

1414
const LEDGER_UNLOCK_REMINDER_INTERVAL = LEDGER_MINIMUM_UNLOCK_REMINDER_INTERVAL - 10000 // Show 10 seconds before 1 minute
1515

16-
// Time the reminder is shown on screen (10 seconds in milliseconds)
17-
const LEDGER_UNLOCK_REMINDER_TIME_ON_SCREEN = 10000
18-
1916
export function LedgerUnlockReminder({ isVisible }: LedgerUnlockReminderProps) {
2017
const [showReminder, setShowReminder] = useState(false)
18+
const [hasShownOnce, setHasShownOnce] = useState(false)
2119

2220
useEffect(() => {
2321
if (!isVisible) {
2422
setShowReminder(false)
23+
setHasShownOnce(false)
2524
return
2625
}
2726

2827
// Show initial reminder at 50 seconds (10 seconds before 1 minute)
2928
const initialTimer = setTimeout(() => {
3029
setShowReminder(true)
31-
// Hide after 10 seconds
32-
setTimeout(() => setShowReminder(false), LEDGER_UNLOCK_REMINDER_TIME_ON_SCREEN)
30+
setHasShownOnce(true)
31+
// Keep it persistent after first appearance
3332
}, LEDGER_UNLOCK_REMINDER_INTERVAL)
3433

35-
// Show reminder every minute after the initial one
36-
const intervalTimer = setInterval(() => {
37-
setShowReminder(true)
38-
// Hide after 10 seconds
39-
setTimeout(() => setShowReminder(false), LEDGER_UNLOCK_REMINDER_TIME_ON_SCREEN)
40-
}, LEDGER_MINIMUM_UNLOCK_REMINDER_INTERVAL)
41-
4234
return () => {
4335
clearTimeout(initialTimer)
44-
clearInterval(intervalTimer)
4536
}
4637
}, [isVisible])
4738

39+
// Once shown, keep it visible while isVisible is true
40+
useEffect(() => {
41+
if (isVisible && hasShownOnce) {
42+
setShowReminder(true)
43+
}
44+
}, [isVisible, hasShownOnce])
45+
4846
if (!showReminder) return null
4947

5048
return (

0 commit comments

Comments
 (0)