aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/modules/mail/mail-notify.sh
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/modules/mail/mail-notify.sh')
-rwxr-xr-xdesktop/modules/mail/mail-notify.sh73
1 files changed, 45 insertions, 28 deletions
diff --git a/desktop/modules/mail/mail-notify.sh b/desktop/modules/mail/mail-notify.sh
index 968799c..0052a8d 100755
--- a/desktop/modules/mail/mail-notify.sh
+++ b/desktop/modules/mail/mail-notify.sh
@@ -32,6 +32,7 @@ set -u
STATE="${MAIL_NOTIFY_STATE:-$HOME/.local/state/mail-notify.lastmod}"
CONFIG="${MAIL_NOTIFY_CONFIG:-$HOME/.config/qtmaildir/qtmaildir.conf}"
SCOPE='tag:unread and tag:inbox'
+OPEN="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/mail-open.sh"
# How many threads a notification body lists before eliding into "+N more".
# Three matches the drawer's own --limit=3.
@@ -119,6 +120,25 @@ build_body() {
printf '\n'
}
+# One action per listed thread, "<thread-id>=<label>" per line,
+# followed by the account itself as the default action. The balloon shell
+# offers them in a picker on click.
+#
+# The key is the thread id, not a Message-ID: thread ids are hex, so a key
+# never carries an "=" or a quote, and mail-open.sh finds the message when
+# the action is picked. The label is plain text for rofi, so no escaping, but
+# it is flattened to one line and shortened, since it is a menu entry.
+build_actions() {
+ local json="$1" label="$2"
+
+ printf '%s' "$json" | jq -r '
+ .[] | select(.thread | test("^[0-9a-f]+$"))
+ | .thread + "=" + ((.authors // "(unknown)") + " — " + (.subject // "(no subject)")
+ | gsub("[\r\n]+"; " ") | .[0:80])
+ ' 2>/dev/null
+ printf 'default=Open %s\n' "$label"
+}
+
# The last revision this script notified up to, or empty when there is none
# to trust. Empty means "seed silently": record where we are now and notify
# nothing.
@@ -158,41 +178,38 @@ write_state() {
# Sends one notification for one account.
#
-# dunstify rather than notify-send because actions need it. A stack tag per
-# account means a second batch for the same account replaces the first rather
-# than stacking, which is what "one notification per account" has to mean when
-# mail keeps arriving.
+# notify-send, whose -A takes NAME=Text and prints the picked NAME, which is
+# all the actions need; dunstify is not installed here. NAME is split on the
+# first "=", so a label may contain one. A stack tag per account means a
+# second batch for the same account replaces the first rather than stacking,
+# which is what "one notification per account" has to mean when mail keeps
+# arriving.
#
# Normal urgency and an explicit 10s timeout, deliberately not -u critical:
-# on most dunst configurations critical notifications never expire, which
-# would leave mail popups on screen until clicked.
+# critical notifications commonly never expire, which would leave mail popups
+# on screen until clicked.
#
-# The click cannot open the account it belongs to. qtmaildir accepts no
-# command line arguments and startup_account is a static config setting, not
-# a flag, which is the same limitation the drawer's thread rows already have.
+# The picked action opens qtmaildir through mail-open.sh: a thread's key opens
+# its newest unread message, "default" the account.
notify_account() {
- local label="$1" key="$2" count="$3" body="$4"
-
- # -a carries "New Mail" because the user's dunst format renders %a as the
- # bold heading line, with %s italic below it.
- if ! command -v dunstify >/dev/null 2>&1; then
- # No actions available, but a notification without a click is still
- # worth having.
- notify-send -a "New Mail" -u normal -t 10000 -i "$MAIL_ICON" \
- "$label ($count)" "$body"
- return 0
- fi
+ local label="$1" key="$2" count="$3" body="$4" rows="$5"
+ local acts=() line picked
+
+ while IFS= read -r line; do acts+=(-A "$line"); done < <(build_actions "$rows" "$label")
- # Backgrounded because -b blocks until the notification is dismissed or
- # clicked. Without this the loop would stall for the full timeout on
+ # Backgrounded because -A waits until the notification is dismissed or
+ # acted on. Without this the loop would stall for the full timeout on
# every account, and a five-account batch would take most of a minute.
+ # -a carries "New Mail" because it renders as the bold heading line.
(
- if [[ "$(dunstify -a "New Mail" -i "$MAIL_ICON" -u normal -t 10000 -b \
+ picked="$(notify-send -a "New Mail" -i "$MAIL_ICON" -u normal -t 10000 \
-h "string:x-dunst-stack-tag:mail-$key" \
- -A "default,open" \
- "$label ($count)" "$body")" == "default" ]]; then
- "$HOME/bin/qtmaildir" &
- fi
+ "${acts[@]}" \
+ "$label ($count)" "$body")"
+ case "$picked" in
+ default) "$OPEN" "$key" & ;;
+ [0-9a-f]*) "$OPEN" "$key" "$picked" & ;;
+ esac
) >/dev/null 2>&1 &
}
@@ -243,7 +260,7 @@ tick() {
rows="$(notmuch search --format=json --limit="$ROWS" \
--sort=newest-first "$query" 2>/dev/null)" || rows="[]"
- notify_account "$label" "$key" "$count" "$(build_body "$rows" "$count")"
+ notify_account "$label" "$key" "$count" "$(build_body "$rows" "$count")" "$rows"
done < <(parse_accounts < "$CONFIG")
# Written only after every account is done. A single account whose count