Skip to content
This repository has been archived by the owner on Mar 14, 2023. It is now read-only.

Commit

Permalink
Add: matrix-client-room-list
Browse files Browse the repository at this point in the history
  • Loading branch information
alphapapa committed Jan 8, 2019
1 parent 19a488e commit 4929a73
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 5 deletions.
7 changes: 7 additions & 0 deletions README.org
Expand Up @@ -94,6 +94,7 @@ Then run the command ~matrix-client-connect~ or ~matrix-client-frame~ to connect
- Return point to input prompt: @@html:<kbd>@@RET@@html:</kbd>@@ (with point before prompt)
- Open room in a new frame: Middle-click or press @@html:<kbd>@@<C-return>@@html:</kbd>@@ in the room list.
- Switch to the notifications buffer: @@html:<kbd>@@C-c C-n@@html:</kbd>@@
- Show room list: =/rooms=

** Notifications buffer

Expand All @@ -107,6 +108,12 @@ Here's an example of following multiple, related conversations across multiple r

[[images/notifications-buffer.png]]

** Room list

Open the room list by pressing @@html:<kbd>@@C-c C-r@@html:</kbd>@@ or calling command =matrix-client-room-list=. Just like in the dedicated frame's room-list sidebar, you can click rooms to show their buffers, right-click rooms to change their settings, and middle-click rooms to open them in a new frame.

[[images/notifications-buffer-and-room-list_spacemacs-dark.png]]

* Configuration

