Skip to content

Commit

Permalink
Change/Fix: Cache computed displaynames
Browse files Browse the repository at this point in the history
When a user's displayname was unset, we did not cache the computed
displayname.  In most cases that seemed to be no problem; but in the
case of a direct room in which the other user had no displayname set,
it would mean iterating over the events and looking for one setting
the displayname every time that EMENT--USER-DISPLAYNAME-IN is called.

When the room list buffer is updated, that function is called for each
such room; over time, as such rooms' gain more events, that means
taking longer to iterate over them, which means taking longer to
update the room list buffer.

Now we cache the computed displayname even if unset; and if a new
member event comes in that sets the displayname, we force
recalculation.

Fixes #298.

Reported-by: Rutherther <https://github.com/Rutherther>
  • Loading branch information
alphapapa committed Sep 21, 2024
1 parent 3f87a95 commit b20fda0
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 31 deletions.
1 change: 1 addition & 0 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ Ement.el doesn't support encrypted rooms natively, but it can be used transparen
+ Set ~filter-buffer-substring-function~ in room buffers to prevent undesired text properties from being included in copied text. ([[https://github.com/alphapapa/ement.el/pull/278][#278]]. Thanks to [[https://github.com/phil-s][Phil Sainty]].)
+ Command ~ement-disconnect~ no longer shows an error message. ([[https://github.com/alphapapa/ement.el/issues/208][#208]].)
+ Retrieval of earlier events in a just-joined room. ([[https://github.com/alphapapa/ement.el/issues/148][#148]]. Thanks to [[https://github.com/MagicRB][Richard Brežák]] for reporting, and to [[https://github.com/phil-s][Phil Sainty]] for testing.)
+ Cache computed displaynames in rooms (avoiding unnecessary reiteration and recalculation). ([[https://github.com/alphapapa/ement.el/issues/298][#298]]. Thanks to [[https://github.com/Rutherther][Rutherther]] for reporting and testing, and to [[https://github.com/phil-s][Phil Sainty]].)

** 0.15.1

Expand Down
49 changes: 25 additions & 24 deletions ement-lib.el
Original file line number Diff line number Diff line change
Expand Up @@ -1642,33 +1642,34 @@ problems."
(cl-incf (ement-session-transaction-id session))
(format-time-string "%s")))

(defun ement--user-displayname-in (room user)
"Return the displayname for USER in ROOM."
(defun ement--user-displayname-in (room user &optional recalculatep)
"Return the displayname for USER in ROOM.
If RECALCULATEP, force recalculation; otherwise return a cached
name if available."
;; SPEC: <https://matrix.org/docs/spec/client_server/r0.6.1#calculating-the-display-name-for-a-user>.
;; FIXME: Add step 3 of the spec. For now we skip to step 4.

;; NOTE: Both state and timeline events must be searched. (A helpful user
;; in #matrix-dev:matrix.org, Michael (t3chguy), clarified this for me).
(if-let ((cached-name (gethash user (ement-room-displaynames room))))
cached-name
;; Put timeline events before state events, because IIUC they should be more recent.
(cl-labels ((join-displayname-event-p (event)
(and (eq user (ement-event-sender event))
(equal "m.room.member" (ement-event-type event))
(equal "join" (alist-get 'membership (ement-event-content event)))
(alist-get 'displayname (ement-event-content event)))))
;; FIXME: Should probably sort the relevant events to get the latest one.
(if-let* ((displayname (or (cl-loop for event in (ement-room-timeline room)
when (join-displayname-event-p event)
return (alist-get 'displayname (ement-event-content event)))
(cl-loop for event in (ement-room-state room)
when (join-displayname-event-p event)
return (alist-get 'displayname (ement-event-content event)))))
(calculated-name displayname))
(puthash user calculated-name (ement-room-displaynames room))
;; No membership state event: use pre-calculated displayname or ID.
(or (ement-user-displayname user)
(ement-user-id user))))))
(or (unless recalculatep
(gethash user (ement-room-displaynames room)))
(cl-labels ((event-sets-displayname (event)
(and (eq user (ement-event-sender event))
(equal "m.room.member" (ement-event-type event))
(equal "join" (alist-get 'membership (ement-event-content event)))
(alist-get 'displayname (ement-event-content event)))))
;; Search timeline events before state events, because IIUC they should be more
;; recent. Also, we assume that the timeline and state events are sorted
;; most-recent-first, so the first such event found is the one to use.
(puthash user (or (cl-loop for event in (ement-room-timeline room)
when (event-sets-displayname event)
return it)
(cl-loop for event in (ement-room-state room)
when (event-sets-displayname event)
return it)
;; FIXME: Add step 3 of the spec. For now we skip to step 4.
;; No membership state event: use pre-calculated displayname or ID.
(ement-user-displayname user)
(ement-user-id user))
(ement-room-displaynames room)))))

(defun ement--xml-escape-string (string)
"Return STRING having been escaped with `xml-escape-string'.
Expand Down
23 changes: 16 additions & 7 deletions ement.el
Original file line number Diff line number Diff line change
Expand Up @@ -983,17 +983,26 @@ and `session' to the session. Adds function to
event)
(user (or (gethash state-key ement-users)
(puthash state-key
(make-ement-user :id state-key :avatar-url avatar-url
;; NOTE: The spec doesn't seem to say whether the
;; displayname in the member event applies only to the
;; room or is for the user generally, so we'll save it
;; in the struct anyway.
:displayname displayname)
(make-ement-user
:id state-key :avatar-url avatar-url
;; NOTE: The spec doesn't seem to say whether the
;; displayname in the member event applies only to
;; the room or is for the user generally, so we'll
;; save it in the struct anyway.
;; FIXME: This is probably wrong: it probably means
;; overwriting the global displayname with any
;; room-specific one that was most recently processed.
:displayname displayname)
ement-users))))
(pcase membership
("join"
(puthash state-key user members)
(puthash user displayname (ement-room-displaynames room)))
(if displayname
;; NOTE: This handler is only called for new events, not when retrieving old events.
;; Therefore it's safe to update the cached displayname from such an event.
(puthash user displayname (ement-room-displaynames room))
;; No displayname set for this room: recalculate.
(ement--user-displayname-in room user 'recalculate)))
(_ (remhash state-key members)
(remhash user (ement-room-displaynames room))))))

Expand Down

0 comments on commit b20fda0

Please sign in to comment.