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

feat(frontend): persist selected entry in URL params

Extend parseLocation() to read ?entry= and initialise
selectedEntryUid from it on mount.

When entries load (or reload), a new effect checks whether
selectedEntryUid is set without a corresponding selectedEntry
object and restores it by finding the matching entry in the list.
This covers page refresh, URL sharing, and back/forward navigation.

The URL params sync effect now includes selectedEntryUid alongside
q and tag, and the popstate handler restores all three.

Also includes rebuilt frontend static assets.
This commit is contained in:
TheGeneralist 2026-07-13 14:28:38 +02:00
parent bb4e57e1a2
commit 6db480229c
Signed by: thegeneralist01
SSH key fingerprint: SHA256:pp9qddbCNmVNoSjevdvQvM5z0DHN7LTa8qBMbcMq/R4
4 changed files with 63 additions and 52 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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-Gm5awz10.js"></script>
<script type="module" crossorigin src="/assets/index-kKL3zajW.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-C89vW3ol.css">
</head>
<body>

View file

@ -36,7 +36,8 @@ function parseLocation() {
const params = new URLSearchParams(window.location.search)
const q = params.get('q') ?? ''
const tag = params.get('tag') ?? null
return { view, settingsTab, q, tag }
const entry = params.get('entry') ?? null
return { view, settingsTab, q, tag, entry }
}
function locationPath(view, settingsTab) {
@ -69,11 +70,12 @@ export default function App() {
// Sync URL state on back/forward
useEffect(() => {
const handler = () => {
const { view, settingsTab, q, tag } = parseLocation()
const { view, settingsTab, q, tag, entry } = parseLocation()
setView(view)
setSettingsTab(settingsTab)
setSearchQuery(q)
setTagFilter(tag)
setSelectedEntryUid(entry)
setSelectedEntry(null)
}
window.addEventListener('popstate', handler)
@ -83,7 +85,7 @@ export default function App() {
const [archives, setArchives] = useState([])
const [archiveId, setArchiveId] = useState(null)
const [entries, setEntries] = useState([])
const [selectedEntryUid, setSelectedEntryUid] = useState(null)
const [selectedEntryUid, setSelectedEntryUid] = useState(() => parseLocation().entry)
const [selectedEntry, setSelectedEntry] = useState(null)
const [tagFilter, setTagFilter] = useState(() => parseLocation().tag)
const [view, setView] = useState(() => parseLocation().view)
@ -285,17 +287,26 @@ export default function App() {
setSelectedEntryUid(prev => prev === entryUid ? null : prev)
}, [])
// Restore selectedEntry object from selectedEntryUid when entries load.
// Handles page refresh and back/forward navigation where only the UID is known.
useEffect(() => {
if (!selectedEntryUid || selectedEntry) return
const found = entries.find(e => e.entry_uid === selectedEntryUid)
if (found) setSelectedEntry(found)
}, [entries, selectedEntryUid])
// 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)
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])
}, [searchQuery, tagFilter, selectedEntryUid])
// K / Ctrl+K: focus the search input, switching to archive view first if needed.
useEffect(() => {