emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals-release/ement 4be346c563 12/12: Merge: v0.14


From: ELPA Syncer
Subject: [elpa] externals-release/ement 4be346c563 12/12: Merge: v0.14
Date: Thu, 25 Jan 2024 21:58:01 -0500 (EST)

branch: externals-release/ement
commit 4be346c5635ef4a732e94d7aad8bec0cc3063b3a
Merge: 3f5f792fef 0c725c6c73
Author: Adam Porter <adam@alphapapa.net>
Commit: Adam Porter <adam@alphapapa.net>

    Merge: v0.14
---
 .github/workflows/test.yml |   3 +-
 README.org                 |  28 ++++-
 ement-notifications.el     |  57 +++++++---
 ement-notify.el            |  12 +-
 ement-room-list.el         |  37 ++++++-
 ement-room.el              | 266 ++++++++++++++++++++++++---------------------
 ement.el                   |   9 +-
 7 files changed, 254 insertions(+), 158 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index b37fb71e41..d7a68c46a6 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -41,8 +41,7 @@ jobs:
       fail-fast: false
       matrix:
         emacs_version:
-          - 26.3
-          - 27.1
+          - 27.2
           - 28.2
           - 29.1
           - snapshot
diff --git a/README.org b/README.org
index 6b1bb89ff5..1fe1bb0bec 100644
--- a/README.org
+++ b/README.org
@@ -18,7 +18,7 @@
 # ELPA badge image.
 
