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.
This commit is contained in:
Ben Burwell 2019-07-29 10:50:02 -04:00 committed by Drew DeVault
parent 2804f00001
commit 152f8c9519
9 changed files with 56 additions and 4 deletions

View file

@ -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

View file

@ -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{

View file

@ -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.

View file

@ -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)) {

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -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 {

View file

@ -8,4 +8,5 @@ type TabHost interface {
BeginExCommand()
SetStatus(status string) *StatusMessage
PushStatus(text string, expiry time.Duration) *StatusMessage
Beep()
}