2020-12-03 20:56:59 -06:00
|
|
|
|
;;; ement-room-list.el --- Ement room list buffer -*- lexical-binding: t; -*-
|
|
|
|
|
|
|
|
|
|
;; Copyright (C) 2020 Adam Porter
|
|
|
|
|
|
|
|
|
|
;; Author: Adam Porter <adam@alphapapa.net>
|
2021-07-27 21:50:05 -05:00
|
|
|
|
;; Keywords: comm
|
2020-12-03 20:56:59 -06:00
|
|
|
|
|
|
|
|
|
;; This program is free software; you can redistribute it and/or modify
|
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
;; (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
;; This program is distributed in the hope that it will be useful,
|
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
|
|
|
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;; This library implements a room list buffer.
|
|
|
|
|
|
|
|
|
|
;; NOTE: It doesn't appear that there is a way to get the number of
|
|
|
|
|
;; members in a room other than by retrieving the list of members and
|
|
|
|
|
;; counting them. For a large room (e.g. the Spacemacs Gitter room or
|
|
|
|
|
;; #debian:matrix.org), that means thousands of users, none of the
|
|
|
|
|
;; details of which we care about. So it seems impractical to know
|
|
|
|
|
;; the number of members when using lazy-loading. So I guess we just
|
|
|
|
|
;; won't show the number of members.
|
|
|
|
|
|
2020-12-04 19:17:58 -06:00
|
|
|
|
;; TODO: (Or maybe there is, see m.joined_member_count).
|
|
|
|
|
|
2021-07-28 20:12:12 -05:00
|
|
|
|
;; NOTE: The tabulated-list API is awkward here. When the
|
|
|
|
|
;; `tabulated-list-format' is changed, we have to make the change in 4
|
|
|
|
|
;; or 5 other places, and if one forgets to, bugs with non-obvious
|
|
|
|
|
;; causes happen. I think library using EIEIO or structs would be
|
|
|
|
|
;; very helpful.
|
|
|
|
|
|
2020-12-03 20:56:59 -06:00
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
;;;; Requirements
|
|
|
|
|
|
|
|
|
|
(require 'cl-lib)
|
|
|
|
|
(require 'tabulated-list)
|
|
|
|
|
|
|
|
|
|
;; FIXME: Depend on ts.
|
|
|
|
|
(require 'ts)
|
|
|
|
|
|
|
|
|
|
(require 'ement)
|
|
|
|
|
|
|
|
|
|
;;;; Variables
|
|
|
|
|
|
2021-07-29 04:04:21 -05:00
|
|
|
|
(defvar ement-room-list-mode-map
|
2020-12-03 20:56:59 -06:00
|
|
|
|
(let ((map (make-sparse-keymap)))
|
2021-07-29 04:04:21 -05:00
|
|
|
|
;; (define-key map (kbd "g") #'tabulated-list-revert)
|
|
|
|
|
;; (define-key map (kbd "q") #'bury-buffer)
|
|
|
|
|
(define-key map (kbd "SPC") #'ement-room-list-next-unread)
|
|
|
|
|
;; (define-key map (kbd "S") #'tabulated-list-sort)
|
|
|
|
|
map))
|
2020-12-03 20:56:59 -06:00
|
|
|
|
|
|
|
|
|
(defvar ement-sessions)
|
|
|
|
|
|
|
|
|
|
;;;; Customization
|
|
|
|
|
|
2021-07-29 04:35:21 -05:00
|
|
|
|
(defgroup ement-room-list nil
|
|
|
|
|
"Options for the room list buffer."
|
|
|
|
|
:group 'ement)
|
|
|
|
|
|
|
|
|
|
(defcustom ement-room-list-auto-update t
|
|
|
|
|
"Automatically update the room list buffer."
|
|
|
|
|
:type 'boolean)
|
|
|
|
|
|
2021-08-05 02:50:41 -05:00
|
|
|
|
(defcustom ement-room-list-avatars t
|
|
|
|
|
"Show room avatars in the room list."
|
|
|
|
|
:type 'boolean)
|
|
|
|
|
|
2021-07-25 20:31:41 -05:00
|
|
|
|
;;;; Bookmark support
|
|
|
|
|
|
|
|
|
|
;; Especially useful with Burly: <https://github.com/alphapapa/burly.el>
|
|
|
|
|
|
|
|
|
|
(require 'bookmark)
|
|
|
|
|
|
|
|
|
|
(defun ement-room-list-bookmark-make-record ()
|
|
|
|
|
"Return a bookmark record for the `ement-room-list' buffer."
|
|
|
|
|
(pcase-let* (((cl-struct ement-session user) ement-session)
|
|
|
|
|
((cl-struct ement-user (id session-id)) user))
|
|
|
|
|
;; MAYBE: Support bookmarking specific events in a room.
|
|
|
|
|
(list (concat "Ement room list (" session-id ")")
|
|
|
|
|
(cons 'session-id session-id)
|
|
|
|
|
(cons 'handler #'ement-room-list-bookmark-handler))))
|
|
|
|
|
|
|
|
|
|
(defun ement-room-list-bookmark-handler (bookmark)
|
|
|
|
|
"Show Ement room list buffer for BOOKMARK."
|
|
|
|
|
(pcase-let* (((map session-id) bookmark))
|
|
|
|
|
(unless (cl-loop for session in ement-sessions
|
|
|
|
|
thereis (equal session-id (ement-user-id (ement-session-user session))))
|
|
|
|
|
;; MAYBE: Automatically connect.
|
|
|
|
|
(user-error "Session %s not connected: call `ement-connect' first" session-id))
|
|
|
|
|
;; FIXME: Support multiple sessions.
|
|
|
|
|
(ement-room-list)))
|
2020-12-03 20:56:59 -06:00
|
|
|
|
|
|
|
|
|
;;;; Commands
|
|
|
|
|
|
2021-07-29 04:04:21 -05:00
|
|
|
|
(defun ement-room-list-next-unread ()
|
|
|
|
|
"Show next unread room."
|
|
|
|
|
(interactive)
|
|
|
|
|
(unless (button-at (point))
|
|
|
|
|
(call-interactively #'forward-button))
|
|
|
|
|
(unless (cl-loop with starting-line = (line-number-at-pos)
|
|
|
|
|
if (equal "U" (elt (tabulated-list-get-entry) 0))
|
|
|
|
|
do (progn
|
|
|
|
|
(goto-char (button-end (button-at (point))))
|
|
|
|
|
(push-button (1- (point)))
|
|
|
|
|
(cl-return t))
|
|
|
|
|
else do (call-interactively #'forward-button)
|
|
|
|
|
while (> (line-number-at-pos) starting-line))
|
2021-08-05 07:21:48 -05:00
|
|
|
|
;; No more unread rooms.
|
|
|
|
|
(message "No more unread rooms")))
|
2021-07-29 04:04:21 -05:00
|
|
|
|
|
2020-12-03 20:56:59 -06:00
|
|
|
|
;;;###autoload
|
2020-12-04 03:12:49 -06:00
|
|
|
|
(defun ement-room-list (&rest _ignore)
|
2020-12-04 03:08:09 -06:00
|
|
|
|
"Show buffer listing joined rooms.
|
|
|
|
|
Calls `pop-to-buffer-same-window'. Interactively, with prefix,
|
|
|
|
|
call `pop-to-buffer'."
|
2020-12-03 20:56:59 -06:00
|
|
|
|
(interactive)
|
|
|
|
|
(with-current-buffer (get-buffer-create "*Ement Rooms*")
|
|
|
|
|
(ement-room-list-mode)
|
2021-07-25 20:31:41 -05:00
|
|
|
|
(setq-local bookmark-make-record-function #'ement-room-list-bookmark-make-record)
|
2020-12-04 03:08:09 -06:00
|
|
|
|
;; FIXME: There must be a better way to handle this.
|
|
|
|
|
(funcall (if current-prefix-arg
|
|
|
|
|
#'pop-to-buffer #'pop-to-buffer-same-window)
|
|
|
|
|
(current-buffer))))
|
2020-12-03 20:56:59 -06:00
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defalias 'ement-list-rooms 'ement-room-list)
|
|
|
|
|
|
|
|
|
|
(define-derived-mode ement-room-list-mode tabulated-list-mode
|
2021-08-06 10:54:58 -05:00
|
|
|
|
"Ement Room List"
|
2020-12-03 20:56:59 -06:00
|
|
|
|
:group 'ement
|
2021-07-29 04:04:21 -05:00
|
|
|
|
(setf tabulated-list-format (vector
|
2021-07-29 04:44:40 -05:00
|
|
|
|
'("U" 1 t) '("B" 1 t)
|
2021-08-05 02:50:41 -05:00
|
|
|
|
;; '("U" 1 t)
|
2021-07-28 23:22:03 -05:00
|
|
|
|
'("D" 1 t) ; Direct
|
2021-08-05 02:50:41 -05:00
|
|
|
|
(list (propertize "🐱"
|
|
|
|
|
'help-echo "Avatar")
|
|
|
|
|
4 t) ; Avatar
|
2020-12-03 20:56:59 -06:00
|
|
|
|
'("Name" 25 t) '("Topic" 35 t)
|
|
|
|
|
'("Latest" 20 ement-room-list-latest<)
|
2021-07-28 20:12:12 -05:00
|
|
|
|
'("Members" 7 ement-room-list-members<)
|
2021-07-28 23:22:03 -05:00
|
|
|
|
;; '("P" 1 t) '("Tags" 15 t)
|
2020-12-03 20:56:59 -06:00
|
|
|
|
'("Session" 15 t))
|
|
|
|
|
tabulated-list-sort-key '("Latest" . t))
|
|
|
|
|
(add-hook 'tabulated-list-revert-hook #'ement-room-list--set-entries nil 'local)
|
|
|
|
|
(tabulated-list-init-header)
|
|
|
|
|
(ement-room-list--set-entries)
|
|
|
|
|
(tabulated-list-revert))
|
|
|
|
|
|
|
|
|
|
(defun ement-room-list-action (event)
|
2020-12-04 03:08:09 -06:00
|
|
|
|
"Show buffer for room at EVENT or point."
|
2020-12-03 20:56:59 -06:00
|
|
|
|
(interactive "e")
|
|
|
|
|
(mouse-set-point event)
|
|
|
|
|
(pcase-let* ((room (tabulated-list-get-id))
|
2021-08-05 02:50:41 -05:00
|
|
|
|
(`[,_unread ,_buffer ,_direct ,_avatar ,_name ,_topic ,_latest ,_members ,user-id]
|
2020-12-03 20:56:59 -06:00
|
|
|
|
(tabulated-list-get-entry))
|
|
|
|
|
(session (cl-loop for session in ement-sessions
|
|
|
|
|
when (equal user-id (ement-user-id (ement-session-user session)))
|
2021-08-01 01:05:14 -05:00
|
|
|
|
return session)))
|
|
|
|
|
(ement-view-room session room)))
|
2020-12-03 20:56:59 -06:00
|
|
|
|
|
|
|
|
|
;;;; Functions
|
|
|
|
|
|
2021-07-30 01:30:30 -05:00
|
|
|
|
;;;###autoload
|
2021-07-29 04:35:21 -05:00
|
|
|
|
(defun ement-room-list-auto-update (_session)
|
|
|
|
|
"Automatically update the room list buffer.
|
|
|
|
|
Does so when variable `ement-room-list-auto-update' is non-nil.
|
|
|
|
|
To be called in `ement-sync-callback-hook'."
|
|
|
|
|
(when (and ement-room-list-auto-update
|
|
|
|
|
(buffer-live-p (get-buffer "*Ement Rooms*")))
|
|
|
|
|
(with-current-buffer (get-buffer "*Ement Rooms*")
|
|
|
|
|
(revert-buffer))))
|
|
|
|
|
|
2020-12-03 20:56:59 -06:00
|
|
|
|
(defun ement-room-list--set-entries ()
|
|
|
|
|
"Set `tabulated-list-entries'."
|
|
|
|
|
;; Reset avatar size in case default font size has changed.
|
|
|
|
|
;; TODO: After implementing avatars.
|
|
|
|
|
;; (customize-set-variable 'ement-room-avatar-in-buffer-name-size ement-room-avatar-in-buffer-name-size)
|
|
|
|
|
|
|
|
|
|
;; NOTE: From Emacs docs:
|
|
|
|
|
|
|
|
|
|
;; This buffer-local variable specifies the entries displayed in the
|
|
|
|
|
;; Tabulated List buffer. Its value should be either a list, or a
|
|
|
|
|
;; function.
|
|
|
|
|
;;
|
|
|
|
|
;; If the value is a list, each list element corresponds to one entry,
|
|
|
|
|
;; and should have the form ‘(ID CONTENTS)’, where
|
|
|
|
|
;;
|
|
|
|
|
;; • ID is either ‘nil’, or a Lisp object that identifies the
|
|
|
|
|
;; entry. If the latter, the cursor stays on the same entry when
|
|
|
|
|
;; re-sorting entries. Comparison is done with ‘equal’.
|
|
|
|
|
;;
|
|
|
|
|
;; • CONTENTS is a vector with the same number of elements as
|
|
|
|
|
;; ‘tabulated-list-format’. Each vector element is either a
|
|
|
|
|
;; string, which is inserted into the buffer as-is, or a list
|
|
|
|
|
;; ‘(LABEL . PROPERTIES)’, which means to insert a text button by
|
|
|
|
|
;; calling ‘insert-text-button’ with LABEL and PROPERTIES as
|
|
|
|
|
;; arguments (*note Making Buttons::).
|
|
|
|
|
;;
|
|
|
|
|
;; There should be no newlines in any of these strings.
|
|
|
|
|
(setf tabulated-list-entries
|
|
|
|
|
(cl-loop for session in ement-sessions
|
|
|
|
|
append (mapcar (apply-partially #'ement-room-list--entry session)
|
|
|
|
|
(ement-session-rooms session)))))
|
|
|
|
|
|
|
|
|
|
(defun ement-room-list--entry (session room)
|
|
|
|
|
"Return entry for ROOM in SESSION for `tabulated-list-entries'."
|
2021-08-05 02:50:41 -05:00
|
|
|
|
(pcase-let* (((cl-struct ement-room id canonical-alias display-name avatar topic latest-ts summary
|
|
|
|
|
(local (map buffer room-list-avatar)))
|
2021-07-29 04:04:21 -05:00
|
|
|
|
room)
|
2021-07-28 20:12:12 -05:00
|
|
|
|
((map ('m.joined_member_count member-count)) summary)
|
2020-12-03 20:56:59 -06:00
|
|
|
|
(e-alias (or canonical-alias
|
|
|
|
|
(setf (ement-room-canonical-alias room)
|
|
|
|
|
(ement--room-alias room))
|
|
|
|
|
id))
|
|
|
|
|
;; FIXME: Figure out how to track unread status cleanly.
|
2021-07-29 04:04:21 -05:00
|
|
|
|
(e-unread (if (and buffer (buffer-modified-p buffer))
|
2021-07-29 21:04:15 -05:00
|
|
|
|
(propertize "U" 'help-echo "Unread") ""))
|
|
|
|
|
(e-buffer (if buffer (propertize "B" 'help-echo "Room has buffer") ""))
|
2021-08-05 02:50:41 -05:00
|
|
|
|
(e-avatar (if (and ement-room-list-avatars avatar)
|
|
|
|
|
(or room-list-avatar
|
|
|
|
|
(let ((new-avatar (propertize " " 'display
|
|
|
|
|
(ement--resize-image (get-text-property 0 'display avatar)
|
|
|
|
|
nil (frame-char-height)))))
|
|
|
|
|
;; alist-get doesn't seem to return the new value when used with setf?
|
|
|
|
|
(setf (alist-get 'room-list-avatar (ement-room-local room))
|
|
|
|
|
new-avatar)
|
|
|
|
|
new-avatar))
|
|
|
|
|
""))
|
2020-12-03 20:56:59 -06:00
|
|
|
|
(e-name (list (propertize (or display-name
|
2021-07-29 02:05:22 -05:00
|
|
|
|
(ement-room--room-display-name room))
|
2020-12-03 20:56:59 -06:00
|
|
|
|
'help-echo e-alias)
|
|
|
|
|
'action #'ement-room-list-action))
|
|
|
|
|
(e-topic (if topic
|
|
|
|
|
;; Remove newlines from topic. Yes, this can happen.
|
|
|
|
|
(replace-regexp-in-string "\n" "" topic t t)
|
|
|
|
|
""))
|
2021-07-29 00:22:12 -05:00
|
|
|
|
(formatted-timestamp (ts-human-format-duration (- (ts-unix (ts-now)) (/ latest-ts 1000))
|
|
|
|
|
t))
|
|
|
|
|
(e-latest (progn
|
|
|
|
|
(when (string-empty-p formatted-timestamp)
|
2021-07-29 21:02:48 -05:00
|
|
|
|
;; FIXME: Remove this check when ts-0.3 is released
|
|
|
|
|
;; (with the fix also included in ts-0.2.1).
|
|
|
|
|
(message "Ement: Please upgrade the `ts' library to fix a bug")
|
|
|
|
|
(setf formatted-timestamp "0s"))
|
2021-07-29 00:22:12 -05:00
|
|
|
|
(propertize formatted-timestamp 'value latest-ts)))
|
2020-12-03 20:56:59 -06:00
|
|
|
|
(e-session (propertize (ement-user-id (ement-session-user session))
|
|
|
|
|
'value session))
|
|
|
|
|
;; ((e-tags favorite-p low-priority-p) (ement-room-list--tags room))
|
2021-07-28 23:22:03 -05:00
|
|
|
|
(e-direct-p (if (ement-room--direct-p room session)
|
|
|
|
|
(propertize "D" 'help-echo "Direct room")
|
|
|
|
|
""))
|
2020-12-03 20:56:59 -06:00
|
|
|
|
;; (e-priority (cond (favorite-p "F")
|
|
|
|
|
;; (low-priority-p "l")
|
|
|
|
|
;; ("N")))
|
2021-07-28 20:12:12 -05:00
|
|
|
|
(e-members (number-to-string member-count)))
|
2021-07-29 04:44:40 -05:00
|
|
|
|
(list room (vector e-unread e-buffer e-direct-p
|
2021-08-05 02:50:41 -05:00
|
|
|
|
e-avatar e-name e-topic e-latest e-members
|
2021-07-29 04:04:21 -05:00
|
|
|
|
;; e-priority e-tags
|
|
|
|
|
e-session
|
|
|
|
|
;; e-avatar
|
|
|
|
|
))))
|
2020-12-03 20:56:59 -06:00
|
|
|
|
|
|
|
|
|
;; TODO: Define sorters with a macro? This gets repetitive and hard to update.
|
|
|
|
|
|
2021-07-28 20:12:12 -05:00
|
|
|
|
(defun ement-room-list-members< (a b)
|
|
|
|
|
"Return non-nil if entry A has fewer members than room B.
|
|
|
|
|
A and B should be entries from `tabulated-list-mode'."
|
2021-08-05 02:50:41 -05:00
|
|
|
|
(pcase-let* ((`(,_room [,_unread ,_buffer ,_direct ,_avatar ,_name-for-list ,_topic ,_latest ,a-members ,_session]) a)
|
|
|
|
|
(`(,_room [,_unread ,_buffer ,_direct ,_avatar ,_name-for-list ,_topic ,_latest ,b-members ,_session]) b))
|
2021-07-28 20:12:12 -05:00
|
|
|
|
(< (string-to-number a-members) (string-to-number b-members))))
|
2020-12-03 20:56:59 -06:00
|
|
|
|
|
|
|
|
|
(defun ement-room-list-latest< (a b)
|
|
|
|
|
"Return non-nil if entry A has fewer members than room B.
|
|
|
|
|
A and B should be entries from `tabulated-list-mode'."
|
2021-08-05 02:50:41 -05:00
|
|
|
|
(pcase-let* ((`(,_room-a [,_unread ,_buffer ,_direct ,_avatar ,_name-for-list ,_topic ,a-latest ,_a-members ,_session]) a)
|
|
|
|
|
(`(,_room-b [,_unread ,_buffer ,_direct ,_avatar ,_name-for-list ,_topic ,b-latest ,_b-members ,_session]) b))
|
2020-12-03 20:56:59 -06:00
|
|
|
|
(< (get-text-property 0 'value a-latest)
|
|
|
|
|
(get-text-property 0 'value b-latest))))
|
|
|
|
|
|
|
|
|
|
;;;; Footer
|
|
|
|
|
|
|
|
|
|
(provide 'ement-room-list)
|
|
|
|
|
|
|
|
|
|
;;; ement-room-list.el ends here
|