From e7acf292a5f3c4975f5bcee5dba479348568aa1d Mon Sep 17 00:00:00 2001 From: Adam Porter Date: Sat, 12 Oct 2024 21:04:50 -0500 Subject: [PATCH] Fix: Persist session data for reconnect When disconnecting from a session, we now keep the slots necessary for reconnecting, and we persist those slots. Previously all of the session's data was discarded after writing, so if multiple accounts were disconnected but not at the same time, the first session to be disconnected was forgotten. Fixes #243. Reported-by: formula-spectre --- ement.el | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/ement.el b/ement.el index 0cb2fae4..21daa6e1 100644 --- a/ement.el +++ b/ement.el @@ -350,9 +350,6 @@ in them won't work." (interactive (list (if current-prefix-arg (mapcar #'cdr ement-sessions) (list (ement-complete-session))))) - (when ement-save-sessions - ;; Write sessions before we remove them from the variable. - (ement--write-sessions ement-sessions)) (dolist (session sessions) (let ((user-id (ement-user-id (ement-session-user session)))) (when-let ((process (map-elt ement-syncs session))) @@ -362,11 +359,16 @@ in them won't work." (delete-process process)) ;; NOTE: I'd like to use `map-elt' here, but not until ;; is fixed, I guess. - (setf (alist-get session ement-syncs nil nil #'equal) nil - (alist-get user-id ement-sessions nil 'remove #'equal) nil))) - (unless ement-sessions - ;; HACK: If no sessions remain, clear the users table. It might be best - ;; to store a per-session users table, but this is probably good enough. + (setf (alist-get session ement-syncs) nil + ;; Set the session to an "emptied" one, which only retains slots needed to + ;; reconnect. + (alist-get user-id ement-sessions nil nil #'equal) (ement--emptied session)))) + (when ement-save-sessions + ;; Write sessions now that we have emptied the session. + (ement--write-sessions ement-sessions)) + (unless (cl-some #'ement-session-has-synced-p ement-sessions :key #'cdr) + ;; HACK: If no connected sessions remain, clear the users table. It might be best to + ;; store a per-session users table, but this is probably good enough. (clrhash ement-users)) ;; TODO: Should call this hook for each session with the session as argument. (run-hooks 'ement-disconnect-hook) @@ -873,6 +875,7 @@ Returns nil if unable to read `ement-sessions-file'." :token token :transaction-id transaction-id)))) (message "Ement: Writing sessions...") + ;; TODO: Use `persist'. (with-temp-file ement-sessions-file (pcase-let* ((print-level nil) (print-length nil) @@ -885,6 +888,22 @@ Returns nil if unable to read `ement-sessions-file'." ;; Ensure permissions are safe. (chmod ement-sessions-file #o600))) +(cl-defmethod ement--emptied ((session ement-session)) + "Return a copy of SESSION with most slots nulled. +The copy only includes these slots: user, server, token, and +transaction-id. This is suitable for persisting upon disconnect +so the session can be reconnected, but allowing most data to be +garbage-collected." + (pcase-let* (((cl-struct ement-session user server token transaction-id) session) + ((cl-struct ement-user (id user-id) username) user) + ((cl-struct ement-server (name server-name) uri-prefix) server)) + (make-ement-session :user (list :id user-id + :username username) + :server (list :name server-name + :uri-prefix uri-prefix) + :token token + :transaction-id transaction-id))) + (defun ement--kill-emacs-hook () "Function to be added to `kill-emacs-hook'. Writes Ement session to disk when enabled."