refactor: extract useAppNavigation hook from App.tsx — reduce god component churn (#194)

Co-authored-by: Test <test@test.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Luca Rossi
2026-03-12 07:24:46 +01:00
committed by GitHub
parent 8137125569
commit a15f36ec6a
3 changed files with 234 additions and 52 deletions

View File

@@ -0,0 +1,89 @@
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useNavigationHistory } from './useNavigationHistory'
import { useNavigationGestures } from './useNavigationGestures'
import type { VaultEntry } from '../types'
interface TabLike {
entry: { path: string }
}
interface UseAppNavigationParams {
entries: VaultEntry[]
tabs: TabLike[]
activeTabPath: string | null
onSelectNote: (entry: VaultEntry) => void
onSwitchTab: (path: string) => void
}
/**
* Encapsulates browser-style back/forward navigation for the app:
* - Navigation history (push on tab change, back/forward traversal)
* - Mouse button & trackpad gesture bindings
* - O(1) path→entry lookup map
*/
export function useAppNavigation({
entries,
tabs,
activeTabPath,
onSelectNote,
onSwitchTab,
}: UseAppNavigationParams) {
const navHistory = useNavigationHistory()
// Push to navigation history whenever the active tab changes (user-initiated)
const navFromHistoryRef = useRef(false)
useEffect(() => {
if (activeTabPath && !navFromHistoryRef.current) {
navHistory.push(activeTabPath)
}
navFromHistoryRef.current = false
}, [activeTabPath]) // eslint-disable-line react-hooks/exhaustive-deps -- navHistory.push is stable
const isEntryExists = useCallback(
(path: string) => entries.some(e => e.path === path),
[entries],
)
const handleGoBack = useCallback(() => {
const target = navHistory.goBack(isEntryExists)
if (target) {
navFromHistoryRef.current = true
if (tabs.some(t => t.entry.path === target)) {
onSwitchTab(target)
} else {
const entry = entries.find(e => e.path === target)
if (entry) onSelectNote(entry)
}
}
}, [navHistory, isEntryExists, entries, tabs, onSelectNote, onSwitchTab])
const handleGoForward = useCallback(() => {
const target = navHistory.goForward(isEntryExists)
if (target) {
navFromHistoryRef.current = true
if (tabs.some(t => t.entry.path === target)) {
onSwitchTab(target)
} else {
const entry = entries.find(e => e.path === target)
if (entry) onSelectNote(entry)
}
}
}, [navHistory, isEntryExists, entries, tabs, onSelectNote, onSwitchTab])
useNavigationGestures({ onGoBack: handleGoBack, onGoForward: handleGoForward })
// O(1) path lookup map — rebuilt only when entries change
const entriesByPath = useMemo(() => {
const map = new Map<string, VaultEntry>()
for (const e of entries) map.set(e.path, e)
return map
}, [entries])
return {
handleGoBack,
handleGoForward,
canGoBack: navHistory.canGoBack,
canGoForward: navHistory.canGoForward,
entriesByPath,
}
}