fix: prevent bank sync loading screen from persisting forever (#201)
* fix: add rounding tolerance to INK2 balance check and clarify warnings INK2/SRU rounds each ruta independently to whole kronor, so with 11+ rutor the accumulated rounding can produce a 1-2 kr difference that triggered a false "balance sheet not in balance" warning. Add a 2 kr tolerance. Also clarify the unclosed fiscal year warning to indicate that generation still works. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: also warn when equity/liabilities exist but assets are zero Address Greptile review feedback — the balance check guard should trigger when either side has a non-zero total, not only when assets > 0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: prevent bank sync loading screen from persisting forever Replace isSyncing state guard with a ref to prevent the useEffect from re-firing when sync state changes (race condition with router.replace changing searchParams). Add AbortController with 2-minute timeout so the fetch can't hang indefinitely on slow syncs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: skip toast and state updates on unmount-abort Add unmountedRef to distinguish timeout-abort from unmount-abort. When the user navigates away mid-sync, silently bail instead of showing the misleading "took too long" toast. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
211033410c
commit
0baeea303f
@@ -20,11 +20,16 @@ export default function BankingSettingsPage() {
|
||||
const [isSyncing, setIsSyncing] = useState(false)
|
||||
const [syncResult, setSyncResult] = useState<{ imported: number } | null>(null)
|
||||
const successTimerRef = useRef<ReturnType<typeof setTimeout>>(null)
|
||||
const syncInitiatedRef = useRef(false)
|
||||
const abortControllerRef = useRef<AbortController | null>(null)
|
||||
const unmountedRef = useRef(false)
|
||||
const hasBankingExtension = ENABLED_EXTENSION_IDS.has('enable-banking')
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
unmountedRef.current = true
|
||||
if (successTimerRef.current) clearTimeout(successTimerRef.current)
|
||||
if (abortControllerRef.current) abortControllerRef.current.abort()
|
||||
}
|
||||
}, [])
|
||||
|
||||
@@ -32,19 +37,26 @@ export default function BankingSettingsPage() {
|
||||
const bankConnected = searchParams.get('bank_connected')
|
||||
const bankError = searchParams.get('bank_error')
|
||||
|
||||
if (bankConnected === 'true' && !isSyncing) {
|
||||
if (bankConnected === 'true' && !syncInitiatedRef.current) {
|
||||
syncInitiatedRef.current = true
|
||||
const connectionId = searchParams.get('connection_id')
|
||||
router.replace('/settings/banking')
|
||||
|
||||
if (connectionId) {
|
||||
setIsSyncing(true)
|
||||
const controller = new AbortController()
|
||||
abortControllerRef.current = controller
|
||||
const syncTimeout = setTimeout(() => controller.abort(), 120_000)
|
||||
|
||||
;(async () => {
|
||||
try {
|
||||
const res = await fetch('/api/extensions/ext/enable-banking/sync', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ connection_id: connectionId, days_back: 120 }),
|
||||
signal: controller.signal,
|
||||
})
|
||||
clearTimeout(syncTimeout)
|
||||
const data = await res.json()
|
||||
if (res.ok) {
|
||||
setSyncResult({ imported: data.imported ?? 0 })
|
||||
@@ -56,11 +68,20 @@ export default function BankingSettingsPage() {
|
||||
throw new Error(data.error || 'Sync failed')
|
||||
}
|
||||
} catch (err) {
|
||||
toast({
|
||||
title: 'Synkronisering misslyckades',
|
||||
description: err instanceof Error ? err.message : 'Kunde inte hämta transaktioner',
|
||||
variant: 'destructive',
|
||||
})
|
||||
clearTimeout(syncTimeout)
|
||||
if (unmountedRef.current) return
|
||||
if (controller.signal.aborted) {
|
||||
toast({
|
||||
title: 'Synkronisering tog för lång tid',
|
||||
description: 'Transaktionerna hämtas i bakgrunden. Ladda om sidan om en stund.',
|
||||
})
|
||||
} else {
|
||||
toast({
|
||||
title: 'Synkronisering misslyckades',
|
||||
description: err instanceof Error ? err.message : 'Kunde inte hämta transaktioner',
|
||||
variant: 'destructive',
|
||||
})
|
||||
}
|
||||
setIsSyncing(false)
|
||||
}
|
||||
})()
|
||||
@@ -82,7 +103,7 @@ export default function BankingSettingsPage() {
|
||||
setBankConnectionError(errorMsg)
|
||||
router.replace('/settings/banking')
|
||||
}
|
||||
}, [searchParams, router, toast, isSyncing])
|
||||
}, [searchParams, router, toast])
|
||||
|
||||
if (isSyncing) {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user