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

fix: sync URL to active view and settings sub-tab

This commit is contained in:
TheGeneralist 2026-07-01 14:43:41 +02:00
parent c4f1c7a137
commit 44f67390be
Signed by: thegeneralist01
SSH key fingerprint: SHA256:pp9qddbCNmVNoSjevdvQvM5z0DHN7LTa8qBMbcMq/R4
5 changed files with 85 additions and 48 deletions

View file

@ -15,6 +15,22 @@ import ContextRail from './components/ContextRail'
export const AuthContext = createContext(null);
const VIEWS = ['archive','runs','admin','tags','collections','settings']
const SETTINGS_TABS = ['profile','tokens','instance']
function parseLocation() {
const parts = window.location.pathname.split('/').filter(Boolean)
const view = VIEWS.includes(parts[0]) ? parts[0] : 'archive'
const settingsTab = (view === 'settings' && SETTINGS_TABS.includes(parts[1])) ? parts[1] : 'profile'
return { view, settingsTab }
}
function locationPath(view, settingsTab) {
if (view === 'archive') return '/'
if (view === 'settings') return `/settings/${settingsTab}`
return `/${view}`
}
export default function App() {
const [authState, setAuthState] = useState('loading');
const [currentUser, setCurrentUser] = useState(null);
@ -36,13 +52,25 @@ export default function App() {
return () => window.removeEventListener('auth:expired', handler);
}, []);
// Sync URL state on back/forward
useEffect(() => {
const handler = () => {
const { view, settingsTab } = parseLocation()
setView(view)
setSettingsTab(settingsTab)
}
window.addEventListener('popstate', handler)
return () => window.removeEventListener('popstate', handler)
}, [])
const [archives, setArchives] = useState([])
const [archiveId, setArchiveId] = useState(null)
const [entries, setEntries] = useState([])
const [selectedEntryUid, setSelectedEntryUid] = useState(null)
const [selectedEntry, setSelectedEntry] = useState(null)
const [tagFilter, setTagFilter] = useState(null)
const [view, setView] = useState('archive')
const [view, setView] = useState(() => parseLocation().view)
const [settingsTab, setSettingsTab] = useState(() => parseLocation().settingsTab)
const [searchQuery, setSearchQuery] = useState('')
const [resultCount, setResultCount] = useState('')
const [searchBusy, setSearchBusy] = useState(false)
@ -105,10 +133,12 @@ export default function App() {
return () => clearTimeout(timer)
}, [searchQuery, archiveId]) // tagFilter handled separately below
// Tag filter change: switch to archive view, reload
// Tag filter applied: switch to archive view and reload.
// Only reset view when tagFilter is non-null; archive change alone (tagFilter=null)
// must not stomp the URL-initialised view on first load.
useEffect(() => {
if (archiveId === null) return
setView('archive')
if (tagFilter !== null) setView('archive')
loadEntries(archiveId, searchQuery, tagFilter)
}, [tagFilter, archiveId]) // intentional: searchQuery excluded to avoid double-fire
@ -123,6 +153,14 @@ export default function App() {
}
}, [archiveId])
// Sync view + settingsTab URL
useEffect(() => {
const path = locationPath(view, settingsTab)
if (window.location.pathname !== path) {
history.pushState(null, '', path)
}
}, [view, settingsTab])
const selectEntry = useCallback((entry) => {
setSelectedEntryUid(entry ? entry.entry_uid : null)
setSelectedEntry(entry)
@ -230,7 +268,7 @@ export default function App() {
<CollectionsView archiveId={archiveId} />
)}
{view === 'settings' && (
<SettingsView />
<SettingsView tab={settingsTab} onTabChange={setSettingsTab} />
)}
</div>
<ContextRail

View file

@ -8,10 +8,9 @@ import {
const ROLE_ADMIN = 4
export default function SettingsView() {
export default function SettingsView({ tab, onTabChange }) {
const { currentUser, setCurrentUser } = useContext(AuthContext) ?? {}
const isAdmin = currentUser && ((currentUser.role_bits & ROLE_ADMIN) !== 0)
const [tab, setTab] = useState('profile')
const tabs = ['profile', 'tokens', ...(isAdmin ? ['instance'] : [])]
const tabLabels = { profile: 'Profile', tokens: 'API Tokens', instance: 'Instance' }
@ -23,7 +22,7 @@ export default function SettingsView() {
{tabs.map(t => (
<button key={t}
className={`view-tab${tab === t ? ' is-active' : ''}`}
onClick={() => setTab(t)}>
onClick={() => onTabChange(t)}>
{tabLabels[t]}
</button>
))}