feat(ui): split book-card play action into reader/detail routing
The play button on book cards now opens the reader directly, instead of always going to the detail page. Cards with an active progress sync conflict route the play button to the detail page (which hosts the conflict dialogue and resolves before writing progress), so the user is never silently dropped into the reader with an unresolved conflict. Backend: - Add HasConflict to BookInfo and stamp it via ListSyncConflictsByUser (MarkActiveConflicts / MarkActiveConflictsSections) on the dashboard, bookshelf, series, tag, and search result card builders. - Each page issues a single conflict query regardless of card count. BookCard: - Restructure into a detail link (cover + meta) with the play action as a sibling overlay using a pointer-events split: the container passes clicks through to detail while only the circular button routes to the reader. No nested anchors. - On touch devices (hover: none) the play button stays visible. Fix: carousel nav buttons had opacity-0 without pointer-events-none, so they swallowed hover/clicks over book cards on the dashboard. They are now click-through until the carousel is hovered.
This commit is contained in:
+26
-13
@@ -62,7 +62,7 @@ templ CollectionCarousel(section handlers.SectionData) {
|
||||
</div>
|
||||
<div class="carousel-container relative group">
|
||||
<button
|
||||
class="carousel-nav-left absolute left-0 top-1/2 -translate-y-1/2 z-10 h-full w-12 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200"
|
||||
class="carousel-nav-left absolute left-0 top-1/2 -translate-y-1/2 z-10 h-full w-12 flex items-center justify-center opacity-0 pointer-events-none group-hover:pointer-events-auto group-hover:opacity-100 transition-opacity duration-200"
|
||||
data-action="scroll-carousel"
|
||||
data-collection-id={ section.ID }
|
||||
data-direction="-1"
|
||||
@@ -89,7 +89,7 @@ templ CollectionCarousel(section handlers.SectionData) {
|
||||
}
|
||||
</div>
|
||||
<button
|
||||
class="carousel-nav-right absolute right-0 top-1/2 -translate-y-1/2 z-10 h-full w-12 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200"
|
||||
class="carousel-nav-right absolute right-0 top-1/2 -translate-y-1/2 z-10 h-full w-12 flex items-center justify-center opacity-0 pointer-events-none group-hover:pointer-events-auto group-hover:opacity-100 transition-opacity duration-200"
|
||||
data-action="scroll-carousel"
|
||||
data-collection-id={ section.ID }
|
||||
data-direction="1"
|
||||
@@ -103,13 +103,8 @@ templ CollectionCarousel(section handlers.SectionData) {
|
||||
}
|
||||
|
||||
templ BookCard(item handlers.BookInfo) {
|
||||
<a href={ "/media/" + item.MediaItemID } title={ item.Title } class="block h-full">
|
||||
<div
|
||||
class="book-card w-full h-full rounded-xl overflow-hidden cursor-pointer"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
aria-label={ "View " + item.Title }
|
||||
>
|
||||
<div class="book-card relative w-full h-full rounded-xl overflow-hidden cursor-pointer">
|
||||
<a href={ "/media/" + item.MediaItemID } class="block h-full">
|
||||
<div
|
||||
class="book-card-cover aspect-[2/3] overflow-hidden"
|
||||
style="background-color: color-mix(in srgb, var(--text-primary) 8%, transparent);"
|
||||
@@ -129,9 +124,6 @@ templ BookCard(item handlers.BookInfo) {
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
}
|
||||
<div class="book-card-action">
|
||||
<span class="book-card-action-btn">@Icon("play", "h-5 w-5 translate-x-0.5")</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="book-card-meta">
|
||||
<h3 class="font-semibold text-sm leading-snug line-clamp-2" style="color: var(--text-primary)" title={ item.Title }>
|
||||
@@ -143,8 +135,29 @@ templ BookCard(item handlers.BookInfo) {
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</a>
|
||||
<div class="book-card-action">
|
||||
if item.HasConflict {
|
||||
<a
|
||||
href={ "/media/" + item.MediaItemID }
|
||||
class="book-card-action-btn"
|
||||
aria-label={ "Resolve progress conflict for " + item.Title }
|
||||
title="Resolve progress conflict"
|
||||
>
|
||||
@Icon("play", "h-5 w-5 translate-x-0.5")
|
||||
</a>
|
||||
} else {
|
||||
<a
|
||||
href={ "/readers/" + item.MediaItemID }
|
||||
class="book-card-action-btn"
|
||||
aria-label={ "Read " + item.Title }
|
||||
title="Read"
|
||||
>
|
||||
@Icon("play", "h-5 w-5 translate-x-0.5")
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ DashboardSettingsModal(sections []handlers.SectionData, hiddenCollections []string, itemsPerSection int) {
|
||||
|
||||
Reference in New Issue
Block a user