fix(search): link results to book detail page and add cover thumbnails
Release / build-and-push (push) Successful in 2m24s
Release / build-and-push (push) Successful in 2m24s
Search results navigated to /bookshelf with no filters instead of the selected book's page. Results now link to /media/:id and display cover thumbnails, with cover URLs resolved server-side via ResolveMediaURL. Removes the dead selectedBook localStorage plumbing.
This commit is contained in:
@@ -1791,6 +1791,10 @@ func (mh *MediaHandler) SearchMediaItems(c *echo.Context) error {
|
|||||||
"results": []interface{}{},
|
"results": []interface{}{},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
for i := range results {
|
||||||
|
resolved := utils.ResolveMediaURL(results[i].LibraryID, results[i].CoverImagePath)
|
||||||
|
results[i].CoverImagePath = pgtype.Text{String: resolved, Valid: resolved != ""}
|
||||||
|
}
|
||||||
return c.JSON(http.StatusOK, results)
|
return c.JSON(http.StatusOK, results)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-20
@@ -1,5 +1,4 @@
|
|||||||
import { Alpine } from "./alpine";
|
import { Alpine } from "./alpine";
|
||||||
import { setSelectedLibrary } from "./storage";
|
|
||||||
|
|
||||||
let searchInputTimeout: ReturnType<typeof setTimeout> | null = null;
|
let searchInputTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||||
const SEARCH_DEBOUNCE_MS = 300;
|
const SEARCH_DEBOUNCE_MS = 300;
|
||||||
@@ -190,12 +189,6 @@ function showSearchResults(results: MediaItemSummary[], query: string, activeId?
|
|||||||
|
|
||||||
searchResults.dataset.selectedIndex = "-1";
|
searchResults.dataset.selectedIndex = "-1";
|
||||||
|
|
||||||
const libraryIconMap: Record<string, string> = {
|
|
||||||
ebooks: "📚",
|
|
||||||
comics: "📖",
|
|
||||||
manga: "🗾",
|
|
||||||
};
|
|
||||||
|
|
||||||
let html = `
|
let html = `
|
||||||
<div class="p-3 border-b" style="border-color: var(--border)">
|
<div class="p-3 border-b" style="border-color: var(--border)">
|
||||||
<p class="text-xs font-semibold uppercase tracking-wide" style="color: var(--text-secondary)">
|
<p class="text-xs font-semibold uppercase tracking-wide" style="color: var(--text-secondary)">
|
||||||
@@ -206,19 +199,23 @@ function showSearchResults(results: MediaItemSummary[], query: string, activeId?
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
results.forEach((item, index) => {
|
results.forEach((item, index) => {
|
||||||
const icon = libraryIconMap[item.library_type_name] || "📁";
|
|
||||||
const titleHtml = highlightMatch(item.title, query);
|
const titleHtml = highlightMatch(item.title, query);
|
||||||
const authorHtml = item.author ? highlightMatch(item.author, query) : "";
|
const authorHtml = item.author ? highlightMatch(item.author, query) : "";
|
||||||
|
const coverUrl = item.cover_image_path || "/static/placeholder-book.svg";
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div class="search-result-item p-3 border-b hover:bg-opacity-50 transition-colors cursor-pointer"
|
<div class="search-result-item p-3 border-b hover:bg-opacity-50 transition-colors cursor-pointer"
|
||||||
style="border-color: var(--border); background-color: var(--bg-secondary)"
|
style="border-color: var(--border); background-color: var(--bg-secondary)"
|
||||||
data-index="${index}">
|
data-index="${index}">
|
||||||
<a href="/bookshelf"
|
<a href="/media/${item.id}" class="block">
|
||||||
class="block"
|
|
||||||
onclick="window.selectLibraryAndBook('${item.library_id}', '${item.id}')">
|
|
||||||
<div class="flex items-start space-x-3">
|
<div class="flex items-start space-x-3">
|
||||||
<div class="text-2xl">${icon}</div>
|
<div class="w-10 h-14 flex-shrink-0 rounded overflow-hidden bg-gradient-to-br from-gray-700 to-gray-900">
|
||||||
|
<img src="${coverUrl}"
|
||||||
|
alt="${searchEscapeHtml(item.title)}"
|
||||||
|
class="w-full h-full object-cover"
|
||||||
|
loading="lazy"
|
||||||
|
onerror="this.src='/static/placeholder-book.svg'">
|
||||||
|
</div>
|
||||||
<div class="flex-1 min-w-0">
|
<div class="flex-1 min-w-0">
|
||||||
<h4 class="text-sm font-medium truncate" style="color: var(--text-primary)">
|
<h4 class="text-sm font-medium truncate" style="color: var(--text-primary)">
|
||||||
${titleHtml}
|
${titleHtml}
|
||||||
@@ -323,15 +320,8 @@ function searchEscapeHtml(text: string): string {
|
|||||||
return div.innerHTML;
|
return div.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectLibraryAndBook(libraryId: string, bookId: string): void {
|
export { initializeSearch };
|
||||||
setSelectedLibrary(libraryId);
|
|
||||||
localStorage.setItem("selectedBook", bookId);
|
|
||||||
hideSearchResults();
|
|
||||||
}
|
|
||||||
|
|
||||||
export { selectLibraryAndBook, initializeSearch };
|
|
||||||
|
|
||||||
Alpine.data("search", () => ({
|
Alpine.data("search", () => ({
|
||||||
selectLibraryAndBook,
|
|
||||||
initializeSearch,
|
initializeSearch,
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -45,14 +45,6 @@ function setSelectedLibrary(libraryId: string): void {
|
|||||||
document.cookie = `selectedLibrary=${encodeURIComponent(value)};path=/;max-age=${365 * 24 * 60 * 60};samesite=lax`;
|
document.cookie = `selectedLibrary=${encodeURIComponent(value)};path=/;max-age=${365 * 24 * 60 * 60};samesite=lax`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelectedBook(): string | null {
|
|
||||||
return localStorage.getItem("selectedBook");
|
|
||||||
}
|
|
||||||
|
|
||||||
function setSelectedBook(bookId: string): void {
|
|
||||||
localStorage.setItem("selectedBook", bookId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearAll(): void {
|
function clearAll(): void {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
}
|
}
|
||||||
@@ -61,14 +53,12 @@ export {
|
|||||||
ALL_LIBRARIES,
|
ALL_LIBRARIES,
|
||||||
clearAll,
|
clearAll,
|
||||||
getRefreshToken,
|
getRefreshToken,
|
||||||
getSelectedBook,
|
|
||||||
getSelectedLibrary,
|
getSelectedLibrary,
|
||||||
getTheme,
|
getTheme,
|
||||||
getToken,
|
getToken,
|
||||||
removeRefreshToken,
|
removeRefreshToken,
|
||||||
removeToken,
|
removeToken,
|
||||||
setRefreshToken,
|
setRefreshToken,
|
||||||
setSelectedBook,
|
|
||||||
setSelectedLibrary,
|
setSelectedLibrary,
|
||||||
setTheme,
|
setTheme,
|
||||||
setToken
|
setToken
|
||||||
|
|||||||
Reference in New Issue
Block a user