feat: show in-progress books under 'Continue reading' on home page

Reading progress lives in localStorage so sorting is done client-side.
On page load, JS reads the reading_progress map, moves any matching book
cards into a 'Continue reading' grid above the main grid, and reveals an
'Available' heading for the remainder. Books with no saved progress are
unaffected and no server changes are required.
This commit is contained in:
Admin
2026-03-01 17:24:55 +05:00
parent e0dc4678b3
commit 07fbfe5555

View File

@@ -146,15 +146,24 @@ const homeTmpl = `
<div id="scrape-status" class="mt-3"></div>
</div>
<!-- Book grid -->
<div class="grid gap-4 sm:grid-cols-2">
<!-- Continue reading section (populated by JS) -->
<div id="continue-reading-section" class="hidden mb-10">
<h2 class="text-lg font-semibold text-zinc-200 mb-3">Continue reading</h2>
<div id="continue-reading-grid" class="grid gap-4 sm:grid-cols-2"></div>
</div>
<!-- All books grid -->
<div id="available-section">
<h2 id="available-heading" class="text-lg font-semibold text-zinc-200 mb-3 hidden">Available</h2>
<div id="books-grid" class="grid gap-4 sm:grid-cols-2">
{{range .Books}}
<a href="/books/{{.Slug}}"
hx-get="/books/{{.Slug}}"
hx-target="#main-content"
hx-push-url="true"
hx-swap="innerHTML"
class="group block rounded-xl border border-zinc-800 bg-zinc-900 p-5 hover:border-amber-500 transition-colors cursor-pointer">
data-slug="{{.Slug}}"
class="book-card group block rounded-xl border border-zinc-800 bg-zinc-900 p-5 hover:border-amber-500 transition-colors cursor-pointer">
<div class="flex gap-4">
{{if .Cover}}
<img src="{{.Cover}}" alt="cover" class="w-14 h-20 object-cover rounded flex-shrink-0">
@@ -173,7 +182,37 @@ const homeTmpl = `
<p class="text-zinc-500 col-span-2">No books scraped yet.</p>
{{end}}
</div>
</div>`
</div>
</div>
<script>
(function () {
var progress = {};
try { progress = JSON.parse(localStorage.getItem('reading_progress') || '{}'); } catch(_) {}
var inProgress = Object.keys(progress);
if (inProgress.length === 0) return;
var continueSection = document.getElementById('continue-reading-section');
var continueGrid = document.getElementById('continue-reading-grid');
var availableHeading = document.getElementById('available-heading');
var moved = 0;
inProgress.forEach(function(slug) {
var card = document.querySelector('[data-slug="' + slug + '"]');
if (!card) return;
var clone = card.cloneNode(true);
continueGrid.appendChild(clone);
card.parentNode.removeChild(card);
moved++;
});
if (moved > 0) {
continueSection.classList.remove('hidden');
availableHeading.classList.remove('hidden');
}
}());
</script>`
func (s *Server) handleHome(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {