1
Fork 0
mirror of https://github.com/thegeneralist01/archivr synced 2026-07-21 18:55:36 +02:00

fix(frontend): URL state regressions from Codex review

Two issues:

1. Re-selection stall when popstate lands on the same entry UID.
   The restoration effect only had [entries, selectedEntryUid] as
   deps, so setting selectedEntry=null without changing either dep
   left the rail blank. Adding selectedEntry to the dep array lets
   the null→restore cycle complete.

2. tag/entry params leaking onto non-archive URLs.
   pushState on view changes carried window.location.search verbatim,
   so /settings?tag=foo was possible; reloading it triggered the
   tagFilter effect and force-switched back to archive.
   Fix: parseLocation() only extracts tag/entry when view=archive,
   and the params-sync effect only emits them on archive too.
This commit is contained in:
TheGeneralist 2026-07-13 14:48:15 +02:00
parent 5bca1a5d6d
commit b2e5ad863d
Signed by: thegeneralist01
SSH key fingerprint: SHA256:pp9qddbCNmVNoSjevdvQvM5z0DHN7LTa8qBMbcMq/R4
3 changed files with 8 additions and 8 deletions

View file

@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Archivr</title>
<script type="module" crossorigin src="/assets/index-BZnJs_tc.js"></script>
<script type="module" crossorigin src="/assets/index-u6DliWTb.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-p2mzAHzf.css">
</head>
<body>

View file

@ -35,8 +35,8 @@ function parseLocation() {
const settingsTab = (view === 'settings' && SETTINGS_TABS.includes(parts[1])) ? parts[1] : 'profile'
const params = new URLSearchParams(window.location.search)
const q = params.get('q') ?? ''
const tag = params.get('tag') ?? null
const entry = params.get('entry') ?? null
const tag = view === 'archive' ? (params.get('tag') ?? null) : null
const entry = view === 'archive' ? (params.get('entry') ?? null) : null
return { view, settingsTab, q, tag, entry }
}
@ -293,20 +293,20 @@ export default function App() {
if (!selectedEntryUid || selectedEntry) return
const found = entries.find(e => e.entry_uid === selectedEntryUid)
if (found) setSelectedEntry(found)
}, [entries, selectedEntryUid])
}, [entries, selectedEntryUid, selectedEntry])
// Sync search params URL via replaceState (no new history entry).
useEffect(() => {
if (PREVIEW_ROUTE) return
const params = new URLSearchParams()
if (searchQuery) params.set('q', searchQuery)
if (tagFilter) params.set('tag', tagFilter)
if (selectedEntryUid) params.set('entry', selectedEntryUid)
if (view === 'archive' && tagFilter) params.set('tag', tagFilter)
if (view === 'archive' && selectedEntryUid) params.set('entry', selectedEntryUid)
const qs = params.toString()
const url = window.location.pathname + (qs ? '?' + qs : '')
const current = window.location.pathname + window.location.search
if (current !== url) history.replaceState(null, '', url)
}, [searchQuery, tagFilter, selectedEntryUid])
}, [searchQuery, tagFilter, selectedEntryUid, view])
// K / Ctrl+K: focus the search input, switching to archive view first if needed.
useEffect(() => {