feat: add filter parameter to spine-index-model for concise default view

This commit is contained in:
2026-06-21 09:08:54 -04:00
parent d00732a8ca
commit 126330c1dc
+82 -51
View File
@@ -74,8 +74,13 @@ Returns the rendered string."
("existing_authors" (sort authors #'string<)) ("existing_authors" (sort authors #'string<))
("existing_categories" (sort categories #'string<))))) ("existing_categories" (sort categories #'string<)))))
(defun spine-index-model (books &optional selected-id) (defun spine-index-model (books &optional filter selected-id)
"Build the view model ht for templates/index.mustache from BOOKS. "Build the view model ht for templates/index.mustache from BOOKS.
FILTER controls which books are shown:
nil - concise: READING + 5 most recent WANT, others as summary links
all - full listing, no truncation
read - only READ books
want - only WANT books
SELECTED-ID expands the matching book's detail section." SELECTED-ID expands the matching book's detail section."
(let* ((status-labels (let* ((status-labels
'(("WANT" . "on deck") '(("WANT" . "on deck")
@@ -87,6 +92,8 @@ SELECTED-ID expands the matching book's detail section."
("ebook" . "ti-device-tablet") ("ebook" . "ti-device-tablet")
("audiobook" . "ti-headphones"))) ("audiobook" . "ti-headphones")))
(order '("WANT" "READING" "READ" "ABANDONED")) (order '("WANT" "READING" "READ" "ABANDONED"))
(valid-filters '("all" "read" "want"))
(effective-filter (if (member filter valid-filters) filter nil))
(grouped (make-hash-table :test 'equal)) (grouped (make-hash-table :test 'equal))
(total (length books)) (total (length books))
(reading-count 0)) (reading-count 0))
@@ -96,64 +103,88 @@ SELECTED-ID expands the matching book's detail section."
(dolist (book books) (dolist (book books)
(let ((status (or (plist-get book :status) "WANT"))) (let ((status (or (plist-get book :status) "WANT")))
(push book (gethash status grouped)))) (push book (gethash status grouped))))
(let ((groups nil)) (let ((groups nil)
(summary-groups nil))
(dolist (status order) (dolist (status order)
(let ((group-books (nreverse (gethash status grouped)))) (let ((group-books (nreverse (gethash status grouped))))
(when group-books (when group-books
(let ((book-models nil)) (if (and (null effective-filter)
(dolist (book group-books) (member status '("READ" "ABANDONED")))
(let* ((id (plist-get book :id)) (push (ht ("label" (cdr (assoc status status-labels)))
(selected (equal id selected-id)) ("count" (length group-books))
(fmt (plist-get book :format)) ("href" (format "/index?filter=%s"
(rating (plist-get book :rating)) (downcase status))))
(notes (plist-get book :notes)) summary-groups)
(status (or (plist-get book :status) "WANT")) (when (or (null effective-filter)
(model (string= (downcase status) effective-filter)
(ht (string= effective-filter "all"))
("format_icon" (when (and (null effective-filter)
(or (cdr (assoc fmt format-icons)) "")) (string= status "WANT"))
("status_class" (downcase status)) (setq group-books
("status_label" (downcase status)) (cl-subseq (sort group-books
("title" (plist-get book :title)) (lambda (a b)
("author" (or (plist-get book :author) "")) (let ((a-date (plist-get a :added))
("tags_inline" (b-date (plist-get b :added)))
(let ((tags (plist-get book :tags))) (cond
(when tags ((null a-date) nil)
(mapconcat (lambda (tag) (concat ":" tag ":")) ((null b-date) t)
tags " ")))) (t (string> a-date b-date))))))
("rec_via" (plist-get book :rec_by)) 0 (min (length group-books) 5))))
("rating_display" (let ((book-models nil))
(when (and rating (> (length rating) 0)) (dolist (book group-books)
(make-string (string-to-number rating) ?\u2605))) (let* ((id (plist-get book :id))
("selected" selected) (selected (equal id selected-id))
("detail" (fmt (plist-get book :format))
(when selected (rating (plist-get book :rating))
(notes (plist-get book :notes))
(status (or (plist-get book :status) "WANT"))
(model
(ht (ht
("meta" ("format_icon"
(let ((added (plist-get book :added)) (or (cdr (assoc fmt format-icons)) ""))
(fmt (plist-get book :format))) ("status_class" (downcase status))
(cond ("status_label" (downcase status))
((and added fmt) ("title" (plist-get book :title))
(format "started %s · %s" added fmt)) ("author" (or (plist-get book :author) ""))
(added (format "started %s" added)) ("tags_inline"
(fmt (format "format: %s" fmt)) (let ((tags (plist-get book :tags)))
(t "")))) (when tags
("notes" (mapconcat (lambda (tag) (concat ":" tag ":"))
(when notes tags " "))))
(cl-loop for (date text) in notes ("rec_via" (plist-get book :rec_by))
collect (ht ("rating_display"
("date" (format "[%s]" date)) (when (and rating (> (length rating) 0))
("text" text))))))))))) (make-string (string-to-number rating) ?\u2605)))
(push model book-models))) ("selected" selected)
(push (ht ("label" (cdr (assoc status status-labels))) ("detail"
("count" (length group-books)) (when selected
("books" (nreverse book-models))) (ht
groups))))) ("meta"
(let ((added (plist-get book :added))
(fmt (plist-get book :format)))
(cond
((and added fmt)
(format "started %s \302\267 %s" added fmt))
(added (format "started %s" added))
(fmt (format "format: %s" fmt))
(t ""))))
("notes"
(when notes
(cl-loop for (date text) in notes
collect (ht
("date" (format "[%s]" date))
("text" text)))))))))))
(push model book-models)))
(push (ht ("label" (cdr (assoc status status-labels)))
("count" (length group-books))
("books" (nreverse book-models)))
groups)))))))
(ht ("app_title" "spine") (ht ("app_title" "spine")
("total_books" total) ("total_books" total)
("reading_count" reading-count) ("reading_count" reading-count)
("current_filter" (or effective-filter ""))
("summary_groups" (nreverse summary-groups))
("groups" (nreverse groups)))))) ("groups" (nreverse groups))))))
(defun spine-add-book (&rest args) (defun spine-add-book (&rest args)
"Add a new WANT book to `spine-org-file'. "Add a new WANT book to `spine-org-file'.
Keyword arguments: :title :author :category :format :isbn :cover Keyword arguments: :title :author :category :format :isbn :cover