aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKoni Marti <koni.marti@gmail.com>2022-03-18 22:35:33 +0100
committerRobin Jarry <robin@jarry.cc>2022-03-18 23:42:07 +0100
commit2512c0403fa42b19c3e87fed44240da045ec902f (patch)
treea258d6a9c70e79cf316985363efcdc9529c91924
parent807870ea3542f2fcb00e7e0451af37c224041dfe (diff)
downloadaerc-2512c0403fa42b19c3e87fed44240da045ec902f.tar.gz
statusline: implement per-account status
Implement a statusline state for each account. Keep the ex line and the push notifications global. Add account name prefix to push notifications. Prefix status line with account name when multiple accounts are available. Use account-specific status line for each tab where an account is defined. Handle threading, filter/search, viewer passthrough and connection status. Signed-off-by: Koni Marti <koni.marti@gmail.com> Acked-by: Robin Jarry <robin@jarry.cc>
-rw-r--r--commands/account/clear.go4
-rw-r--r--commands/account/connection.go12
-rw-r--r--commands/account/search.go11
-rw-r--r--commands/msg/toggle-threads.go2
-rw-r--r--commands/msgview/toggle-key-passthrough.go7
-rw-r--r--commands/next-tab.go1
-rw-r--r--lib/statusline/state.go133
-rw-r--r--widgets/account.go41
-rw-r--r--widgets/aerc.go14
-rw-r--r--widgets/status.go16
10 files changed, 196 insertions, 45 deletions
diff --git a/commands/account/clear.go b/commands/account/clear.go
index 259a9de8..64e70122 100644
--- a/commands/account/clear.go
+++ b/commands/account/clear.go
@@ -3,6 +3,7 @@ package account
import (
"errors"
+ "git.sr.ht/~rjarry/aerc/lib/statusline"
"git.sr.ht/~rjarry/aerc/widgets"
)
@@ -30,6 +31,7 @@ func (Clear) Execute(aerc *widgets.Aerc, args []string) error {
return errors.New("Cannot perform action. Messages still loading")
}
store.ApplyClear()
- aerc.ClearExtraStatus()
+ acct.SetStatus(statusline.SearchFilterClear())
+
return nil
}
diff --git a/commands/account/connection.go b/commands/account/connection.go
index a87993b6..52b569c4 100644
--- a/commands/account/connection.go
+++ b/commands/account/connection.go
@@ -3,6 +3,7 @@ package account
import (
"errors"
+ "git.sr.ht/~rjarry/aerc/lib/statusline"
"git.sr.ht/~rjarry/aerc/widgets"
"git.sr.ht/~rjarry/aerc/worker/types"
)
@@ -26,12 +27,15 @@ func (Connection) Execute(aerc *widgets.Aerc, args []string) error {
if acct == nil {
return errors.New("No account selected")
}
+ cb := func(msg types.WorkerMessage) {
+ acct.SetStatus(statusline.ConnectionActivity(""))
+ }
if args[0] == "connect" {
- acct.Worker().PostAction(&types.Connect{}, nil)
- acct.SetStatus("Connecting...")
+ acct.Worker().PostAction(&types.Connect{}, cb)
+ acct.SetStatus(statusline.ConnectionActivity("Connecting..."))
} else {
- acct.Worker().PostAction(&types.Disconnect{}, nil)
- acct.SetStatus("Disconnecting...")
+ acct.Worker().PostAction(&types.Disconnect{}, cb)
+ acct.SetStatus(statusline.ConnectionActivity("Disconnecting..."))
}
return nil
}
diff --git a/commands/account/search.go b/commands/account/search.go
index 86d9deac..eeee7bdb 100644
--- a/commands/account/search.go
+++ b/commands/account/search.go
@@ -2,8 +2,9 @@ package account
import (
"errors"
- "fmt"
+ "strings"
+ "git.sr.ht/~rjarry/aerc/lib/statusline"
"git.sr.ht/~rjarry/aerc/widgets"
)
@@ -33,16 +34,16 @@ func (SearchFilter) Execute(aerc *widgets.Aerc, args []string) error {
var cb func([]uint32)
if args[0] == "filter" {
- aerc.SetExtraStatus("Filtering...")
+ acct.SetStatus(statusline.FilterActivity("Filtering..."), statusline.Search(""))
cb = func(uids []uint32) {
- aerc.SetExtraStatus(fmt.Sprintf("%s", args))
+ acct.SetStatus(statusline.FilterResult(strings.Join(args, " ")))
acct.Logger().Printf("Filter results: %v", uids)
store.ApplyFilter(uids)
}
} else {
- aerc.SetExtraStatus("Searching...")
+ acct.SetStatus(statusline.Search("Searching..."))
cb = func(uids []uint32) {
- aerc.SetExtraStatus(fmt.Sprintf("%s", args))
+ acct.SetStatus(statusline.Search(strings.Join(args, " ")))
acct.Logger().Printf("Search results: %v", uids)
store.ApplySearch(uids)
// TODO: Remove when stores have multiple OnUpdate handlers
diff --git a/commands/msg/toggle-threads.go b/commands/msg/toggle-threads.go
index e93cb425..79d515cb 100644
--- a/commands/msg/toggle-threads.go
+++ b/commands/msg/toggle-threads.go
@@ -3,6 +3,7 @@ package msg
import (
"errors"
+ "git.sr.ht/~rjarry/aerc/lib/statusline"
"git.sr.ht/~rjarry/aerc/widgets"
)
@@ -34,6 +35,7 @@ func (ToggleThreads) Execute(aerc *widgets.Aerc, args []string) error {
return err
}
store.SetBuildThreads(!store.BuildThreads())
+ acct.SetStatus(statusline.Threading(store.BuildThreads()))
acct.Messages().Invalidate()
return nil
}
diff --git a/commands/msgview/toggle-key-passthrough.go b/commands/msgview/toggle-key-passthrough.go
index 6cd575bf..1ac370e6 100644
--- a/commands/msgview/toggle-key-passthrough.go
+++ b/commands/msgview/toggle-key-passthrough.go
@@ -3,6 +3,7 @@ package msgview
import (
"errors"
+ "git.sr.ht/~rjarry/aerc/lib/statusline"
"git.sr.ht/~rjarry/aerc/widgets"
)
@@ -26,10 +27,8 @@ func (ToggleKeyPassthrough) Execute(aerc *widgets.Aerc, args []string) error {
}
mv, _ := aerc.SelectedTab().(*widgets.MessageViewer)
keyPassthroughEnabled := mv.ToggleKeyPassthrough()
- if keyPassthroughEnabled {
- aerc.SetExtraStatus("[passthrough]")
- } else {
- aerc.ClearExtraStatus()
+ if acct := mv.SelectedAccount(); acct != nil {
+ acct.SetStatus(statusline.Passthrough(keyPassthroughEnabled))
}
return nil
}
diff --git a/commands/next-tab.go b/commands/next-tab.go
index 9d6a09ba..854353f8 100644
--- a/commands/next-tab.go
+++ b/commands/next-tab.go
@@ -42,6 +42,7 @@ func (NextPrevTab) Execute(aerc *widgets.Aerc, args []string) error {
aerc.NextTab()
}
}
+ aerc.UpdateStatus()
return nil
}
diff --git a/lib/statusline/state.go b/lib/statusline/state.go
new file mode 100644
index 00000000..30029c13
--- /dev/null
+++ b/lib/statusline/state.go
@@ -0,0 +1,133 @@
+package statusline
+
+import (
+ "fmt"
+ "strings"
+)
+
+type State struct {
+ Name string
+ Multiple bool
+ Separator string
+
+ Connection string
+ ConnActivity string
+ Connected bool
+
+ Search string
+ Filter string
+ FilterActivity string
+
+ Threading string
+ Passthrough string
+}
+
+func NewState(name string, multipleAccts bool, sep string) *State {
+ return &State{Name: name, Multiple: multipleAccts, Separator: sep}
+}
+
+func (s *State) String() string {
+ var line []string
+ if s.Connection != "" || s.ConnActivity != "" {
+ conn := s.Connection
+ if s.ConnActivity != "" {
+ conn = s.ConnActivity
+ }
+ if s.Multiple {
+ line = append(line, fmt.Sprintf("[%s] %s", s.Name, conn))
+ } else {
+ line = append(line, conn)
+ }
+ }
+ if s.Connected {
+ if s.FilterActivity != "" {
+ line = append(line, s.FilterActivity)
+ } else {
+ if s.Filter != "" {
+ line = append(line, s.Filter)
+ }
+ }
+ if s.Search != "" {
+ line = append(line, s.Search)
+ }
+ if s.Threading != "" {
+ line = append(line, s.Threading)
+ }
+ if s.Passthrough != "" {
+ line = append(line, s.Passthrough)
+ }
+ }
+ return strings.Join(line, s.Separator)
+}
+
+type SetStateFunc func(s *State)
+
+func Connected(state bool) SetStateFunc {
+ return func(s *State) {
+ s.ConnActivity = ""
+ s.Connected = state
+ if state {
+ s.Connection = "Connected"
+ } else {
+ s.Connection = "Disconnected"
+ }
+ }
+}
+
+func ConnectionActivity(desc string) SetStateFunc {
+ return func(s *State) {
+ s.ConnActivity = desc
+ }
+}
+
+func SearchFilterClear() SetStateFunc {
+ return func(s *State) {
+ s.Search = ""
+ s.FilterActivity = ""
+ s.Filter = ""
+ }
+}
+
+func FilterActivity(str string) SetStateFunc {
+ return func(s *State) {
+ s.FilterActivity = str
+ }
+}
+
+func FilterResult(str string) SetStateFunc {
+ return func(s *State) {
+ s.FilterActivity = ""
+ s.Filter = concatFilters(s.Filter, str)
+ }
+}
+
+func concatFilters(existing, next string) string {
+ if existing == "" {
+ return next
+ }
+ return fmt.Sprintf("%s && %s", existing, next)
+}
+
+func Search(desc string) SetStateFunc {
+ return func(s *State) {
+ s.Search = desc
+ }
+}
+
+func Threading(on bool) SetStateFunc {
+ return func(s *State) {
+ s.Threading = ""
+ if on {
+ s.Threading = "threading"
+ }
+ }
+}
+
+func Passthrough(on bool) SetStateFunc {
+ return func(s *State) {
+ s.Passthrough = ""
+ if on {
+ s.Passthrough = "passthrough"
+ }
+ }
+}
diff --git a/widgets/account.go b/widgets/account.go
index 87a8cef5..647a3aee 100644
--- a/widgets/account.go
+++ b/widgets/account.go
@@ -4,12 +4,14 @@ import (
"errors"
"fmt"
"log"
+ "time"
"github.com/gdamore/tcell/v2"
"git.sr.ht/~rjarry/aerc/config"
"git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/lib/sort"
+ "git.sr.ht/~rjarry/aerc/lib/statusline"
"git.sr.ht/~rjarry/aerc/lib/ui"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker"
@@ -29,6 +31,7 @@ type AccountView struct {
logger *log.Logger
msglist *MessageList
worker *types.Worker
+ state *statusline.State
}
func (acct *AccountView) UiConfig() config.UIConfig {
@@ -55,6 +58,7 @@ func NewAccountView(aerc *Aerc, conf *config.AercConfig, acct *config.AccountCon
conf: conf,
host: host,
logger: logger,
+ state: statusline.NewState(acct.Name, len(conf.Accounts) > 1, " | "),
}
view.grid = ui.NewGrid().Rows([]ui.GridSpec{
@@ -86,7 +90,7 @@ func NewAccountView(aerc *Aerc, conf *config.AercConfig, acct *config.AccountCon
worker.PostAction(&types.Configure{Config: acct}, nil)
worker.PostAction(&types.Connect{}, nil)
- host.SetStatus("Connecting...")
+ view.SetStatus(statusline.ConnectionActivity("Connecting..."))
return view, nil
}
@@ -105,8 +109,22 @@ func (acct *AccountView) Tick() bool {
}
}
-func (acct *AccountView) SetStatus(msg string) {
- acct.host.SetStatus(msg)
+func (acct *AccountView) SetStatus(setters ...statusline.SetStateFunc) {
+ for _, fn := range setters {
+ fn(acct.state)
+ }
+}
+
+func (acct *AccountView) UpdateStatus() {
+ acct.host.SetStatus(acct.state.String())
+}
+
+func (acct *AccountView) PushStatus(status string, expiry time.Duration) {
+ acct.aerc.PushStatus(fmt.Sprintf("%s: %v", acct.acct.Name, status), expiry)
+}
+
+func (acct *AccountView) PushError(err error) {
+ acct.aerc.PushError(fmt.Sprintf("%s: %v", acct.acct.Name, err))
}
func (acct *AccountView) AccountConfig() *config.AccountConfig {
@@ -140,6 +158,7 @@ func (acct *AccountView) Invalidate() {
}
func (acct *AccountView) Draw(ctx *ui.Context) {
+ acct.UpdateStatus()
acct.grid.Draw(ctx)
}
@@ -203,7 +222,7 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) {
case *types.Done:
switch msg.InResponseTo().(type) {
case *types.Connect, *types.Reconnect:
- acct.host.SetStatus("Listing mailboxes...")
+ acct.SetStatus(statusline.ConnectionActivity("Listing mailboxes..."))
acct.logger.Println("Listing mailboxes...")
acct.dirlist.UpdateList(func(dirs []string) {
var dir string
@@ -221,13 +240,13 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) {
}
acct.msglist.SetInitDone()
acct.logger.Println("Connected.")
- acct.host.SetStatus("Connected.")
+ acct.SetStatus(statusline.Connected(true))
})
case *types.Disconnect:
acct.dirlist.UpdateList(nil)
acct.msglist.SetStore(nil)
acct.logger.Println("Disconnected.")
- acct.host.SetStatus("Disconnected.")
+ acct.SetStatus(statusline.Connected(false))
case *types.OpenDirectory:
if store, ok := acct.dirlist.SelectedMsgStore(); ok {
// If we've opened this dir before, we can re-render it from
@@ -289,14 +308,14 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) {
case *types.LabelList:
acct.labels = msg.Labels
case *types.ConnError:
- acct.logger.Printf("connection error: %v", msg.Error)
- acct.host.SetStatus("Disconnected.")
- acct.aerc.PushError(fmt.Sprintf("%v", msg.Error))
+ acct.logger.Printf("connection error: [%s] %v", acct.acct.Name, msg.Error)
+ acct.SetStatus(statusline.Connected(false))
+ acct.PushError(msg.Error)
acct.msglist.SetStore(nil)
acct.worker.PostAction(&types.Reconnect{}, nil)
case *types.Error:
acct.logger.Printf("%v", msg.Error)
- acct.aerc.PushError(fmt.Sprintf("%v", msg.Error))
+ acct.PushError(msg.Error)
}
}
@@ -306,7 +325,7 @@ func (acct *AccountView) getSortCriteria() []*types.SortCriterion {
}
criteria, err := sort.GetSortCriteria(acct.UiConfig().Sort)
if err != nil {
- acct.aerc.PushError(" ui.sort: " + err.Error())
+ acct.PushError(fmt.Errorf("ui sort: %v", err))
return nil
}
return criteria
diff --git a/widgets/aerc.go b/widgets/aerc.go
index 3a8f47fd..a8b23fe7 100644
--- a/widgets/aerc.go
+++ b/widgets/aerc.go
@@ -337,6 +337,7 @@ func (aerc *Aerc) NumTabs() int {
func (aerc *Aerc) NewTab(clickable ui.Drawable, name string) *ui.Tab {
tab := aerc.tabs.Add(clickable, name)
aerc.tabs.Select(len(aerc.tabs.Tabs) - 1)
+ aerc.UpdateStatus()
return tab
}
@@ -400,17 +401,20 @@ func (aerc *Aerc) SelectPreviousTab() bool {
return aerc.tabs.SelectPrevious()
}
-// TODO: Use per-account status lines, but a global ex line
func (aerc *Aerc) SetStatus(status string) *StatusMessage {
return aerc.statusline.Set(status)
}
-func (aerc *Aerc) SetExtraStatus(status string) {
- aerc.statusline.SetExtra(status)
+func (aerc *Aerc) UpdateStatus() {
+ if acct := aerc.SelectedAccount(); acct != nil {
+ acct.UpdateStatus()
+ } else {
+ aerc.ClearStatus()
+ }
}
-func (aerc *Aerc) ClearExtraStatus() {
- aerc.statusline.ClearExtra()
+func (aerc *Aerc) ClearStatus() {
+ aerc.statusline.Set("")
}
func (aerc *Aerc) SetError(status string) *StatusMessage {
diff --git a/widgets/status.go b/widgets/status.go
index 960f2445..c70d215f 100644
--- a/widgets/status.go
+++ b/widgets/status.go
@@ -14,7 +14,6 @@ type StatusLine struct {
ui.Invalidatable
stack []*StatusMessage
fallback StatusMessage
- extra string
aerc *Aerc
uiConfig config.UIConfig
}
@@ -30,7 +29,6 @@ func NewStatusLine(uiConfig config.UIConfig) *StatusLine {
style: uiConfig.GetStyle(config.STYLE_STATUSLINE_DEFAULT),
message: "Idle",
},
- extra: "",
uiConfig: uiConfig,
}
}
@@ -51,11 +49,7 @@ func (status *StatusLine) Draw(ctx *ui.Context) {
pendingKeys += string(pendingKey.Rune)
}
}
- text := line.message
- if status.extra != "" {
- text += " " + status.extra
- }
- message := runewidth.FillRight(text, ctx.Width()-len(pendingKeys)-5)
+ message := runewidth.FillRight(line.message, ctx.Width()-len(pendingKeys)-5)
ctx.Printf(0, 0, line.style, "%s%s", message, pendingKeys)
}
@@ -109,14 +103,6 @@ func (status *StatusLine) PushSuccess(text string) *StatusMessage {
return msg
}
-func (status *StatusLine) SetExtra(text string) {
- status.extra = text
-}
-
-func (status *StatusLine) ClearExtra() {
- status.extra = ""
-}
-
func (status *StatusLine) Expire() {
status.stack = nil
}