Files
jbrechtel cedd82738a fix: show notifications and unread count for SSE messages arriving when chat closed
Three gaps caused messages received via SSE to be invisible when
the conversation buffer wasn't already open:

1. Echo-area notification (noise-receive.el): when an SSE message
   arrives and the conversation buffer isn't in a visible window,
   show "[Noise] <name>" in the echo area.

2. Modeline unread counter (noise.el): the noise-mode lighter now
   shows Noise[N] for N unread messages via a lightweight SUM
   aggregation across all conversations.

3. Auto-refresh hook (noise-conversation.el): a
   window-buffer-change-functions hook re-renders *signal:* buffers
   when they become visible, covering direct C-x b switches.

Also adds regression test:
noise-receive-sse-stores-then-open-shows-message —
store via SSE handler with no buffer open, then open and verify.
2026-06-14 12:57:05 -04:00

109 lines
3.3 KiB
EmacsLisp

;;; noise.el --- Signal Messenger client for Emacs -*- lexical-binding: t; -*-
;; Author: Noise contributors
;; Version: 0.1.0
;; Package-Requires: ((emacs "27.1"))
;; Keywords: comm
;;; Commentary:
;; Noise is a Signal Messenger client powered by signal-cli JSON-RPC.
;;; Code:
(require 'noise-rpc)
(require 'noise-chat-list)
(require 'noise-conversation)
(require 'noise-new-chat)
(require 'noise-receive)
(defgroup noise nil
"Signal Messenger client for Emacs."
:group 'comm
:prefix "noise-")
(defcustom noise-signal-cli-address "http://localhost:8080"
"Base URL of the signal-cli JSON-RPC daemon.
Format: http://HOST:PORT
The default port is 8080, matching signal-cli's `--http` default."
:type 'string
:group 'noise)
(defcustom noise-account nil
"The Signal account to use, as a phone number in E.164 format.
signal-cli can have multiple accounts registered. When the daemon
serves more than one account, every request must say which account it
is for, so this must be set (e.g. \"+15551234567\"). When nil, the
daemon is assumed to serve a single account (started with `-a`).
Use `noise-select-account' to pick one interactively from the
accounts registered with signal-cli."
:type '(choice (const :tag "Single-account daemon" nil)
(string :tag "Phone number (E.164)"))
:group 'noise)
(defcustom noise-sse-max-reconnect-delay 60
"Maximum delay in seconds between SSE reconnection attempts."
:type 'integer
:group 'noise)
;;;###autoload
(defun noise-select-account ()
"Select which signal-cli account Noise should use.
Queries the daemon for registered accounts and sets `noise-account'."
(interactive)
(let* ((accounts (noise-rpc-call "listAccounts"))
(numbers (mapcar (lambda (a) (gethash "number" a))
(append accounts nil))))
(cond
((null numbers)
(user-error "No accounts registered with signal-cli"))
((null (cdr numbers))
(setq noise-account (car numbers)))
(t
(setq noise-account
(completing-read "Signal account: " numbers nil t))))
(message "Using Signal account %s" noise-account)))
;;;###autoload
(defun noise-check-connection ()
"Check connectivity to the signal-cli JSON-RPC daemon."
(interactive)
(condition-case err
(progn
(noise-rpc-check)
(message "signal-cli connected at %s" noise-signal-cli-address))
(noise-rpc-error
(message "Error: %s" (cdr err)))))
(defun noise--unread-lighter ()
"Return the mode-line lighter string with unread count."
(let ((count 0))
(when (and (boundp 'noise-db--connection)
noise-db--connection
(fboundp 'sqlitep)
(sqlitep noise-db--connection))
(condition-case nil
(setq count (or (caar (sqlite-select noise-db--connection
"SELECT COALESCE(SUM(unread_count), 0) FROM conversations"))
0))
(error 0)))
(if (> count 0)
(format " Noise[%d]" count)
" Noise")))
;;;###autoload
(define-minor-mode noise-mode
"Toggle Noise Signal client mode."
:global t
:lighter (:eval (noise--unread-lighter))
:group 'noise
(if noise-mode
(progn
(noise-chat-list)
(noise-receive-start-sse))
(noise-receive-stop-sse)
(message "Noise mode disabled")))
(provide 'noise)
;;; noise.el ends here