From 2d97fbf1bce0d873146b35a52b8397614abd8e44 Mon Sep 17 00:00:00 2001 From: Jakob Wennberg Date: Fri, 14 Aug 2026 10:04:36 +0200 Subject: [PATCH] fix: false popup-blocked toast on Visa dokument + scope SKV reconnect line to skattekonto source (#1613) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(documents): stop false popup-blocked toast on Visa dokument window.open() returns null BY SPEC when 'noopener' is in the features string, even when the tab opens, so the destructive 'Tillåt popupfönster' toast fired on every successful open. Open without the features string and sever the reverse channel manually (tab.opener = null), the same pattern lib/browser/deferred-tab.ts already uses; the toast now fires only on a genuine popup block. Co-Authored-By: Claude Fable 5 * fix(transactions): scope the SKV reconnect line to the skattekonto source The reconnect attn line rendered on /transactions whenever the SKV connection needed renewal, regardless of what the user was looking at, so it read as permanent noise. It now shows only when the source picker is on Skatteverket (the rows it actually explains); the skattekonto page keeps its own reconnect line. Co-Authored-By: Claude Fable 5 * fix(transactions): keep Skatteverket source pickable while reconnect is needed In the reconnect-needed state the transaktioner fetch 401s, skvRows goes empty, the Skatteverket option left the source picker, and the stale-filter effect reset the filter to 'all': the source-gated reconnect line became unreachable exactly when it applied. Show the source whenever rows exist OR reconnect is needed (skvNeedsReconnect already requires connected=true, so never-connected companies get no phantom source). No component test: repo test scope is lib/ + app/api/ (no component tests). Co-Authored-By: Claude Fable 5 --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 --- app/(dashboard)/transactions/page.tsx | 13 ++++++++++--- components/bookkeeping/DocumentViewButton.tsx | 9 ++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/(dashboard)/transactions/page.tsx b/app/(dashboard)/transactions/page.tsx index 0e76da7a..0a3b0f2f 100644 --- a/app/(dashboard)/transactions/page.tsx +++ b/app/(dashboard)/transactions/page.tsx @@ -745,7 +745,11 @@ export default function TransactionsPage() { ) const sourceItems = useMemo(() => { - const showSkvSource = skvRows.length > 0 + // Keep the Skatteverket source pickable while reconnect is needed even + // though the rows fetch fails then (401 -> skvRows []): the reconnect + // attn line only renders under this source, so dropping the option would + // hide the reconnect path exactly when it applies. + const showSkvSource = skvRows.length > 0 || skvNeedsReconnect const items: ContextPickerItem[] = [ { id: 'all', @@ -771,7 +775,7 @@ export default function TransactionsPage() { items.push({ id: 'skatteverket', label: t('source_skatteverket_label') }) } return items - }, [cashAccounts, hasUnassignedBankRows, skvRows.length, t, totalSourceBalance]) + }, [cashAccounts, hasUnassignedBankRows, skvNeedsReconnect, skvRows.length, t, totalSourceBalance]) // A narrowed filter can go stale (account disabled, skv rows drained, // "övriga" bucket emptied): fall back to everything rather than filtering @@ -3161,7 +3165,10 @@ export default function TransactionsPage() { setIsDialogOpen(true)} /> - {skvNeedsReconnect ? ( + {skvNeedsReconnect && sourceFilter === 'skatteverket' ? ( + // Only when the user is actually looking at skattekonto rows: as a + // permanent page-wide line it read as noise (feedback 2026-08-14). + // The skattekonto page keeps its own reconnect line. {t('skv_reconnect_body')} diff --git a/components/bookkeeping/DocumentViewButton.tsx b/components/bookkeeping/DocumentViewButton.tsx index c61f6552..3c3ec14f 100644 --- a/components/bookkeeping/DocumentViewButton.tsx +++ b/components/bookkeeping/DocumentViewButton.tsx @@ -40,7 +40,14 @@ export function DocumentViewButton({ documentId, label = 'Visa dokument', classN return } - if (!window.open(`/api/documents/${documentId}/inline`, '_blank', 'noopener,noreferrer')) { + // window.open() returns null BY SPEC when 'noopener' is in the features + // string, even on success, so passing it here made this toast fire on + // every successful open. Open with a real return value and sever the + // reverse channel manually (same pattern as lib/browser/deferred-tab.ts). + const tab = window.open(`/api/documents/${documentId}/inline`, '_blank') + if (tab) { + tab.opener = null + } else { toast({ title: 'Kunde inte öppna dokumentet', description: 'Tillåt popupfönster för Accounted i webbläsaren och försök igen.',