145 lines
5.2 KiB
EmacsLisp
145 lines
5.2 KiB
EmacsLisp
;;; noise-chat-list.el --- Chat list buffer for Noise -*- lexical-binding: t; -*-
|
|
|
|
;;; Commentary:
|
|
|
|
;; Provides the *signal* buffer — an ibuffer-style list of all conversations
|
|
;; with unread counts, last message previews, and timestamps.
|
|
|
|
;;; Code:
|
|
|
|
(require 'noise-db)
|
|
(require 'noise-new-chat)
|
|
(require 'cl-lib)
|
|
|
|
(defvar noise-chat-list-mode-map
|
|
(let ((map (make-sparse-keymap)))
|
|
(define-key map (kbd "n") #'next-line)
|
|
(define-key map (kbd "p") #'previous-line)
|
|
(define-key map (kbd "RET") #'noise-chat-list-open)
|
|
(define-key map (kbd "m") #'noise-chat-list-mark-read)
|
|
(define-key map (kbd "c") #'noise-new-chat)
|
|
(define-key map (kbd "g") #'noise-chat-list-refresh)
|
|
(define-key map (kbd "/") #'noise-chat-list-filter)
|
|
(define-key map (kbd "q") #'quit-window)
|
|
map)
|
|
"Keymap for `noise-chat-list-mode'.")
|
|
|
|
(define-derived-mode noise-chat-list-mode special-mode "Signal Chats"
|
|
"Major mode for the Noise Signal chat list.
|
|
\\{noise-chat-list-mode-map}"
|
|
:group 'noise
|
|
(setq buffer-read-only t)
|
|
(setq-local header-line-format
|
|
(concat " U "
|
|
(format "%-20s" "Name")
|
|
(format "%-40s" "Last message")
|
|
"When")))
|
|
|
|
;;;###autoload
|
|
(defun noise-chat-list ()
|
|
"Display the Noise Signal chat list."
|
|
(interactive)
|
|
(noise-db-init)
|
|
(switch-to-buffer "*signal*")
|
|
(noise-chat-list-mode)
|
|
(noise-chat-list--render))
|
|
|
|
(defun noise-chat-list--render ()
|
|
"Render the conversation list in the *signal* buffer."
|
|
(let ((inhibit-read-only t)
|
|
(conversations (noise-db-get-all-conversations)))
|
|
(erase-buffer)
|
|
(if (null conversations)
|
|
(insert "No conversations. Sync from signal-cli with `g`.\n")
|
|
(dolist (conv conversations)
|
|
(insert (noise-chat-list--format-row conv) "\n")))
|
|
(noise-chat-list--update-modeline conversations)
|
|
(goto-char (point-min))
|
|
(set-buffer-modified-p nil)))
|
|
|
|
(defun noise-chat-list--update-modeline (conversations)
|
|
"Update the mode line with chat statistics."
|
|
(let* ((total (length conversations))
|
|
(unread-total (cl-reduce #'+
|
|
(mapcar (lambda (c) (or (gethash "unread_count" c) 0))
|
|
conversations)
|
|
:initial-value 0)))
|
|
(setq mode-line-format
|
|
(format " ⌁ %s %d chats %s %s (%s) ─ U:%%- ─ Top ───"
|
|
(propertize "*signal*" 'face 'bold)
|
|
total
|
|
(if (> unread-total 0)
|
|
(format "· %d unread" unread-total)
|
|
"")
|
|
""
|
|
"Signal Chats"))))
|
|
|
|
(defun noise-chat-list--format-time (timestamp)
|
|
"Format TIMESTAMP (unix seconds) as a human-readable relative time string.
|
|
Today: HH:MM, this week: Ddd, older: Mon DD."
|
|
(let* ((now (current-time))
|
|
(msg-time (seconds-to-time timestamp))
|
|
(diff (float-time (time-subtract now msg-time)))
|
|
(one-day 86400)
|
|
(one-week (* 7 one-day)))
|
|
(cond
|
|
((< diff one-day)
|
|
(format-time-string "%H:%M" msg-time))
|
|
((< diff one-week)
|
|
(format-time-string "%a" msg-time))
|
|
(t
|
|
(format-time-string "%b %e" msg-time)))))
|
|
|
|
(defun noise-chat-list--format-row (conv)
|
|
"Format a single conversation row for display.
|
|
CONV is a hash table with string keys from noise-db."
|
|
(let* ((unread (or (gethash "unread_count" conv) 0))
|
|
(name (or (gethash "name" conv) "Unknown"))
|
|
(preview (or (gethash "last_message_preview" conv) ""))
|
|
(sender (or (gethash "last_message_sender" conv) ""))
|
|
(timestamp (or (gethash "last_message_time" conv) 0))
|
|
(fringe (if (> unread 0) "●" " "))
|
|
(unread-str (if (> unread 0) (format "%3d" unread) " "))
|
|
(name-str (truncate-string-to-width name 18 nil nil "…"))
|
|
(preview-prefix
|
|
(cond
|
|
((string= sender "") preview)
|
|
((string= sender "me") (concat "me: " preview))
|
|
(t (concat sender ": " preview))))
|
|
(preview-str (truncate-string-to-width preview-prefix 36 nil nil "…"))
|
|
(time-str (noise-chat-list--format-time timestamp)))
|
|
(concat " " fringe " " unread-str " "
|
|
(propertize (format "%-18s" name-str) 'face 'bold) " "
|
|
preview-str " "
|
|
(propertize time-str 'face 'shadow))))
|
|
|
|
(defun noise-chat-list-open ()
|
|
"Open the conversation at point."
|
|
(interactive)
|
|
(let* ((conversations (noise-db-get-all-conversations))
|
|
(idx (1- (line-number-at-pos)))
|
|
(conv (nth idx conversations)))
|
|
(if conv
|
|
(message "Open conversation: %s (not yet implemented)"
|
|
(gethash "name" conv))
|
|
(message "No conversation on this line."))))
|
|
|
|
(defun noise-chat-list-mark-read ()
|
|
"Mark the conversation at point as read."
|
|
(interactive)
|
|
(message "Mark read: not yet connected to signal-cli"))
|
|
|
|
(defun noise-chat-list-refresh ()
|
|
"Refresh the chat list (re-render from database)."
|
|
(interactive)
|
|
(noise-chat-list--render)
|
|
(message "Chat list refreshed."))
|
|
|
|
(defun noise-chat-list-filter ()
|
|
"Filter the chat list with a search query."
|
|
(interactive)
|
|
(message "Filter: not yet implemented"))
|
|
|
|
(provide 'noise-chat-list)
|
|
;;; noise-chat-list.el ends here
|