#!/usr/bin/env bash # Reminders without an app: transient user timers. # # panama-remind add "20m" "stand up" relative: 90s, 20m, 1h, 1h30m # panama-remind add "9:30" "standup" clock time: today, or tomorrow if past # panama-remind list one line per pending reminder # panama-remind pick choose one in the launcher; cancel it # panama-remind cancel cancel by unit name # # systemd-run carries the whole feature: the timer survives shell exit, fires # notify-send inside the user session with its activation environment intact, # and cleans itself up after firing. The message rides the unit Description so # list can show it without a state file. Everything relative is converted to # seconds first, so there is exactly one systemd-run form to get right. set -euo pipefail usage() { echo 'usage: panama-remind add "<20m, 1h30m, or 9:30>" ""' >&2 echo ' panama-remind list, pick, or cancel ' >&2 exit 2 } seconds_until() { local when="$1" total=0 rest num unit if [[ "$when" =~ ^([0-9]{1,2}):([0-9]{2})$ ]]; then local target now target="$(date -d "$when" +%s 2>/dev/null)" || return 1 now="$(date +%s)" (( target <= now )) && target=$(( target + 86400 )) echo $(( target - now )) return 0 fi rest="$when" [[ "$rest" =~ ^([0-9]+[smh])+$ ]] || return 1 while [[ "$rest" =~ ^([0-9]+)([smh])(.*)$ ]]; do num="${BASH_REMATCH[1]}" unit="${BASH_REMATCH[2]}" rest="${BASH_REMATCH[3]}" case "$unit" in s) total=$(( total + num ));; m) total=$(( total + num * 60 ));; h) total=$(( total + num * 3600 ));; esac done (( total > 0 )) || return 1 echo "$total" } cmd="${1:-}" case "$cmd" in add) when="${2:-}" text="${3:-}" [[ -n "$when" && -n "$text" ]] || usage if ! secs="$(seconds_until "$when")"; then notify-send "Reminder not set" "Could not read \"$when\" — try 20m, 1h30m, or 9:30" 2>/dev/null || true echo "panama-remind: could not parse \"$when\"" >&2 exit 1 fi unit="panama-remind-$(date +%s)-$RANDOM" systemd-run --user --collect --unit="$unit" \ --description="$text" \ --on-active="${secs}s" --timer-property=AccuracySec=1s \ notify-send --urgency=critical --icon=alarm-symbolic "Reminder" "$text" \ >/dev/null notify-send "Reminder set" "\"$text\" in $when" 2>/dev/null || true ;; list) # unit when it fires message, one per pending reminder. # `next` is microseconds since the epoch. systemctl --user list-timers 'panama-remind-*' --output=json 2>/dev/null \ | jq -r '.[] | [.unit, ((.next // 0) / 1000000 | floor)] | @tsv' \ | while IFS=$'\t' read -r unit next; do desc="$(systemctl --user show "$unit" --property=Description --value 2>/dev/null)" printf '%s\t%s\t%s\n' "$unit" "$(date -d "@$next" '+%H:%M:%S' 2>/dev/null)" "$desc" done ;; pick) lines="$("$0" list)" if [[ -z "$lines" ]]; then notify-send "Reminders" "Nothing pending" 2>/dev/null || true exit 0 fi chosen="$(awk -F'\t' '{ printf "%s — %s\n", $3, $2 }' <<<"$lines" \ | vicinae dmenu --navigation-title "Cancel a reminder" \ --section-title 'Pending ({count})' --format index)" || exit 0 [[ -n "$chosen" ]] || exit 0 unit="$(sed -n "$(( chosen + 1 ))p" <<<"$lines" | cut -f1)" [[ -n "$unit" ]] && "$0" cancel "$unit" ;; cancel) unit="${2:-}"; [[ -n "$unit" ]] || usage systemctl --user stop "${unit%.timer}.timer" 2>/dev/null || true notify-send "Reminder cancelled" 2>/dev/null || true ;; *) usage;; esac