diff --git a/books/views.py b/books/views.py index e39557c..a068074 100644 --- a/books/views.py +++ b/books/views.py @@ -27,11 +27,13 @@ def book_list(request): b['uploaded_at'] = b['uploaded_at'].isoformat() # Include saved scroll_fraction for each book progress_map = { - p.book_id: p.scroll_fraction + p.book_id: (p.scroll_fraction, p.updated_at) for p in EBookProgress.objects.filter(user=request.user) } for b in books: - b['scroll_fraction'] = progress_map.get(b['id'], 0.0) + prog = progress_map.get(b['id']) + b['scroll_fraction'] = prog[0] if prog else 0.0 + b['last_read'] = prog[1].isoformat() if prog else None return JsonResponse(books, safe=False) diff --git a/static/js/app.js b/static/js/app.js index f36bce8..5b1eb5d 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -2644,12 +2644,18 @@ async function loadBookList() { const metaBuf = await decryptBytes(key, b.meta_iv, b.meta_ct); const meta = JSON.parse(new TextDecoder().decode(metaBuf)); bookMetaCache[b.id] = {title: meta.title || '?', author: meta.author || '', type: meta.type || 'epub'}; - decrypted.push({id: b.id, title: meta.title || '?', author: meta.author || '', type: meta.type || 'epub', scroll_fraction: b.scroll_fraction, uploaded_at: b.uploaded_at, keyOk: true}); + decrypted.push({id: b.id, title: meta.title || '?', author: meta.author || '', type: meta.type || 'epub', scroll_fraction: b.scroll_fraction, uploaded_at: b.uploaded_at, last_read: b.last_read || null, keyOk: true}); } catch (e) { bookMetaCache[b.id] = {title: `Book #${b.id}`, author: '', type: 'epub'}; - decrypted.push({id: b.id, title: `Book #${b.id}`, author: '', type: 'epub', scroll_fraction: b.scroll_fraction, uploaded_at: b.uploaded_at, keyOk: false}); + decrypted.push({id: b.id, title: `Book #${b.id}`, author: '', type: 'epub', scroll_fraction: b.scroll_fraction, uploaded_at: b.uploaded_at, last_read: b.last_read || null, keyOk: false}); } } + decrypted.sort((a, b) => { + if (a.last_read && b.last_read) return b.last_read.localeCompare(a.last_read); + if (a.last_read) return -1; + if (b.last_read) return 1; + return b.uploaded_at.localeCompare(a.uploaded_at); + }); renderBookList(decrypted); } catch (e) { if (listEl) listEl.innerHTML = `

Error loading books: ${e.message}

`;