aboutsummaryrefslogtreecommitdiff
path: root/modules/signal-config.el
diff options
context:
space:
mode:
Diffstat (limited to 'modules/signal-config.el')
-rw-r--r--modules/signal-config.el61
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