From 152f8c9519ac1b7b35c3789b03f3d1cc3b1e8881 Mon Sep 17 00:00:00 2001 From: Ben Burwell Date: Mon, 29 Jul 2019 10:50:02 -0400 Subject: Ring bell when new messages arrive Add a "new-message-bell" option to the UI section of aerc.conf. A new hook into the message store allows the msglist widget to detect new messages being added to the displayed list. When new messages are delivered, and the new-message-bell option is enabled (as it is by default), the terminal will beep. --- config/aerc.conf.in | 6 ++++++ config/config.go | 2 ++ doc/aerc-config.5.scd | 5 +++++ lib/msgstore.go | 15 ++++++++++++--- lib/ui/interfaces.go | 9 +++++++++ lib/ui/ui.go | 3 ++- widgets/account.go | 4 ++++ widgets/aerc.go | 15 +++++++++++++++ widgets/tabhost.go | 1 + 9 files changed, 56 insertions(+), 4 deletions(-) diff --git a/config/aerc.conf.in b/config/aerc.conf.in index 55dfa130..4d0f9fd2 100644 --- a/config/aerc.conf.in +++ b/config/aerc.conf.in @@ -37,6 +37,12 @@ empty-dirlist=(no folders) # Default: false mouse-enabled=false +# +# Ring the bell when new messages are received +# +# Default: yes +new-message-bell=true + [viewer] # # Specifies the pager to use when displaying emails. Note that some filters diff --git a/config/config.go b/config/config.go index 8ba705ca..bfcbecf6 100644 --- a/config/config.go +++ b/config/config.go @@ -32,6 +32,7 @@ type UIConfig struct { EmptyMessage string `ini:"empty-message"` EmptyDirlist string `ini:"empty-dirlist"` MouseEnabled bool `ini:"mouse-enabled"` + NewMessageBell bool `ini:"new-message-bell"` } const ( @@ -344,6 +345,7 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) { EmptyMessage: "(no messages)", EmptyDirlist: "(no folders)", MouseEnabled: false, + NewMessageBell: true, }, Viewer: ViewerConfig{ diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd index 72823115..a57f760b 100644 --- a/doc/aerc-config.5.scd +++ b/doc/aerc-config.5.scd @@ -105,6 +105,11 @@ These options are configured in the *[ui]* section of aerc.conf. Default: false +*new-message-bell* + Ring the bell when a new message is received. + + Default: true + ## VIEWER These options are configured in the *[viewer]* section of aerc.conf. diff --git a/lib/msgstore.go b/lib/msgstore.go index 48a105ed..b3461291 100644 --- a/lib/msgstore.go +++ b/lib/msgstore.go @@ -33,12 +33,14 @@ type MessageStore struct { pendingHeaders map[uint32]interface{} worker *types.Worker - triggerNewEmail func(*models.MessageInfo) + triggerNewEmail func(*models.MessageInfo) + triggerDirectoryChange func() } func NewMessageStore(worker *types.Worker, dirInfo *models.DirectoryInfo, - triggerNewEmail func(*models.MessageInfo)) *MessageStore { + triggerNewEmail func(*models.MessageInfo), + triggerDirectoryChange func()) *MessageStore { return &MessageStore{ Deleted: make(map[uint32]interface{}), @@ -52,7 +54,8 @@ func NewMessageStore(worker *types.Worker, pendingHeaders: make(map[uint32]interface{}), worker: worker, - triggerNewEmail: triggerNewEmail, + triggerNewEmail: triggerNewEmail, + triggerDirectoryChange: triggerDirectoryChange, } } @@ -147,6 +150,7 @@ func merge(to *models.MessageInfo, from *models.MessageInfo) { func (store *MessageStore) Update(msg types.WorkerMessage) { update := false + directoryChange := false switch msg := msg.(type) { case *types.DirectoryInfo: store.DirInfo = *msg.Info @@ -159,6 +163,7 @@ func (store *MessageStore) Update(msg types.WorkerMessage) { newMap[uid] = msg } else { newMap[uid] = nil + directoryChange = true } } store.Messages = newMap @@ -225,6 +230,10 @@ func (store *MessageStore) Update(msg types.WorkerMessage) { if update { store.update() } + + if directoryChange && store.triggerDirectoryChange != nil { + store.triggerDirectoryChange() + } } func (store *MessageStore) OnUpdate(fn func(store *MessageStore)) { diff --git a/lib/ui/interfaces.go b/lib/ui/interfaces.go index 9008ea72..2f634248 100644 --- a/lib/ui/interfaces.go +++ b/lib/ui/interfaces.go @@ -23,6 +23,10 @@ type Interactive interface { Focus(focus bool) } +type Beeper interface { + OnBeep(func() error) +} + type Simulator interface { // Queues up the given input events for simulation Simulate(events []tcell.Event) @@ -33,6 +37,11 @@ type DrawableInteractive interface { Interactive } +type DrawableInteractiveBeeper interface { + DrawableInteractive + Beeper +} + // A drawable which contains other drawables type Container interface { Drawable diff --git a/lib/ui/ui.go b/lib/ui/ui.go index 13b640b4..4c3dd345 100644 --- a/lib/ui/ui.go +++ b/lib/ui/ui.go @@ -19,7 +19,7 @@ type UI struct { } func Initialize(conf *config.AercConfig, - content DrawableInteractive) (*UI, error) { + content DrawableInteractiveBeeper) (*UI, error) { screen, err := tcell.NewScreen() if err != nil { @@ -57,6 +57,7 @@ func Initialize(conf *config.AercConfig, content.OnInvalidate(func(_ Drawable) { atomic.StoreInt32(&state.invalid, 1) }) + content.OnBeep(screen.Beep) content.Focus(true) return &state, nil diff --git a/widgets/account.go b/widgets/account.go index 86ec00c8..de81ab85 100644 --- a/widgets/account.go +++ b/widgets/account.go @@ -205,6 +205,10 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) { func(msg *models.MessageInfo) { acct.conf.Triggers.ExecNewEmail(acct.acct, acct.conf, msg) + }, func() { + if acct.conf.Ui.NewMessageBell { + acct.host.Beep() + } }) acct.dirlist.SetMsgStore(msg.Info.Name, store) store.OnUpdate(func(_ *lib.MessageStore) { diff --git a/widgets/aerc.go b/widgets/aerc.go index 90b56c82..23dac3e2 100644 --- a/widgets/aerc.go +++ b/widgets/aerc.go @@ -30,6 +30,7 @@ type Aerc struct { statusline *StatusLine pendingKeys []config.KeyStroke tabs *libui.Tabs + beep func() error } func NewAerc(conf *config.AercConfig, logger *log.Logger, @@ -84,6 +85,20 @@ func NewAerc(conf *config.AercConfig, logger *log.Logger, return aerc } +func (aerc *Aerc) OnBeep(f func() error) { + aerc.beep = f +} + +func (aerc *Aerc) Beep() { + if aerc.beep == nil { + aerc.logger.Printf("should beep, but no beeper") + return + } + if err := aerc.beep(); err != nil { + aerc.logger.Printf("tried to beep, but could not: %v", err) + } +} + func (aerc *Aerc) Tick() bool { more := false for _, acct := range aerc.accounts { diff --git a/widgets/tabhost.go b/widgets/tabhost.go index 7c502cb2..2c33cf8d 100644 --- a/widgets/tabhost.go +++ b/widgets/tabhost.go @@ -8,4 +8,5 @@ type TabHost interface { BeginExCommand() SetStatus(status string) *StatusMessage PushStatus(text string, expiry time.Duration) *StatusMessage + Beep() } -- cgit