diff options
-rw-r--r-- | sx-question-list.el | 15 | ||||
-rw-r--r-- | sx-question.el | 52 | ||||
-rw-r--r-- | sx.el | 15 |
3 files changed, 78 insertions, 4 deletions
diff --git a/sx-question-list.el b/sx-question-list.el index dead68f..7dd0d00 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -138,8 +138,20 @@ Letters do not insert themselves; instead, they are commands. ("g" sx-question-list-refresh) (":" sx-question-list-switch-site) ("v" sx-question-list-visit) + ("h" sx-question-list-hide) ([?\r] sx-question-list-display-question))) +(defun sx-question-list-hide (data) + "Hide question under point. +Non-interactively, DATA is a question alist." + (interactive + (list (if (derived-mode-p 'sx-question-list-mode) + (tabulated-list-get-id) + (user-error "Not in `sx-question-list-mode'")))) + (sx-question--mark-hidden data) + (when (called-interactively-p 'any) + (sx-question-list-refresh 'redisplay 'noupdate))) + (defvar sx-question-list--current-page "Latest" ;; Other values (once we implement them) are "Top Voted", ;; "Unanswered", etc. @@ -203,7 +215,8 @@ a new list before redisplaying." (setq sx-question-list--current-dataset question-list) ;; Print the result. (setq tabulated-list-entries - (mapcar #'sx-question-list--print-info question-list))) + (mapcar #'sx-question-list--print-info + (cl-remove-if #'sx-question--hidden-p question-list)))) (when redisplay (tabulated-list-print 'remember))) (defun sx-question-list-visit (&optional data) diff --git a/sx-question.el b/sx-question.el index 6ef9484..972e9d9 100644 --- a/sx-question.el +++ b/sx-question.el @@ -68,6 +68,7 @@ ;;; Question Properties +;;;; Read/unread (defvar sx-question--user-read-list nil "Alist of questions read by the user. Each element has the form (SITE . QUESTION-LIST). @@ -78,7 +79,7 @@ And each element in QUESTION-LIST has the form (QUESTION_ID . LAST-VIEWED-DATE). If no cache exists for it, initialize one with SITE." (unless sx-question--user-read-list (setq sx-question--user-read-list - (sx-cache-get 'read-questions `(list ,site))))) + (sx-cache-get 'read-questions `'((,site)))))) (defun sx-question--read-p (question) "Non-nil if QUESTION has been read since last updated." @@ -104,13 +105,58 @@ If no cache exists for it, initialize one with SITE." ((setq cell (assoc .question_id site-cell)) (setcdr cell .last_activity_date)) ;; Question wasn't present. - (t - (setcdr site-cell (cons q-cell (cdr site-cell))))))) + (t + (sx-sorted-insert-skip-first + q-cell site-cell (lambda (x y) (> (car x) (car y)))))))) ;; This causes a small lag on `j' and `k' as the list gets large. ;; Should we do this on a timer? ;; Save the results. (sx-cache-set 'read-questions sx-question--user-read-list)) + +;;;; Hidden +(defvar sx-question--user-hidden-list nil + "Alist of questions hidden by the user. +Each element has the form (SITE . QUESTION-LIST). +And each element in QUESTION-LIST has the form (QUESTION_ID . LAST-VIEWED-DATE).") + +(defun sx-question--ensure-hidden-list (site) + "Ensure the `sx-question--user-hidden-list' has been read from cache. +If no cache exists for it, initialize one with SITE." + (unless sx-question--user-hidden-list + (setq sx-question--user-hidden-list + (sx-cache-get 'hidden-questions `'((,site)))))) + +(defun sx-question--hidden-p (question) + "Non-nil if QUESTION has been hidden." + (sx-assoc-let question + (sx-question--ensure-hidden-list .site) + (let ((ql (cdr (assoc .site sx-question--user-hidden-list)))) + (and ql (memq .question_id ql))))) + +(defun sx-question--mark-hidden (question) + "Mark QUESTION as being hidden." + (sx-assoc-let question + (sx-question--ensure-hidden-list .site) + (let ((site-cell (assoc .site sx-question--user-hidden-list)) + cell) + ;; If question already hidden, do nothing. + (unless (memq .question_id site-cell) + ;; First question from this site. + (if (null site-cell) + (push (list .site .question_id) sx-question--user-hidden-list) + ;; Question wasn't present. + ;; Add it in, but make sure it's sorted (just in case we need + ;; it later). + (sx-sorted-insert-skip-first .question_id site-cell >)) + ;; This causes a small lag on `j' and `k' as the list gets large. + ;; Should we do this on a timer? + ;; Save the results. + (sx-cache-set 'hidden-questions sx-question--user-hidden-list))))) + + +;;;; Other data + (defun sx-question--accepted-answer-id (question) "Return accepted answer in QUESTION, or nil if none." (sx-assoc-let question @@ -27,6 +27,21 @@ ;;; Utility Functions +(defmacro sx-sorted-insert-skip-first (newelt list &optional predicate) + "Inserted NEWELT into LIST sorted by PREDICATE. +This is designed for the (site id id ...) lists. So the first car +is intentionally skipped." + `(let ((tail ,list) + (x ,newelt)) + ;; The first element is never less-than. + (while (and + ;; We're at the end. + (cdr-safe tail) + ;; We're at the right place. + (,(or predicate #'<) x (cadr tail))) + (setq tail (cdr tail))) + (setcdr tail (cons x (cdr tail))))) + (defun sx-message (format-string &rest args) "Display a message" (message "[stack] %s" (apply #'format format-string args))) |