diff options
Diffstat (limited to 'modules/signal-config.el')
| -rw-r--r-- | modules/signal-config.el | 61 |
1 files changed, 56 insertions, 5 deletions
diff --git a/modules/signal-config.el b/modules/signal-config.el index 102ece86..7e980b62 100644 --- a/modules/signal-config.el +++ b/modules/signal-config.el @@ -16,6 +16,10 @@ ;;; Code: (require 'seq) +(require 'keybindings) ;; provides cj/custom-keymap + cj/register-prefix-map +(require 'system-lib) ;; for cj/executable-find-or-warn + +(declare-function notifications-notify "notifications") (defun cj/signal--jstr (value) "Return VALUE if it is a non-blank string, else nil. @@ -101,6 +105,46 @@ window of a focused frame." (buffer-name (window-buffer (selected-window))) (cj/signal--frame-focused-p)))) +;;; Notifications + +(defcustom cj/signel-notify-sound nil + "When non-nil, incoming-message notifications play the notify script's sound. +Nil (the default) passes --silent so the toast is visual only." + :type 'boolean + :group 'signel) + +(defconst cj/signal--notify-body-max 120 + "Maximum character length of a desktop-notification body. +Longer message text truncates to this length ending in an ellipsis; +the full text is always in the chat buffer.") + +(defun cj/signal--format-notify-body (text) + "Collapse whitespace in TEXT and truncate it for a notification body. +Whitespace runs (including newlines) become single spaces, the result +is trimmed, and anything over `cj/signal--notify-body-max' characters +truncates to that length with a trailing ellipsis." + (let ((flat (string-trim (replace-regexp-in-string "[ \t\n\r]+" " " text)))) + (if (<= (length flat) cj/signal--notify-body-max) + flat + (concat (substring flat 0 (1- cj/signal--notify-body-max)) "…")))) + +(defun cj/signel--notify (chat-id sender body) + "Raise a desktop notification for an incoming Signal message. +Suppressed via `cj/signal--should-notify-p' when the user is actively +viewing CHAT-ID. Routes through the external notify script when it is +on PATH (type info, sound gated by `cj/signel-notify-sound'), falling +back to `notifications-notify' otherwise. SENDER names the title; +BODY is formatted by `cj/signal--format-notify-body'. Installed as +`signel-notify-function' in the use-package :config below." + (when (cj/signal--should-notify-p chat-id) + (let ((title (format "Signal: %s" sender)) + (text (cj/signal--format-notify-body body)) + (script (executable-find "notify"))) + (if script + (apply #'start-process "signel-notify" nil script "info" title text + (unless cj/signel-notify-sound (list "--silent"))) + (notifications-notify :title title :body text))))) + ;;; signel — fork integration (defcustom cj/signal-private-config-file @@ -125,7 +169,13 @@ time." (signel-auto-open-buffer nil) :config (when (file-readable-p cj/signal-private-config-file) - (load cj/signal-private-config-file nil t))) + (load cj/signal-private-config-file nil t)) + ;; Route incoming-message notifications through cj/signel--notify + ;; (suppression + notify script + truncation); warn once at load when + ;; the script is missing — the runtime path still falls back to + ;; notifications-notify, so messages are never silently dropped. + (setq signel-notify-function #'cj/signel--notify) + (cj/executable-find-or-warn "notify" "Signal desktop notifications via the notify script (falling back to notifications-notify)" 'signal-config)) ;; Chat buffers (named `*Signel: <id>*') open in the bottom 30% of the ;; frame rather than wherever display-buffer's fallback rule picks. @@ -291,10 +341,11 @@ that on first use." Leaves =l= unbound for now -- the future =cj/signel-link= command lands in a later pass. See =docs/design/signal-client.org= scope summary.") -(declare-function cj/custom-keymap "keybindings" ()) -(with-eval-after-load 'keybindings - (when (boundp 'cj/custom-keymap) - (keymap-set cj/custom-keymap "M" cj/signel-prefix-map))) +;; Register the messages prefix under C-; M via the documented helper. +;; keybindings.el owns cj/custom-keymap; the (require 'keybindings) above +;; guarantees it is loaded before this runs, so no load-order guard is +;; needed. This is the same pattern every other feature module uses. +(cj/register-prefix-map "M" cj/signel-prefix-map "signal messages") (provide 'signal-config) ;;; signal-config.el ends here |