While there are a lot of configuration settings, here are a selection that are
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 9 additions & 5 deletions matrix-client-frame.el
Expand Up @@ -122,7 +122,8 @@ automatically."
"Open a new frame showing room at point.
Should be called in the frame sidebar buffer."
(interactive)
(when-let* ((buffer (get-text-property (1+ (line-beginning-position)) 'buffer))
(when-let* ((buffer (or (get-text-property (1+ (line-beginning-position)) 'buffer)
(oref* (tabulated-list-get-id) client-data buffer)))
(frame (make-frame
(a-list 'name (buffer-name buffer)
;; MAYBE: Save room avatar to a temp file, pass to `icon-type'.
Expand Down Expand Up @@ -151,6 +152,7 @@ Should be called in the frame sidebar buffer."
(define-key map [mouse-2] #'matrix-client-frame-sidebar-open-room-frame-mouse)
(define-key map [down-mouse-3] #'matrix-client-frame-sidebar-mouse-context-menu)
(define-key map (kbd "<C-return>") #'matrix-client-frame-sidebar-open-room-frame)
(define-key map [return] #'matrix-client-frame-sidebar-open-room-frame)
map)
"Keymap for Matrix Client frame sidebar.")

Expand Down Expand Up @@ -178,15 +180,17 @@ Should be called in the frame sidebar buffer."

(defun matrix-client-frame-sidebar-room-notify (setting)
"Set notification for room at point to SETTING."
(let* ((buffer (get-text-property (point) 'buffer))
(room (buffer-local-value 'matrix-client-room buffer)))
(let* ((room (if (eq major-mode 'matrix-client-room-list-mode)
(tabulated-list-get-id)
(buffer-local-value 'matrix-client-room (get-text-property (point) 'buffer)))) )
(matrix-client-room-command-notify room setting)
(message "%s room notifications set to: %s" (oref room display-name) setting)))

(defun matrix-client-frame-sidebar-room-priority (setting)
"Set priority for room at point to SETTING."
(let* ((buffer (get-text-property (point) 'buffer))
(room (buffer-local-value 'matrix-client-room buffer)))
(let* ((room (if (eq major-mode 'matrix-client-room-list-mode)
(tabulated-list-get-id)
(buffer-local-value 'matrix-client-room (get-text-property (point) 'buffer)))) )
(matrix-client-room-command-priority room setting)
(message "%s room priority set to: %s" (oref room display-name) setting)))

Expand Down
163 changes: 163 additions & 0 deletions matrix-client-room-list.el
@@ -0,0 +1,163 @@
;;; matrix-client-room-list.el --- Matrix Client room list -*- lexical-binding: t; -*-

;; Copyright (C) 2019 Adam Porter

;; Author: Adam Porter <adam@alphapapa.net>

;; 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:

;; Matrix room list!

;;; Code:

;;;; Requirements

(require 'mouse)
(require 'tabulated-list)

(require 'dash)
(require 's)

(require 'matrix-macros)
(require 'matrix-client-room)

;;;; Variables

;; Silence byte-compiler.
(defvar matrix-client-room-avatar-in-buffer-name-size)

;;;; Customization

;;;; Commands

(defun matrix-client-room-list (&rest _ignore)
"Show list of Matrix rooms."
(interactive)
(with-current-buffer (get-buffer-create "*Matrix Rooms*")
(matrix-client-room-list-mode)
(pop-to-buffer (current-buffer))))

(matrix-client-def-room-command rooms
:docstring "Show room list."
:insert (prog1 nil
(ignore input)
(matrix-client-room-list)))

(defun matrix-client-room-list-action (event)
"Pop to buffer for room at EVENT or point."
(interactive "e")
(mouse-set-point event)
(pop-to-buffer (oref* (tabulated-list-get-id) client-data buffer)))

(define-derived-mode matrix-client-room-list-mode tabulated-list-mode
"Matrix Client room list"
:group 'matrix-client
(use-local-map (copy-keymap matrix-client-frame-sidebar-map))
(local-set-key (kbd "g") #'tabulated-list-revert)
(local-set-key (kbd "q") #'bury-buffer)
(local-set-key (kbd "S") #'tabulated-list-sort)
(setf tabulated-list-format (vector '("U" 1 t) '("🐱" 4 t) '("Name" 25 t) '("Topic" 35 t)
'("Members" 7 matrix-client-room-list-members<)
'("D" 1 t) '("F" 1 t) '("Tags" 15 t) '("Session" 15 t))
tabulated-list-revert-hook #'matrix-client-room-list--set-entries
tabulated-list-sort-key '("Name" . nil))
(tabulated-list-init-header)
(matrix-client-room-list--set-entries)
(tabulated-list-revert))

;;;; Functions

(defun matrix-client-room-list--set-entries ()
"Set `tabulated-list-entries'."
;; Reset avatar size in case default font size has changed.
(customize-set-variable 'matrix-client-room-avatar-in-buffer-name-size matrix-client-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 (->> matrix-client-sessions
(--map (oref it rooms))
-flatten
(-map #'matrix-client-room-list--room-entry))))

(defun matrix-client-room-list--room-entry (room)
"Return entry for ROOM for `tabulated-list-entries'."
(with-slots* (((avatar client-data id display-name members session topic) room)
((buffer) client-data)
((user) session))
(-let* ((matrix-client-show-room-avatars-in-buffer-names nil)
(e-unread (if (buffer-modified-p buffer) "U" ""))
(e-avatar (if avatar (matrix-client-resize-avatar avatar) ""))
(e-name (list (matrix--room-display-name room) 'buffer buffer 'action #'matrix-client-room-list-action))
(e-topic (if topic
;; Remove newlines from topic. Yes, this can happen.
(s-replace "\n" " " topic)
""))
((e-tags . favorite-p) (matrix-client-room-list--tags room))
(e-direct-p (if (matrix-room-direct-p id session) "D" ""))
(e-favorite-p (if favorite-p "F" ""))
(e-members (format "%s" (length members))))
;; NOTE: We use the room object as the identifying object. This is allowed, according to `tabulated-list-entries',
;; but it uses the object to keep the cursor on the same entry when sorting by comparing with `equal', and I wonder
;; if that might be a performance problem in some cases.
(list room (vector e-unread e-avatar e-name e-topic e-members e-direct-p e-favorite-p e-tags user)))))

(defun matrix-client-room-list--tags (room)
"Return cons (tags-string . favorite-p) for ROOM."
(let ((favorite-p))
(with-slots (tags) room
(cons (->> (cl-loop for (tag-symbol . order) in tags
for tag-string = (symbol-name tag-symbol)
if (string-prefix-p "u." tag-string)
collect (substring tag-string 2)
else if (string= tag-string "m.favourite")
do (setf favorite-p t)
else collect (pcase tag-string
("m.lowpriority" "low-priority")
(_ tag-string)))
(-sort #'string<)
(s-join ","))
favorite-p))))

(defun matrix-client-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'."
(-let (((_room [_unread _avatar _name-for-list _topic a-members _user]) a)
((_room_[_unread _avatar _name-for-list _topic b-members _user]) b))
(< (string-to-number a-members) (string-to-number b-members))))

;;;; Footer

(provide 'matrix-client-room-list)

;;; matrix-client-room-list.el ends here
1 change: 1 addition & 0 deletions matrix-client-room.el
Expand Up @@ -22,6 +22,7 @@ Used to add a button for pending messages.")
(let ((map (make-sparse-keymap))
(mappings `(
"C-c C-n" matrix-client-switch-to-notifications-buffer
"C-c C-r" matrix-client-room-list
"r" matrix-client-reply-or-insert
"R" (lambda () (interactive) (matrix-client-reply-or-insert t))
"RET" matrix-client-ret
Expand Down
2 changes: 2 additions & 0 deletions matrix-client-standalone.el.sh
Expand Up @@ -146,6 +146,8 @@ exit
(call-interactively #'matrix-client-frame)
(delete-other-frames))

(add-hook 'matrix-after-initial-sync-hook #'matrix-client-room-list)

;; Bind some keys after loading matrix-client.

;; One of the cool things about `hippie-expand' is that it dynamically
Expand Down
10 changes: 10 additions & 0 deletions matrix-client.el
Expand Up @@ -50,6 +50,7 @@
(require 'matrix-notifications)
(require 'matrix-client-images)
(require 'matrix-client-frame)
(require 'matrix-client-room-list)

;;;; Variables

Expand Down Expand Up @@ -260,6 +261,15 @@ Intended to be called from a timer that runs at midnight."

;;;; Helper functions

(defun matrix-client-resize-avatar (avatar-string)
"Return new avatar string for AVATAR-STRING, sized accordingly.
Sized according to `matrix-client-room-avatar-in-buffer-name-size'."
;; Make a new image to avoid modifying the avatar in the header.
(let ((avatar (cl-copy-list (get-text-property 0 'display avatar-string))))
(setf (image-property avatar :max-width) matrix-client-room-avatar-in-buffer-name-size
(image-property avatar :max-height) matrix-client-room-avatar-in-buffer-name-size)
(concat (propertize " " 'display avatar) " ")))

(defun matrix-client-event-timestamp (data)
"Return timestamp of event DATA."
(let ((server-ts (float (a-get* data 'origin_server_ts)))
Expand Down
1 change: 1 addition & 0 deletions matrix-notifications.el
Expand Up @@ -80,6 +80,7 @@ Automatically trimmed to last 20 notifications.")
(defvar matrix-client-notifications-buffer-map
(let ((map (make-sparse-keymap))
(mappings `(
"C-c C-r" matrix-client-room-list
"r" matrix-client-reply-or-insert
"R" (lambda () (interactive) (matrix-client-reply-or-insert t))
"RET" matrix-client-notifications-buffer-RET
Expand Down

0 comments on commit 4929a73

Please sign in to comment.