feat: add LAST_MODIFIED property tracked on book creation and edits
This commit is contained in:
@@ -174,6 +174,38 @@ SELECTED-ID expands the matching book's detail section."
|
||||
collect (ht
|
||||
("date" (format "[%s]" date))
|
||||
("text" text))))))))
|
||||
("minibuffer"
|
||||
(when selected
|
||||
(let ((status (plist-get book :status)))
|
||||
(ht ("actions"
|
||||
(append
|
||||
(cond
|
||||
((equal status "WANT")
|
||||
(list
|
||||
(ht ("command" "status") ("label" "Mark reading")
|
||||
("href" (format "/edit?id=%s&action=status&value=READING" id)))
|
||||
(ht ("command" "note") ("label" "Add note")
|
||||
("href" (format "/edit?id=%s&action=note" id)))))
|
||||
((equal status "READING")
|
||||
(list
|
||||
(ht ("command" "status") ("label" "Mark read")
|
||||
("href" (format "/edit?id=%s&action=status&value=READ" id)))
|
||||
(ht ("command" "status") ("label" "Abandon")
|
||||
("href" (format "/edit?id=%s&action=status&value=ABANDONED" id)))
|
||||
(ht ("command" "note") ("label" "Add note")
|
||||
("href" (format "/edit?id=%s&action=note" id)))))
|
||||
((equal status "READ")
|
||||
(list
|
||||
(ht ("command" "status") ("label" "Read again")
|
||||
("href" (format "/edit?id=%s&action=status&value=READING" id)))
|
||||
(ht ("command" "note") ("label" "Add note")
|
||||
("href" (format "/edit?id=%s&action=note" id)))))
|
||||
((equal status "ABANDONED")
|
||||
(list
|
||||
(ht ("command" "status") ("label" "Try again")
|
||||
("href" (format "/edit?id=%s&action=status&value=READING" id)))))
|
||||
(t nil))
|
||||
nil))))))
|
||||
)))
|
||||
(push model book-models)))
|
||||
(push (ht ("label" (cdr (assoc status status-labels)))
|
||||
@@ -222,8 +254,8 @@ SELECTED-ID expands the matching book's detail section."
|
||||
(ht ("command" "status") ("label" "Try again")
|
||||
("href" (format "/edit?id=%s&action=status&value=READING" selected-id)))))
|
||||
(t nil))
|
||||
nil))))))))
|
||||
)))))
|
||||
nil)))))))))
|
||||
))))
|
||||
(defun spine-add-book (&rest args)
|
||||
"Add a new WANT book to `spine-org-file'.
|
||||
Keyword arguments: :title :author :category :format :isbn :cover
|
||||
@@ -275,6 +307,8 @@ Signals an error on failure."
|
||||
(format "%04x-%s"
|
||||
(random 65535)
|
||||
(substring (sha1 (concat title (number-to-string (random)))) 0 4)))
|
||||
;; Set last modified
|
||||
(org-set-property "LAST_MODIFIED" (format-time-string "[%Y-%m-%d]"))
|
||||
;; Set tags
|
||||
(when (and category (> (length category) 0))
|
||||
(org-set-tags (list category)))
|
||||
@@ -416,6 +450,7 @@ Returns nil if the file is missing or cannot be parsed."
|
||||
:rec_note (spine--prop pos "REC_NOTE")
|
||||
:rating (spine--prop pos "RATING")
|
||||
:added (spine--prop pos "ADDED")
|
||||
:last_modified (spine--prop pos "LAST_MODIFIED")
|
||||
:tags (org-element-property :tags hl)
|
||||
:notes (spine--extract-notes
|
||||
(org-element-property :begin hl)
|
||||
@@ -482,63 +517,6 @@ Set by test harnesses that only need the model functions.")
|
||||
(ht ("app_title" "spine")))))
|
||||
(insert (spine-render "add.mustache" model)))))))
|
||||
|
||||
(defun httpd/edit (proc uri-path query request)
|
||||
"Handle /edit: GET shows prompt page, POST processes actions.
|
||||
Query params:
|
||||
id - book ID
|
||||
action - status | note | rating
|
||||
value - target status / rating value (for status/rating actions)
|
||||
text - note text (for note action)
|
||||
date - optional date string (for note/read actions)"
|
||||
(let ((method (caar request))
|
||||
(id (cadr (assoc "id" query)))
|
||||
(action (cadr (assoc "action" query)))
|
||||
(value (cadr (assoc "value" query)))
|
||||
(text (cadr (assoc "text" query)))
|
||||
(date (cadr (assoc "date" query))))
|
||||
(if (equal method "POST")
|
||||
(progn
|
||||
(cond
|
||||
((equal action "note")
|
||||
(spine-add-note id text date))
|
||||
((equal action "status")
|
||||
(spine-set-status id value))
|
||||
((equal action "rating")
|
||||
(spine-set-rating id value)))
|
||||
(httpd-redirect proc (format "/index?id=%s" id) 303))
|
||||
;; GET: show prompt page for actions needing input
|
||||
(if (and id (equal action "note"))
|
||||
(let* ((books (spine-books))
|
||||
(book (cl-find id books :key (lambda (b) (plist-get b :id)) :test #'equal))
|
||||
(title (if book (plist-get book :title) "Unknown")))
|
||||
(httpd-with-buffer proc "text/html"
|
||||
(insert (format
|
||||
"<!DOCTYPE html>
|
||||
<html lang=\"en\">
|
||||
<head><meta charset=\"utf-8\"/><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>
|
||||
<meta name=\"color-scheme\" content=\"light dark\"/>
|
||||
<title>Spine — note</title>
|
||||
<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@picocss/pico@2.1.1/css/pico.min.css\"/>
|
||||
</head>
|
||||
<body>
|
||||
<main class=\"container\">
|
||||
<article style=\"max-width:560px;margin-inline:auto;\">
|
||||
<header><strong>spine · add note</strong></header>
|
||||
<p><em>%s</em></p>
|
||||
<form method=\"post\" action=\"/edit\">
|
||||
<input type=\"hidden\" name=\"id\" value=\"%s\"/>
|
||||
<input type=\"hidden\" name=\"action\" value=\"note\"/>
|
||||
<label>Date <input type=\"date\" name=\"date\" value=\"%s\"/></label>
|
||||
<label>Note <textarea name=\"text\" rows=\"3\" required autofocus></textarea></label>
|
||||
<button type=\"submit\">Add note</button>
|
||||
</form>
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
||||
</html>"
|
||||
title id (format-time-string "%Y-%m-%d"))))))
|
||||
;; Unknown action or missing id: redirect to index
|
||||
(httpd-redirect proc "/index" 302))))
|
||||
(defun httpd/ (proc uri-path query request)
|
||||
"Redirect root to /index."
|
||||
(httpd-redirect proc "/index" 302))
|
||||
|
||||
Reference in New Issue
Block a user