package templates import ( "fmt" "bookhoard/internal/database" "bookhoard/internal/handlers" ) // pluralizeBooks renders "book"/"books" for count displays. func pluralizeBooks(n int) string { if n == 1 { return "book" } return "books" } // firstPlayableEpisodeURL returns the reader URL that starts the // collection sequence from the first downloaded episode (plan item 2.3 // "Play collection as sequence" โ the queue param makes the reader // build the collection chain), or "" when nothing can play. func firstPlayableEpisodeURL(collectionID string, books []handlers.BookInfo) string { for _, b := range books { if b.IsEpisode && !b.Hidden && b.DownloadState == "downloaded" { return "/readers/" + b.MediaItemID + "?queue=collection:" + collectionID } } return "" } // PlayCollectionButton renders the header's sequence-start button when // the collection holds at least one downloaded episode. templ PlayCollectionButton(collectionID string, books []handlers.BookInfo) { if url := firstPlayableEpisodeURL(collectionID, books); url != "" { โถ Play as Sequence } } // EpisodePickerGrid renders the episode scope of the collection picker // (plan item 2.3): YOUR subscriptions' episodes (RLS scopes), compact // rows with the shared bookPicker selection store. Checksum โ overlay // mirrors BookPickerGrid so selection survives htmx swaps. templ EpisodePickerGrid(eps []database.SearchEpisodesForPickerRow) { for _, ep := range eps {
{ ep.Title }
{ ep.FeedTitle }
No episodes from your subscriptions match.
} } // CollectionBooksGrid renders the content of #books-container: the // collection's book cards, or the empty state. Shared by the collection // detail page and the /collections/:id/books-grid partial route (the // library switcher refreshes the grid through it โ rendering ownership: // templ renders, JS only swaps). templ CollectionBooksGrid(collection CollectionData, books []handlers.BookInfo) { if len(books) == 0 {Not subscribed (or not visible to you) โ the feed's episodes leave the queue until you follow it again.
๐ง { book.FeedTitle }
if book.DownloadState == "downloaded" { โถ Play } else { Not downloaded }by { book.Author }
}Organize your books into custom collections
Create collections to organize your books
{ col.Description }
{ fmt.Sprintf("%d %s", col.BookCount, pluralizeBooks(col.BookCount)) } if col.IsSmart { ยท rules-matched live }
{ collection.Description }
if collection.IsSmart {Membership is rules-matched live โ always current, nothing to refresh.
}{ book.Title }