Use /search/:term path-based search routing
This commit is contained in:
+54
-23
@@ -212,11 +212,20 @@ function normalizeSearchQuery(value: unknown): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getRouteSearchQuery() {
|
function getRouteSearchQuery() {
|
||||||
return normalizeSearchQuery(route.query.q)
|
if (route.name !== "search") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return normalizeSearchQuery(route.params.term)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSearchPath(term: string) {
|
||||||
|
return router.resolve({ name: "search", params: { term } }).path
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBrowsePath() {
|
function getBrowsePath() {
|
||||||
return route.meta.view === "series" ? "/series" : "/movies"
|
if (route.meta.view === "series") return "/series"
|
||||||
|
if (route.meta.view === "movies") return "/movies"
|
||||||
|
return searchReturnPath.value ?? "/movies"
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBrowseViewFromPath(path: string): "movies" | "series" {
|
function getBrowseViewFromPath(path: string): "movies" | "series" {
|
||||||
@@ -445,6 +454,28 @@ function getSearchExitTargetFromFocusedCard(): { path: "/movies" | "/series"; it
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDetailSearchPath(): string | null {
|
||||||
|
if (!route.params.id) return null
|
||||||
|
|
||||||
|
const stateSearchPathRaw = window.history.state?.searchPath
|
||||||
|
if (typeof stateSearchPathRaw === "string") {
|
||||||
|
const stateSearchPath = normalizeHistoryPath(stateSearchPathRaw)
|
||||||
|
if (stateSearchPath.startsWith("/search/")) {
|
||||||
|
return stateSearchPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const backPath =
|
||||||
|
typeof window.history.state?.back === "string"
|
||||||
|
? normalizeHistoryPath(window.history.state.back)
|
||||||
|
: ""
|
||||||
|
if (backPath.startsWith("/search/")) {
|
||||||
|
return backPath
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
function clearSearch(options: { preferBack?: boolean; targetPath?: string } = {}) {
|
function clearSearch(options: { preferBack?: boolean; targetPath?: string } = {}) {
|
||||||
const currentQuery = getRouteSearchQuery()
|
const currentQuery = getRouteSearchQuery()
|
||||||
const targetPath = options.targetPath ?? getBrowsePath()
|
const targetPath = options.targetPath ?? getBrowsePath()
|
||||||
@@ -498,11 +529,11 @@ function updateSearchQuery(nextValue: string) {
|
|||||||
saveFocusForPage(currentView.value)
|
saveFocusForPage(currentView.value)
|
||||||
}
|
}
|
||||||
searchReturnPath.value = browsePath
|
searchReturnPath.value = browsePath
|
||||||
void router.push({ path: browsePath, query: { q: nextQuery } })
|
void router.push(getSearchPath(nextQuery))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
void router.replace({ path: browsePath, query: { q: nextQuery } })
|
void router.replace(getSearchPath(nextQuery))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle Escape key for navigation hierarchy
|
// Handle Escape key for navigation hierarchy
|
||||||
@@ -516,13 +547,13 @@ function handleEscapeKey(event: KeyboardEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const path = route.path
|
const path = route.path
|
||||||
const activeSearchQuery = getRouteSearchQuery()
|
const detailSearchPath = getDetailSearchPath()
|
||||||
|
|
||||||
if (route.params.id && activeSearchQuery) {
|
if (route.params.id && detailSearchPath) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const targetPath = getBrowsePath()
|
void router.push(detailSearchPath).finally(() => {
|
||||||
router.push({ path: targetPath, query: { q: activeSearchQuery } })
|
restoreFocusForPage("movies")
|
||||||
restoreFocusForPage(getBrowseViewFromPath(targetPath))
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,12 +613,12 @@ onUnmounted(() => {
|
|||||||
// Handle back navigation (Escape key or Back button)
|
// Handle back navigation (Escape key or Back button)
|
||||||
function goBack() {
|
function goBack() {
|
||||||
const path = route.path
|
const path = route.path
|
||||||
const activeSearchQuery = getRouteSearchQuery()
|
const detailSearchPath = getDetailSearchPath()
|
||||||
|
|
||||||
if (route.params.id && activeSearchQuery) {
|
if (route.params.id && detailSearchPath) {
|
||||||
const targetPath = getBrowsePath()
|
void router.push(detailSearchPath).finally(() => {
|
||||||
router.push({ path: targetPath, query: { q: activeSearchQuery } })
|
restoreFocusForPage("movies")
|
||||||
restoreFocusForPage(getBrowseViewFromPath(targetPath))
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,7 +646,7 @@ const currentView = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const headerCurrentView = computed<"movies" | "series" | "search">(() => {
|
const headerCurrentView = computed<"movies" | "series" | "search">(() => {
|
||||||
return selectedItem.value && searchQuery.value ? "search" : currentView.value
|
return selectedItem.value && getDetailSearchPath() ? "search" : currentView.value
|
||||||
})
|
})
|
||||||
|
|
||||||
// Header position based on current page
|
// Header position based on current page
|
||||||
@@ -688,21 +719,21 @@ function showDetail(item: MediaItem) {
|
|||||||
if (playableFile) {
|
if (playableFile) {
|
||||||
handlePlay(playableFile)
|
handlePlay(playableFile)
|
||||||
} else {
|
} else {
|
||||||
const query = searchQuery.value ? { q: searchQuery.value } : undefined
|
const searchPath = searchQuery.value ? getSearchPath(searchQuery.value) : null
|
||||||
router.push({ path: `/series/${epData.series.id}`, query })
|
router.push({ path: `/series/${epData.series.id}`, state: searchPath ? { searchPath } : undefined })
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const query = searchQuery.value ? { q: searchQuery.value } : undefined
|
const searchPath = searchQuery.value ? getSearchPath(searchQuery.value) : null
|
||||||
router.push({ path: `/${item.type}/${item.id}`, query })
|
router.push({ path: `/${item.type}/${item.id}`, state: searchPath ? { searchPath } : undefined })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close detail by navigating back to list
|
// Close detail by navigating back to list
|
||||||
function closeDetail() {
|
function closeDetail() {
|
||||||
const activeSearchQuery = getRouteSearchQuery()
|
const detailSearchPath = getDetailSearchPath()
|
||||||
const targetPath = getBrowsePath()
|
const targetPath = getBrowsePath()
|
||||||
if (activeSearchQuery) {
|
if (detailSearchPath) {
|
||||||
router.push({ path: targetPath, query: { q: activeSearchQuery } })
|
router.push(detailSearchPath)
|
||||||
} else {
|
} else {
|
||||||
router.push(targetPath)
|
router.push(targetPath)
|
||||||
}
|
}
|
||||||
@@ -1046,7 +1077,7 @@ watch(
|
|||||||
if (nextQuery !== searchQuery.value) {
|
if (nextQuery !== searchQuery.value) {
|
||||||
searchQuery.value = nextQuery
|
searchQuery.value = nextQuery
|
||||||
}
|
}
|
||||||
if (!nextQuery) {
|
if (!nextQuery && route.name !== "search") {
|
||||||
searchReturnPath.value = null
|
searchReturnPath.value = null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ const router = createRouter({
|
|||||||
component: EmptyRouteComponent,
|
component: EmptyRouteComponent,
|
||||||
meta: { view: "movies" },
|
meta: { view: "movies" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/search/:term",
|
||||||
|
name: "search",
|
||||||
|
component: EmptyRouteComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/series",
|
path: "/series",
|
||||||
name: "series",
|
name: "series",
|
||||||
|
|||||||
Reference in New Issue
Block a user