[[https://elpa.gnu.org/packages/ement.html][https://elpa.gnu.org/packages/ement.svg]]
 
-Ement.el is a Matrix client for Emacs.  It aims to be simple, fast, 
featureful, and reliable.
+Ement.el is a [[http://www.matrix.org/][Matrix]] client for 
[[https://www.gnu.org/software/emacs/][GNU Emacs]].  It aims to be simple, 
fast, featureful, and reliable, while integrating naturally with Emacs.
 
 Feel free to join us in the chat room: 
[[https://matrix.to/#/#ement.el:matrix.org][https://img.shields.io/matrix/ement.el:matrix.org.svg?label=%23ement.el:matrix.org]]
 
@@ -41,7 +41,7 @@ Feel free to join us in the chat room: 
[[https://matrix.to/#/#ement.el:matrix.or
 :ID:       d818f690-5f22-4eb0-83e1-4d8ce16c9e5b
 :END:
 
-The default formatting style resembles IRC clients, with each message being 
prefixed by the username (which enables powerful Emacs features, like using 
Occur to show all messages from or mentioning a user).  Alternative, built-in 
styles include an Element-like one with usernames above groups of messages, as 
well as a classic, no-margins IRC style.  Messages may be optionally displayed 
with unique colors for each user (with customizeable contrast), making it 
easier to follow conversations [...]
+The default formatting style resembles IRC clients, with each message being 
prefixed by the username (which enables powerful Emacs features, like using 
Occur to show all messages from or mentioning a user).  Alternative, built-in 
styles include an Element-like one with usernames above groups of messages, as 
well as a classic, no-margins IRC style.  Messages may be optionally displayed 
with unique colors for each user (with customizable contrast), making it easier 
to follow conversations. [...]
 
 [[images/ement-for-twim.png]]
 
@@ -122,8 +122,8 @@ Ement.el is intended to be installed with Emacs's package 
system, which will ens
 - [[#encrypted-room-support-through-pantalaimon][Encrypted room support 
through Pantalaimon]]
 :END:
 
-1. Call command ~ement-connect~ to connect.  Multiple sessions are supported, 
so you may call the command again to connect to another account.
-2. Wait for initial sync to complete (which can take a few moments--initial 
sync JSON requests can be large).
+1. Call command ~ement-connect~ to connect.  Multiple sessions are supported: 
call the command again with a ~C-u~ universal prefix to connect to another 
account.
+2. Wait for initial sync to complete (which can take a few moments--initial 
sync JSON responses can be large).
 3. Use these commands (room-related commands may be called with universal 
prefix to prompt for the room):
    - ~ement-list-rooms~ to view the list of joined rooms.
    - ~ement-view-room~ to view a room's buffer, selected with completion.
@@ -293,6 +293,26 @@ Ement.el doesn't support encrypted rooms natively, but it 
can be used transparen
 :TOC:      :depth 0
 :END:
 
+** 0.14
+
+*Additions*
+
++ Audio events are rendered as a link to the audio file.  (Thanks to 
[[https://github.com/viiru-][Arto Jantunen]].)
++ Customization group ~ement-room-list~.
++ Option ~ement-room-list-space-prefix~ is applied to space names in the room 
list (e.g. set to empty string for cleaner appearance).
++ Option ~ement-room-reaction-names-limit~ sets how many senders of a reaction 
are shown in the buffer (more than that many are shown in the tooltip).
+
+*Changes*
+
++ Bind ~TAB~ / ~BACKTAB~ to move between links in room and like buffers.  
([[https://github.com/alphapapa/ement.el/issues/113][#113]].  Thanks to 
[[https://github.com/ericsfraga][Eric S. Fraga]] for suggesting.)
+
+*Fixes*
+
++ Insertion of sender headers (when using "Elemental" message format).  
(Refactoring contributed by [[https://github.com/Stebalien][Steven Allen]].)
++ Some room event data was being unintentionally serialized to disk when 
caching the room list visibility state. 
([[https://github.com/alphapapa/ement.el/issues/256][#256]])
++ Notifications buffer restores properly when bookmarked.
++ Command ~ement-room-send-reaction~ checks for an event at point.  (Thanks to 
[[https://github.com/phil-s][Phil Sainty]].)
+
 ** 0.13
 
 *Additions*
diff --git a/ement-notifications.el b/ement-notifications.el
index 40cf7e5bb2..4df7e96b26 100644
--- a/ement-notifications.el
+++ b/ement-notifications.el
@@ -62,15 +62,35 @@ is passed through `ement--make-event'."
 (declare-function ement-room-list "ement-room-list")
 (defvar ement-notifications-mode-map
   (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "<return>") #'ement-notifications-jump)
+    (define-key map [mouse-1] #'ement-notifications-jump-mouse)
+    (define-key map [mouse-2] #'ement-notifications-jump-mouse)
     (define-key map (kbd "S-<return>") #'ement-notify-reply)
     (define-key map (kbd "M-g M-l") #'ement-room-list)
     (define-key map (kbd "M-g M-m") #'ement-notify-switch-to-mentions-buffer)
     (define-key map (kbd "M-g M-n") 
#'ement-notify-switch-to-notifications-buffer)
     (define-key map [remap scroll-down-command] 
#'ement-notifications-scroll-down-command)
     (define-key map [remap mwheel-scroll] #'ement-notifications-mwheel-scroll)
-    (make-composed-keymap (list map button-buffer-map) 'view-mode-map))
+    (make-composed-keymap (list map) 'view-mode-map))
   "Map for Ement notification buffers.")
 
+(cl-defun ement-notifications-jump (&optional (pos (point)))
+  "Jump to Matrix event at POS."
+  (interactive)
+  (let ((session (get-text-property pos 'session))
+        (room (get-text-property pos 'room))
+        (event (get-text-property pos 'event)))
+    (ement-view-room room session)
+    (ement-room-goto-event event)))
+
+(defun ement-notifications-jump-mouse (event)
+  "Jump to Matrix event at EVENT."
+  (interactive "e")
+  (let ((pos (posn-point (event-start event))))
+    (if (button-at pos)
+        (push-button pos)
+      (ement-notifications-jump pos))))
+
 (defvar ement-notifications-hook '(ement-notifications-log-to-buffer)
   "Functions called for `ement-notifications' notifications.
 Each function is called with two arguments, the session and the
@@ -197,19 +217,24 @@ to `ement-api', which see."
                        (inhibit-read-only t)
                        (start) (end))
             (ewoc-goto-node ement-ewoc new-node)
-            (setf start (point))
-            (if-let (next-node (ewoc-next ement-ewoc new-node))
-                (ewoc-goto-node ement-ewoc next-node)
-              (goto-char (point-max)))
-            (setf end (- (point) 2))
+            ;; Apply the button properties only to the room and sender names,
+            ;; allowing buttons in the rest of the message to remain separate.
+            (setf start (point)
+                  end (save-excursion
+                        (re-search-forward (rx "> "))))
+            (add-text-properties start end '( button (t)
+                                              category default-button
+                                              action 
ement-notify-button-action))
+            ;; Apply the session, room, and event properties to the whole 
event.
+            (setf end (save-excursion
+                        (if-let ((next-node (ewoc-next ement-ewoc new-node)))
+                            (ewoc-location next-node)
+                          (point-max))))
             (add-text-properties start end
-                                 (list 'button '(t)
-                                       'category 'default-button
-                                       'action #'ement-notify-button-action
-                                       'session session
+                                 (list 'session session
                                        'room ement-room
                                        'event event))
-            ;; Remove button face property.
+            ;; Remove button face property from the whole event.
             (alter-text-property start end 'face
                                  (lambda (face)
                                    (pcase face
@@ -260,10 +285,14 @@ to `ement-api', which see."
         (cons 'buffer-name (buffer-name))
         (cons 'handler #'ement-notifications-bookmark-handler)))
 
-(defun ement-notifications-bookmark-handler (bookmark)
+(defun ement-notifications-bookmark-handler (_bookmark)
   "Show `ement-notifications' buffer for BOOKMARK."
-  (pcase-let ((`(,_bookmark-name . ,(map buffer-name)) bookmark))
-    (switch-to-buffer (ement-notifications--log-buffer :name buffer-name))))
+  ;; FIXME: Handle multiple sessions.
+  ;; FIXME: This doesn't work quite correctly when the buffer isn't already 
open, because
+  ;; the command is asynchronous in that case, so the buffer can be displayed 
in the wrong
+  ;; window.  Fixing this would be hacky and awkward, but a partial solution 
is probably
+  ;; possible.
+  (ement-notifications (ement-complete-session)))
 
 ;;; Footer
 
diff --git a/ement-notify.el b/ement-notify.el
index c341678b40..7c52371f32 100644
--- a/ement-notify.el
+++ b/ement-notify.el
@@ -38,16 +38,6 @@
 
 ;;;; Variables
 
-(declare-function ement-room-list "ement-room-list")
-(defvar ement-notify-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map (kbd "S-<return>") #'ement-notify-reply)
-    (define-key map (kbd "M-g M-l") #'ement-room-list)
-    (define-key map (kbd "M-g M-m") #'ement-notify-switch-to-mentions-buffer)
-    (define-key map (kbd "M-g M-n") 
#'ement-notify-switch-to-notifications-buffer)
-    (make-composed-keymap (list map button-buffer-map) 'view-mode-map))
-  "Map for Ement notification buffers.")
-
 (defvar ement-notify-dbus-p
   (and (featurep 'dbusbind)
        (require 'dbus nil :no-error)
@@ -141,7 +131,7 @@ can help distinguish messages by room."
 (defcustom ement-notify-room-avatars t
   "Show room avatars in the notifications buffers.
 This shows room avatars at the left of the window margin in
-notification buffers.  It's not customizeable beyond that due to
+notification buffers.  It's not customizable beyond that due to
 limitations and complexities of displaying strings and images in
 margins in Emacs.  But it's useful, anyway."
   :type 'boolean)
diff --git a/ement-room-list.el b/ement-room-list.el
index 232ef6d2c8..f0194db803 100644
--- a/ement-room-list.el
+++ b/ement-room-list.el
@@ -56,6 +56,29 @@ a symbol, it should be unquoted.."
        (mouse-set-point event)
        (call-interactively #',command))))
 
+;;;; Types
+
+(defclass ement-room-list-section (magit-section)
+  ;; We define this class so we can use it as the type of section we insert, 
so we can
+  ;; define a method to return identifiers for our section type, so section 
visibility can
+  ;; be cached concisely (i.e. without storing room event data in the values, 
which can
+  ;; serialize to hundreds of megabytes after receiving many events).
+  nil)
+
+(cl-defmethod magit-section-ident-value ((section ement-room-list-section))
+  ;; FIXME: The name of each taxy could be ambiguous.  Best would be to use the
+  ;; hierarchical path, but since the taxys aren't doubly linked, that isn't 
easily done.
+  ;; Could probably be worked around by binding a special variable around the 
creation of
+  ;; the taxy hierarchy that would allow the path to be saved into each taxy.
+  (pcase-exhaustive (oref section value)
+    ((and (cl-type taxy-magit-section) it)
+     (taxy-name it))
+    (`[,(and (cl-type ement-room) room)
+       ,(and (cl-type ement-session) session)]
+     (vector (ement-user-id (ement-session-user session))
+             (ement-room-id room)))
+    ((pred null) nil)))
+
 ;;;; Variables
 
 (declare-function ement-room-toggle-space "ement-room")
@@ -99,6 +122,10 @@ Set automatically when `ement-room-list-mode' is 
activated.")
 
 ;;;; Customization
 
+(defgroup ement-room-list nil
+  "Options for room list buffers."
+  :group 'ement)
+
 (defcustom ement-room-list-auto-update t
   "Automatically update the taxy-based room list buffer."
   :type 'boolean)
@@ -107,6 +134,10 @@ Set automatically when `ement-room-list-mode' is 
activated.")
   "Show room avatars in the room list."
   :type 'boolean)
 
+(defcustom ement-room-list-space-prefix "Space: "
+  "Prefix applied to space names."
+  :type 'string)
+
 ;;;;; Faces
 
 (defface ement-room-list-direct
@@ -205,7 +236,7 @@ from recent to non-recent for rooms updated in the past 
hour.")
                          (space-name (if parent-room
                                          (ement-room-display-name parent-room)
                                        id)))
-                    (concat "Space: " space-name))))
+                    (concat ement-room-list-space-prefix space-name))))
       (when-let ((key (if id
                           ;; ID specified.
                           (cond ((or (member id parents)
@@ -215,7 +246,7 @@ from recent to non-recent for rooms updated in the past 
hour.")
                                 ((and (equal type "m.space")
                                       (equal id (ement-room-id room)))
                                  ;; Room is a specified space.
-                                 (or name (concat "Space: " 
(ement-room-display-name room)))))
+                                 (or name (concat ement-room-list-space-prefix 
(ement-room-display-name room)))))
                         ;; ID not specified.
                         (pcase (length parents)
                           (0 nil)
@@ -679,7 +710,7 @@ DISPLAY-BUFFER-ACTION is nil, the buffer is not displayed."
             (save-excursion
               (taxy-magit-section-insert taxy :items 'first
                 ;; :blank-between-depth bufler-taxy-blank-between-depth
-                :initial-depth 0))
+                :initial-depth 0 :section-class 'ement-room-list-section))
             (if-let* ((section-ident)
                       (section (magit-get-section section-ident)))
                 (goto-char (oref section start))
diff --git a/ement-room.el b/ement-room.el
index 143c0dcf48..423551dd56 100644
--- a/ement-room.el
+++ b/ement-room.el
@@ -137,6 +137,8 @@ Used to, e.g. call `ement-room-compose-org'.")
     (define-key map (kbd "m") #'ement-room-mark-read)
     (define-key map [remap scroll-down-command] 
#'ement-room-scroll-down-command)
     (define-key map [remap mwheel-scroll] #'ement-room-mwheel-scroll)
+    (define-key map (kbd "<tab>") #'forward-button)
+    (define-key map (kbd "<backtab>") #'backward-button)
 
     ;; Switching
     (define-key map (kbd "M-g M-l") #'ement-room-list)
@@ -250,6 +252,12 @@ Does not include filenames, emotes, etc.")
 Called with two arguments, the room and the session."
   :type 'hook)
 
+(defcustom ement-room-reaction-names-limit 3
+  "Up to this many users, show a reaction's senders' names.
+If more than this many users have sent a reaction, show the
+number of senders instead (and the names in a tooltip)."
+  :type 'natnum)
+
 ;;;;; Faces
 
 (defface ement-room-name
@@ -708,27 +716,28 @@ number (to darken rather than lighten)."
   "Highlight event at POSITION while evaluating BODY."
   ;; MAYBE: Accept a marker for POSITION.
   (declare (indent 1))
-  `(let* ((node (ewoc-locate ement-ewoc ,position))
-          (event (ewoc-data node))
-          ement-room-replying-to-overlay)
-     (unless (and (ement-event-p event)
-                  (ement-event-id event))
-       (error "No event at point"))
-     (unwind-protect
-         (progn
-           (setf ement-room-replying-to-overlay
-                 (make-overlay (ewoc-location node)
-                               ;; NOTE: It doesn't seem possible to get the 
end position of
-                               ;; a node, so if there is no next node, we use 
point-max.
-                               ;; But this might break if we were to use an 
EWOC footer.
-                               (if (ewoc-next ement-ewoc node)
-                                   (ewoc-location (ewoc-next ement-ewoc node))
-                                 (point-max))))
-           (overlay-put ement-room-replying-to-overlay 'face 'highlight)
-           ,@body)
-       (when (overlayp ement-room-replying-to-overlay)
-         (delete-overlay ement-room-replying-to-overlay))
-       (setf ement-room-replying-to-overlay nil))))
+  (let ((node/g (gensym "node")) (event/g (gensym "event")))
+    `(let* ((,node/g (ewoc-locate ement-ewoc ,position))
+            (,event/g (ewoc-data ,node/g))
+            ement-room-replying-to-overlay)
+       (unless (and (ement-event-p ,event/g)
+                    (ement-event-id ,event/g))
+         (error "No event at point"))
+       (unwind-protect
+           (progn
+             (setf ement-room-replying-to-overlay
+                   (make-overlay (ewoc-location ,node/g)
+                                 ;; NOTE: It doesn't seem possible to get the 
end position of
+                                 ;; a node, so if there is no next node, we 
use point-max.
+                                 ;; But this might break if we were to use an 
EWOC footer.
+                                 (if (ewoc-next ement-ewoc ,node/g)
+                                     (ewoc-location (ewoc-next ement-ewoc 
,node/g))
+                                   (point-max))))
+             (overlay-put ement-room-replying-to-overlay 'face 'highlight)
+             ,@body)
+         (when (overlayp ement-room-replying-to-overlay)
+           (delete-overlay ement-room-replying-to-overlay))
+         (setf ement-room-replying-to-overlay nil)))))
 
 (defmacro ement-room-with-typing (&rest body)
   "Send typing notifications around BODY.
@@ -941,8 +950,8 @@ spec) without requiring all events to use the same margin 
width."
 
 (ement-room-define-event-formatter ?r
   "Reactions."
-  (ignore room session)
-  (ement-room--format-reactions event))
+  (ignore session)
+  (ement-room--format-reactions event room))
 
 (ement-room-define-event-formatter ?t
   "Timestamp."
@@ -1774,17 +1783,22 @@ Interactively, to event at point."
                    (replying-to-event (ement--original-event-for event 
ement-session)))
         (ement-room-send-message room session :body body :replying-to-event 
replying-to-event)))))
 
-(defun ement-room-send-reaction (key position)
+(defun ement-room-send-reaction (key position &optional event)
   "Send reaction of KEY to event at POSITION.
 Interactively, send reaction to event at point.  KEY should be a
 reaction string, e.g. \"👍\"."
   (interactive
-   (list (char-to-string (read-char-by-name "Reaction (prepend \"*\" for 
substring search): "))
-         (point)))
+   (let ((event (ewoc-data (ewoc-locate ement-ewoc))))
+     (unless (ement-event-p event)
+       (user-error "No event at point"))
+     (list (char-to-string (read-char-by-name "Reaction (prepend \"*\" for 
substring search): "))
+           (point)
+           event)))
   ;; SPEC: MSC2677 <https://github.com/matrix-org/matrix-doc/pull/2677>
   ;; HACK: We could simplify this by storing the key in a text property...
   (ement-room-with-highlighted-event-at position
-    (pcase-let* ((event (or (ewoc-data (ewoc-locate ement-ewoc position))
+    (pcase-let* ((event (or event
+                            (ewoc-data (ewoc-locate ement-ewoc position))
                             (user-error "No event at point")))
                  ;; NOTE: Sadly, `face-at-point' doesn't work here because, 
e.g. if
                  ;; hl-line-mode is enabled, it only returns the hl-line face.
@@ -3006,63 +3020,40 @@ the first and last nodes in the buffer, respectively."
   "Insert sender headers into EWOC.
 Inserts headers between START-NODE and END-NODE, which default to
 the first and last nodes in the buffer, respectively."
-  (cl-labels ((read-marker-p (data)
-                (member data '(ement-room-fully-read-marker
-                               ement-room-read-receipt-marker)))
-              (message-event-p (data)
+  (cl-labels ((message-event-p (data)
                 (and (ement-event-p data)
-                     (equal "m.room.message" (ement-event-type data))))
-              (insert-sender-before (node)
-                (ewoc-enter-before ewoc node (ement-event-sender (ewoc-data 
node)))))
-    (let* ((event-node (if (ement-event-p (ewoc-data start-node))
-                           start-node
-                         (ement-room--ewoc-next-matching ewoc start-node
-                           #'ement-event-p)))
-           (prev-node (when event-node
-                        ;; Just in case...
-                        (ewoc-prev ewoc event-node))))
-      (while (and event-node
-                  ;; I don't like looking up the location of these nodes on 
every loop
-                  ;; iteration, but it seems like the only reliable way to 
determine
-                  ;; whether we've reached the end node.  However, when this 
function is
-                  ;; called for short batches of events (or even a single 
event, like when
-                  ;; called from `ement-room--insert-event'), the overhead 
should be
-                  ;; minimal.
-                  (<= (ewoc-location event-node) (ewoc-location end-node)))
-        (when (message-event-p (ewoc-data event-node))
-          (if (not prev-node)
-              ;; No previous node and event is a message: insert header.
-              (insert-sender-before event-node)
-            ;; Previous node exists.
-            (when (read-marker-p (ewoc-data prev-node))
-              ;; Previous node is a read marker: we want to act as if they 
don't exist, so
-              ;; we set `prev-node' to the non-marker node before it.
-              (setf prev-node (ement-room--ewoc-next-matching ewoc prev-node
-                                (lambda (data)
-                                  (not (read-marker-p data)))
-                                #'ewoc-prev)))
-            (when prev-node
-              ;; A previous node still exists: maybe we need to add a header.
-              (cl-typecase (ewoc-data prev-node)
-                (ement-event
-                 ;; Previous node is an event.
-                 (when (and (message-event-p (ewoc-data prev-node))
-                            (not (equal (ement-event-sender (ewoc-data 
prev-node))
-                                        (ement-event-sender (ewoc-data 
event-node)))))
-                   ;; Previous node is a message event with a different 
sender: insert
-                   ;; header.
-                   (insert-sender-before event-node)))
-                ((or ement-user ement-room-membership-events)
-                 ;; Previous node is a user or coalesced membership events: do 
not insert
-                 ;; header.
-                 nil)
-                (t
-                 ;; Previous node is not an event and not a read marker: 
insert header.
-                 (insert-sender-before event-node))))))
-        (setf event-node (ement-room--ewoc-next-matching ewoc event-node
-                           #'ement-event-p)
-              prev-node (when event-node
-                          (ewoc-prev ewoc event-node)))))))
+                     (equal "m.room.message" (ement-event-type data)))))
+    (when (and start-node (not (message-event-p (ewoc-data start-node))))
+      ;; Start node not a message event: forward to next message event (and if 
none are
+      ;; found, there's nothing to do).
+      (setf start-node (ement-room--ewoc-next-matching ewoc start-node 
#'message-event-p)))
+    (when end-node
+      ;; Set end node to first message event after it.  (This simplifies the 
loop by
+      ;; continuing until finding `end-node' or the last node, and ensures we 
fix headers
+      ;; after any inserted messages.)
+      (setf end-node (ement-room--ewoc-next-matching ewoc end-node 
#'message-event-p)))
+    (let ((event-node start-node) prev-node)
+      (while (and event-node (not (eq event-node end-node)))
+        (setf prev-node
+              ;; Find previous message or user header.
+              (ement-room--ewoc-next-matching ewoc event-node
+                (lambda (data)
+                  (or (ement-user-p data) (message-event-p data)))
+                #'ewoc-prev))
+        (let ((sender (ement-event-sender (ewoc-data event-node))))
+          (cond ((not prev-node)
+                 ;; No previous message/sender: insert sender.
+                 (ewoc-enter-before ewoc event-node sender))
+                ((ement-user-p (ewoc-data prev-node))
+                 ;; Previous node is a sender.
+                 (unless (equal sender (ewoc-data prev-node))
+                   ;; Previous node is the wrong sender: fix it.
+                   (ewoc-set-data prev-node sender)))
+                ((and (message-event-p (ewoc-data prev-node))
+                      (not (equal sender (ement-event-sender (ewoc-data 
prev-node)))))
+                 ;; Previous node is a message from a different sender: insert 
header.
+                 (ewoc-enter-before ewoc event-node sender))))
+        (setf event-node (ement-room--ewoc-next-matching ewoc event-node 
#'message-event-p))))))
 
 (defun ement-room--coalesce-nodes (a b ewoc)
   "Try to coalesce events in nodes A and B in EWOC.
@@ -3110,6 +3101,9 @@ Search starts from node START and moves by NEXT."
                   ((pred ement-event-p) t)
                   ((pred ement-room-membership-events-p) t)
                   (`(ts . ,_) t)))
+              (read-marker-p
+                (data) (member data '(ement-room-fully-read-marker
+                                      ement-room-read-receipt-marker)))
               (node-ts (data)
                 (pcase data
                   ((pred ement-event-p) (ement-event-origin-server-ts data))
@@ -3129,7 +3123,7 @@ Search starts from node START and moves by NEXT."
       ;; HACK: Insert after any read markers.
       (cl-loop for node-after-node-before = (ewoc-next ewoc event-node-before)
                while node-after-node-before
-               while (not (ement-event-p (ewoc-data node-after-node-before)))
+               while (read-marker-p (ewoc-data node-after-node-before))
                do (setf event-node-before node-after-node-before))
       (setf new-node (if (not event-node-before)
                          (progn
@@ -3347,50 +3341,54 @@ Formats according to `ement-room-message-format-spec', 
which see."
           (propertize " "
                       'display ement-room-event-separator-display-property)))
 
-(defun ement-room--format-reactions (event)
-  "Return formatted reactions to EVENT."
+(defun ement-room--format-reactions (event room)
+  "Return formatted reactions to EVENT in ROOM."
   ;; TODO: Like other events, pop to a buffer showing the raw reaction events 
when a key is pressed.
-  (if-let ((reactions (map-elt (ement-event-local event) 'reactions)))
-      (cl-labels ((format-reaction (ks)
-                    (pcase-let* ((`(,key . ,senders) ks)
-                                 (key (propertize key 'face 
'ement-room-reactions-key))
-                                 (count (propertize (format " (%s)" (length 
senders))
-                                                    'face 
'ement-room-reactions))
-                                 (string
-                                  (propertize (concat key count)
-                                              'button '(t)
-                                              'category 'default-button
-                                              'action 
#'ement-room-reaction-button-action
-                                              'follow-link t
-                                              'help-echo (lambda (_window 
buffer _pos)
-                                                           ;; NOTE: If the 
reaction key string is a Unicode character composed
-                                                           ;; with, e.g. 
"VARIATION SELECTOR-16", `string-to-char' ignores the
-                                                           ;; composed 
modifier/variation-selector and just returns the first
-                                                           ;; character of the 
string.  This should be fine, since it's just
-                                                           ;; for the tooltip.
-                                                           (concat
-                                                            
(get-char-code-property (string-to-char key) 'name) ": "
-                                                            (senders-names 
senders (buffer-local-value 'ement-room buffer))))))
-                                 (local-user-p (cl-member (ement-user-id 
(ement-session-user ement-session)) senders
-                                                          :key #'ement-user-id 
:test #'equal)))
-                      (when local-user-p
-                        (add-face-text-property 0 (length string) '(:box 
(:style pressed-button) :inverse-video t)
-                                                nil string))
-                      (ement--remove-face-property string 'button)
-                      string))
-                  (senders-names (senders room)
-                    (cl-loop for sender in senders
-                             collect (ement--user-displayname-in room sender)
-                             into names
-                             finally return (string-join names ", "))))
+  (cl-labels
+      ((format-reaction (ks)
+         (pcase-let* ((`(,key . ,senders) ks)
+                      (key (propertize key 'face 'ement-room-reactions-key))
+                      (count (propertize (format " (%s)"
+                                                 (if (length> senders 
ement-room-reaction-names-limit)
+                                                     (length senders)
+                                                   (senders-names senders 
room)))
+                                         'face 'ement-room-reactions))
+                      (string
+                       (propertize (concat key count)
+                                   'button '(t)
+                                   'category 'default-button
+                                   'action #'ement-room-reaction-button-action
+                                   'follow-link t
+                                   'help-echo (lambda (_window buffer _pos)
+                                                ;; NOTE: If the reaction key 
string is a Unicode character composed
+                                                ;; with, e.g. "VARIATION 
SELECTOR-16", `string-to-char' ignores the
+                                                ;; composed 
modifier/variation-selector and just returns the first
+                                                ;; character of the string.  
This should be fine, since it's just
+                                                ;; for the tooltip.
+                                                (concat
+                                                 (get-char-code-property 
(string-to-char key) 'name) ": "
+                                                 (senders-names senders 
(buffer-local-value 'ement-room buffer))))))
+                      (local-user-p (cl-member (ement-user-id 
(ement-session-user ement-session)) senders
+                                               :key #'ement-user-id :test 
#'equal)))
+           (when local-user-p
+             (add-face-text-property 0 (length string) '(:box (:style 
pressed-button) :inverse-video t)
+                                     nil string))
+           (ement--remove-face-property string 'button)
+           string))
+       (senders-names (senders room)
+         (cl-loop for sender in senders
+                  collect (ement--user-displayname-in room sender)
+                  into names
+                  finally return (string-join names ", "))))
+    (if-let ((reactions (map-elt (ement-event-local event) 'reactions)))
         (cl-loop with keys-senders
                  for reaction in reactions
                  for key = (map-nested-elt (ement-event-content reaction) 
'(m.relates_to key))
                  for sender = (ement-event-sender reaction)
                  do (push sender (alist-get key keys-senders nil nil 
#'string=))
                  finally do (setf keys-senders (cl-sort keys-senders #'> :key 
(lambda (pair) (length (cdr pair)))))
-                 finally return (concat "\n  " (mapconcat #'format-reaction 
keys-senders "  "))))
-    ""))
+                 finally return (concat "\n  " (mapconcat #'format-reaction 
keys-senders "  ")))
+      "")))
 
 (cl-defun ement-room--format-message (event room session &optional (format 
ement-room-message-format-spec))
   "Return EVENT in ROOM on SESSION formatted according to FORMAT.
@@ -3516,6 +3514,7 @@ If FORMATTED-P, return the formatted body content, when 
available."
                            ("m.image" (ement-room--format-m.image event))
                            ("m.file" (ement-room--format-m.file event))
                            ("m.video" (ement-room--format-m.video event))
+                           ("m.audio" (ement-room--format-m.audio event))
                            (_ (if (or local-redacted-by unsigned-redacted-by)
                                   nil
                                 (format "[unsupported msgtype: %s]" msgtype 
))))))
@@ -4315,6 +4314,31 @@ Then invalidate EVENT's node to show the image."
             (propertize " "
                         'display '(space :relative-height 1.5)))))
 
+(defun ement-room--format-m.audio (event)
+  "Return \"m.audio\" EVENT formatted as a string."
+  (pcase-let* (((cl-struct ement-event
+                           (content (map body
+                                         ('info (map mimetype duration size))
+                                         ('url mxc-url))))
+                event)
+               (url (when mxc-url
+                      (ement--mxc-to-url mxc-url ement-session)))
+               (human-size (file-size-human-readable size))
+               (human-duration (format-seconds "%m:%s" (/ duration 1000)))
+               (string (format "[audio: %s (%s) (%s) (%s)]" body mimetype 
human-duration human-size)))
+    (concat (propertize string
+                        'action #'browse-url
+                        'button t
+                        'button-data url
+                        'category t
+                        'face 'button
+                        'follow-link t
+                        'help-echo url
+                        'keymap button-map
+                        'mouse-face 'highlight)
+            (propertize " "
+                        'display '(space :relative-height 1.5)))))
+
 ;;;;; Org format sending
 
 ;; Some of these declarations may need updating as Org changes.
diff --git a/ement.el b/ement.el
index 125c201f50..4523c23a81 100644
--- a/ement.el
+++ b/ement.el
@@ -5,8 +5,8 @@
 ;; Author: Adam Porter <adam@alphapapa.net>
 ;; Maintainer: Adam Porter <adam@alphapapa.net>
 ;; URL: https://github.com/alphapapa/ement.el
-;; Version: 0.13
-;; Package-Requires: ((emacs "27.1") (map "2.1") (persist "0.5") (plz "0.6") 
(taxy "0.10") (taxy-magit-section "0.12.1") (svg-lib "0.2.5") (transient 
"0.3.7"))
+;; Version: 0.14
+;; Package-Requires: ((emacs "27.1") (map "2.1") (persist "0.5") (plz "0.6") 
(taxy "0.10") (taxy-magit-section "0.13") (svg-lib "0.2.5") (transient "0.3.7"))
 ;; Keywords: comm
 
 ;; This program is free software; you can redistribute it and/or modify
@@ -569,6 +569,7 @@ a filter ID).  When unspecified, the value of
 (defun ement--sync-callback (session data)
   "Process sync DATA for SESSION.
 Runs `ement-sync-callback-hook' with SESSION."
+  (ement-debug (ement-user-id (ement-session-user session)))
   ;; Remove the sync first.  We already have the data from it, and the
   ;; process has exited, so it's safe to run another one.
   (setf (map-elt ement-syncs session) nil)
@@ -685,7 +686,7 @@ Also used for left rooms, in which case STATUS should be 
set to
                (latest-timestamp))
     (setf (ement-room-status room) status
           (ement-room-unread-notifications room) unread-notifications)
-    ;; NOTE: The idea is that, assuming that events in the sync reponse are in
+    ;; NOTE: The idea is that, assuming that events in the sync response are in
     ;; chronological order, we push them to the lists in the room slots in 
that order,
     ;; leaving the head of each list as the most recent event of that type.  
That means
     ;; that, e.g. the room state events may be searched in order to find, e.g. 
the most
@@ -822,6 +823,8 @@ Adds sender to `ement-users' when necessary."
 
 ;;;;; Reading/writing sessions
 
+;; TODO: Use `persist' and/or `multisession'.
+
 (defun ement--read-sessions ()
   "Return saved sessions alist read from disk.
 Returns nil if unable to read `ement-sessions-file'."



reply via email to

[Prev in Thread] Current Thread [Next in Thread]