branch: elpa/jabber
commit 8dcdc39c9de2b5a4f84cb09a340805abcc10cbe9
Author: Thanos Apollo <[email protected]>
Commit: Thanos Apollo <[email protected]>
core: Separate lifecycle effects
---
lisp/jabber-bookmarks.el | 7 ++++++
lisp/jabber-core.el | 40 +++++++++------------------------
lisp/jabber-lifecycle.el | 58 ++++++++++++++++++++++++++++++++++++++++++++++++
lisp/jabber-modeline.el | 10 +++++++++
lisp/jabber-muc.el | 8 +++++++
lisp/jabber-presence.el | 12 ++++++++++
lisp/jabber-register.el | 8 +++++++
7 files changed, 114 insertions(+), 29 deletions(-)
diff --git a/lisp/jabber-bookmarks.el b/lisp/jabber-bookmarks.el
index e85ddf08a5..58cc043a58 100644
--- a/lisp/jabber-bookmarks.el
+++ b/lisp/jabber-bookmarks.el
@@ -30,6 +30,7 @@
(require 'fsm)
(require 'jabber-disco)
+(require 'jabber-lifecycle)
(require 'jabber-private)
(require 'jabber-pubsub)
(require 'jabber-muc-state)
@@ -243,6 +244,10 @@ If REFRESH is non-nil, always fetch from server and
re-detect protocol."
(run-with-timer 0 nil cont jc (when (listp bookmarks) bookmarks))
(jabber-bookmarks--detect-and-fetch jc cont refresh))))
+(defun jabber-bookmarks--prefetch (jc)
+ "Populate the bookmarks cache for a new session on JC."
+ (jabber-get-bookmarks jc #'ignore))
+
(defun jabber-bookmarks--detect-and-fetch (jc cont &optional refresh)
"Detect bookmark protocol on JC via disco and fetch via CONT.
Query bare JID for the `#compat' feature. If present, use XEP-0402
@@ -714,6 +719,8 @@ NICK, if non-nil, is stored in the bookmark."
(clrhash jabber-bookmarks--legacy-accounts))
(add-hook 'jabber-pre-disconnect-hook #'jabber-bookmarks--on-disconnect)
+(add-hook 'jabber-lifecycle-session-bootstrap-functions
+ #'jabber-bookmarks--prefetch 10)
(provide 'jabber-bookmarks)
diff --git a/lisp/jabber-core.el b/lisp/jabber-core.el
index 863c644e77..29924488d7 100644
--- a/lisp/jabber-core.el
+++ b/lisp/jabber-core.el
@@ -36,6 +36,8 @@
(require 'jabber-xml)
(require 'jabber-conn)
(require 'jabber-iq)
+(require 'jabber-lifecycle)
+(require 'jabber-presence-events)
(require 'jabber-sasl)
(require 'jabber-console)
(require 'jabber-sm)
@@ -130,18 +132,6 @@ problems."
;; jabber-connect and jabber-connect-all should load jabber.el, not
;; just jabber-core.el, when autoloaded.
-;; Global reference declarations
-
-(declare-function jabber-muc-connection-closed "jabber-muc.el" (bare-jid))
-(declare-function jabber-roster-update "jabber-roster.el"
- (jc new-items changed-items deleted-items))
-(declare-function jabber-process-roster "jabber-presence.el"
- (jc xml-data closure-data))
-(declare-function jabber-initial-roster-failure "jabber-presence.el"
- (jc xml-data _closure-data))
-(declare-function jabber-get-register "jabber-register.el" (jc to))
-(declare-function jabber-mode-line-presence-update "jabber-modeline.el" (&rest
_))
-(declare-function jabber-get-bookmarks "jabber-bookmarks.el" (jc cont
&optional refresh))
(defvar jabber-debug-keep-process-buffers) ; jabber.el
(defvar jabber-silent-mode) ; jabber.el
(defvar jabber-account-list) ; jabber.el
@@ -149,8 +139,6 @@ problems."
(defvar jabber-xml-data) ; jabber.el
(defvar jabber-default-connection-type) ; jabber-conn.el
(defvar jabber-connect-methods) ; jabber-conn.el
-(defvar jabber-modeline-mode) ; jabber-modeline.el
-(defvar jabber-roster-xmlns) ; jabber-xml.el
;;
@@ -330,7 +318,7 @@ override the defaults from `jabber-account-list'."
(if (and sm-resumable (not expected))
(setq state-data (plist-put state-data :sm-resuming
t))
;; Otherwise clear MUC data and SM state.
- (jabber-muc-connection-closed
(jabber-connection-bare-jid fsm))
+ (jabber-lifecycle-dispatch-session-reset fsm)
(setq state-data (jabber-sm--reset state-data)))
(unless expected
@@ -350,8 +338,7 @@ override the defaults from `jabber-account-list'."
;; of connections.
(setq jabber-connections
(delq fsm jabber-connections))
- (when jabber-modeline-mode
- (jabber-mode-line-presence-update))
+ (jabber-lifecycle-dispatch-connection-list-changed)
;; And let the FSM sleep...
(list state-data nil))))
@@ -542,7 +529,7 @@ override the defaults from `jabber-account-list'."
(define-enter-state jabber-connection :register-account
(fsm state-data)
- (jabber-get-register fsm nil)
+ (jabber-lifecycle-dispatch-registration fsm)
(list state-data nil))
(define-state jabber-connection :register-account
@@ -690,7 +677,7 @@ override the defaults from `jabber-account-list'."
(list :sm-resume state-data))
;; SM resume was hoped for but server doesn't offer SM
here.
((plist-get state-data :sm-resuming)
- (jabber-muc-connection-closed
(jabber-connection-bare-jid fsm))
+ (jabber-lifecycle-dispatch-session-reset fsm)
(setq state-data (jabber-sm--reset state-data))
(setq state-data (plist-put state-data :sm-resuming nil))
;; Fall through to normal bind.
@@ -853,7 +840,7 @@ override the defaults from `jabber-account-list'."
((jabber-sm--failed-p stanza)
(message "Stream Management resume failed, falling back to
auth")
;; Resume failed: clean up MUC state now, reset SM, do
full auth.
- (jabber-muc-connection-closed (jabber-connection-bare-jid
fsm))
+ (jabber-lifecycle-dispatch-session-reset fsm)
(setq state-data (jabber-sm--reset state-data))
(setq state-data (plist-put state-data :sm-resuming nil))
(list :sasl-auth state-data))
@@ -882,14 +869,8 @@ override the defaults from `jabber-account-list'."
(setq state-data (jabber-sm--start-r-timer fsm
state-data)))
(setq state-data (plist-put state-data :sm-resumed
nil))
(run-hook-with-args 'jabber-post-resume-hooks fsm))
- ;; Normal connect: fetch roster (which triggers
post-connect hooks
- ;; from the roster callback) and prefetch bookmarks.
- (jabber-send-iq fsm nil
- "get"
- `(query ((xmlns . ,jabber-roster-xmlns)))
- #'jabber-process-roster 'initial
- #'jabber-initial-roster-failure nil)
- (jabber-get-bookmarks fsm #'ignore))
+ ;; Normal connect: feature modules fetch initial session
data.
+ (jabber-lifecycle-dispatch-session-bootstrap fsm))
(list (plist-put state-data :ever-session-established t)
nil))
(define-state jabber-connection :session-established
@@ -946,7 +927,8 @@ override the defaults from `jabber-account-list'."
;; Update roster
(let ((pending-updates (plist-get state-data
:roster-pending-updates)))
(setq state-data (plist-put state-data
:roster-pending-updates nil))
- (jabber-roster-update fsm nil pending-updates nil)
+ (jabber-presence-events-dispatch-roster-update
+ fsm nil pending-updates nil)
(list :session-established state-data)))
(:send-if-connected
diff --git a/lisp/jabber-lifecycle.el b/lisp/jabber-lifecycle.el
new file mode 100644
index 0000000000..cd6ca71cb7
--- /dev/null
+++ b/lisp/jabber-lifecycle.el
@@ -0,0 +1,58 @@
+;;; jabber-lifecycle.el --- Connection lifecycle effect dispatch -*-
lexical-binding: t; -*-
+
+;; Copyright (C) 2026 Thanos Apollo
+
+;; Maintainer: Thanos Apollo <[email protected]>
+
+;; This file is a part of jabber.el.
+
+;; 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 2 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, write to the Free Software
+;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+;;; Commentary:
+
+;; Keeps the connection FSM independent from feature lifecycle effects.
+
+;;; Code:
+
+(defvar jabber-lifecycle-session-reset-functions nil
+ "Functions called with a connection when its logical session is lost.")
+
+(defvar jabber-lifecycle-session-bootstrap-functions nil
+ "Functions called with a connection after a new session is established.")
+
+(defvar jabber-lifecycle-registration-functions nil
+ "Functions called with a connection entering account registration.")
+
+(defvar jabber-lifecycle-connection-list-changed-functions nil
+ "Functions called after the live connection list changes.")
+
+(defun jabber-lifecycle-dispatch-session-reset (jc)
+ "Dispatch logical session reset effects for JC."
+ (run-hook-with-args 'jabber-lifecycle-session-reset-functions jc))
+
+(defun jabber-lifecycle-dispatch-session-bootstrap (jc)
+ "Dispatch new session bootstrap effects for JC."
+ (run-hook-with-args 'jabber-lifecycle-session-bootstrap-functions jc))
+
+(defun jabber-lifecycle-dispatch-registration (jc)
+ "Dispatch account registration startup for JC."
+ (run-hook-with-args 'jabber-lifecycle-registration-functions jc))
+
+(defun jabber-lifecycle-dispatch-connection-list-changed ()
+ "Dispatch effects of a change to the live connection list."
+ (run-hooks 'jabber-lifecycle-connection-list-changed-functions))
+
+(provide 'jabber-lifecycle)
+;;; jabber-lifecycle.el ends here
diff --git a/lisp/jabber-modeline.el b/lisp/jabber-modeline.el
index eaf41801f7..e328cc9f11 100644
--- a/lisp/jabber-modeline.el
+++ b/lisp/jabber-modeline.el
@@ -30,6 +30,7 @@
(require 'jabber-presence-display)
(require 'jabber-alert)
(require 'jabber-activity)
+(require 'jabber-lifecycle)
(eval-when-compile (require 'cl-lib))
(defgroup jabber-mode-line nil
@@ -51,6 +52,7 @@ Available sections: `presence', `contacts', `activity'."
;; Global reference declarations
(defvar jabber-current-show) ; jabber.el
+(defvar jabber-modeline-mode)
;; Activity variables (defined in jabber-activity.el)
@@ -87,6 +89,14 @@ Available sections: `presence', `contacts', `activity'."
(cdr (assoc jabber-current-show jabber-presence-strings))
"Offline")))
+(defun jabber-modeline--connection-list-changed ()
+ "Refresh presence display after a live connection list update."
+ (when jabber-modeline-mode
+ (jabber-mode-line-presence-update)))
+
+(add-hook 'jabber-lifecycle-connection-list-changed-functions
+ #'jabber-modeline--connection-list-changed)
+
(defvar jabber-mode-line--recount-timer nil
"Pending timer for a debounced `jabber-mode-line--do-count-contacts' call.")
diff --git a/lisp/jabber-muc.el b/lisp/jabber-muc.el
index f74758a58f..ff6a2aa4c6 100644
--- a/lisp/jabber-muc.el
+++ b/lisp/jabber-muc.el
@@ -33,6 +33,7 @@
(require 'jabber-widget)
(require 'jabber-buffer-registry)
(require 'jabber-disco)
+(require 'jabber-lifecycle)
(require 'jabber-muc-protocol)
(require 'jabber-muc-state)
(require 'jabber-presence-events)
@@ -461,6 +462,13 @@ entry is removed."
(remhash room jabber-muc--nonanonymous-rooms)))))
(setq jabber-muc--rooms-before-disconnect snapshot)))
+(defun jabber-muc--session-reset (jc)
+ "Clear room state belonging to the lost logical session on JC."
+ (jabber-muc-connection-closed (jabber-connection-bare-jid jc)))
+
+(add-hook 'jabber-lifecycle-session-reset-functions
+ #'jabber-muc--session-reset)
+
(defun jabber-muc--self-ping-failed (jc xml-data closure-data)
"Handle failed MUC self-ping per XEP-0410 error classification.
JC is the connection. XML-DATA is the IQ error stanza.
diff --git a/lisp/jabber-presence.el b/lisp/jabber-presence.el
index e122065e1b..30b2c5dede 100644
--- a/lisp/jabber-presence.el
+++ b/lisp/jabber-presence.el
@@ -30,6 +30,7 @@
(require 'jabber-core)
(require 'jabber-disco)
(require 'jabber-iq)
+(require 'jabber-lifecycle)
(require 'jabber-alert)
(require 'jabber-util)
(require 'jabber-muc-protocol)
@@ -149,6 +150,17 @@ obtained from `xml-parse-region'."
(jabber-report-success jc xml-data "Initial roster retrieval")
(run-hook-with-args 'jabber-post-connect-hooks jc))
+(defun jabber-presence--request-initial-roster (jc)
+ "Request the initial roster for JC."
+ (jabber-send-iq jc nil
+ "get"
+ `(query ((xmlns . ,jabber-roster-xmlns)))
+ #'jabber-process-roster 'initial
+ #'jabber-initial-roster-failure nil))
+
+(add-hook 'jabber-lifecycle-session-bootstrap-functions
+ #'jabber-presence--request-initial-roster)
+
(defun jabber-presence--extract-metadata (xml-data)
"Parse presence metadata from XML-DATA.
Return a plist (:show :status :priority :error)."
diff --git a/lisp/jabber-register.el b/lisp/jabber-register.el
index 79bbf26d13..e65ef35b28 100644
--- a/lisp/jabber-register.el
+++ b/lisp/jabber-register.el
@@ -28,6 +28,7 @@
;;; Code:
(require 'jabber-iq)
+(require 'jabber-lifecycle)
(require 'jabber-widget)
(require 'jabber-xdata)
@@ -60,6 +61,13 @@ JC is the Jabber connection."
#'jabber-process-data #'jabber-process-register-or-search
#'jabber-report-success "Registration"))
+(defun jabber-register--start-account-registration (jc)
+ "Start in-band account registration on JC."
+ (jabber-get-register jc nil))
+
+(add-hook 'jabber-lifecycle-registration-functions
+ #'jabber-register--start-account-registration)
+
(defun jabber-process-register-or-search (jc xml-data)
"Display results from jabber:iq:{register,search} query as a form.