account.go (5090B)
1 package widgets 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/gdamore/tcell" 8 9 "git.sr.ht/~sircmpwn/aerc/config" 10 "git.sr.ht/~sircmpwn/aerc/lib" 11 "git.sr.ht/~sircmpwn/aerc/lib/ui" 12 "git.sr.ht/~sircmpwn/aerc/worker" 13 "git.sr.ht/~sircmpwn/aerc/worker/types" 14 ) 15 16 type AccountView struct { 17 acct *config.AccountConfig 18 conf *config.AercConfig 19 dirlist *DirectoryList 20 grid *ui.Grid 21 host TabHost 22 logger *log.Logger 23 msglist *MessageList 24 msgStores map[string]*lib.MessageStore 25 worker *types.Worker 26 } 27 28 func NewAccountView(conf *config.AercConfig, acct *config.AccountConfig, 29 logger *log.Logger, host TabHost) *AccountView { 30 31 grid := ui.NewGrid().Rows([]ui.GridSpec{ 32 {ui.SIZE_WEIGHT, 1}, 33 }).Columns([]ui.GridSpec{ 34 {ui.SIZE_EXACT, conf.Ui.SidebarWidth}, 35 {ui.SIZE_WEIGHT, 1}, 36 }) 37 38 worker, err := worker.NewWorker(acct.Source, logger) 39 if err != nil { 40 host.SetStatus(fmt.Sprintf("%s: %s", acct.Name, err)). 41 Color(tcell.ColorDefault, tcell.ColorRed) 42 return &AccountView{ 43 acct: acct, 44 grid: grid, 45 host: host, 46 logger: logger, 47 } 48 } 49 50 dirlist := NewDirectoryList(acct, logger, worker) 51 grid.AddChild(ui.NewBordered(dirlist, ui.BORDER_RIGHT)) 52 53 msglist := NewMessageList(conf, logger) 54 grid.AddChild(msglist).At(0, 1) 55 56 view := &AccountView{ 57 acct: acct, 58 conf: conf, 59 dirlist: dirlist, 60 grid: grid, 61 host: host, 62 logger: logger, 63 msglist: msglist, 64 msgStores: make(map[string]*lib.MessageStore), 65 worker: worker, 66 } 67 68 go worker.Backend.Run() 69 70 worker.PostAction(&types.Configure{Config: acct}, nil) 71 worker.PostAction(&types.Connect{}, view.connected) 72 host.SetStatus("Connecting...") 73 74 return view 75 } 76 77 func (acct *AccountView) Tick() bool { 78 if acct.worker == nil { 79 return false 80 } 81 select { 82 case msg := <-acct.worker.Messages: 83 msg = acct.worker.ProcessMessage(msg) 84 acct.onMessage(msg) 85 return true 86 default: 87 return false 88 } 89 } 90 91 func (acct *AccountView) AccountConfig() *config.AccountConfig { 92 return acct.acct 93 } 94 95 func (acct *AccountView) Worker() *types.Worker { 96 return acct.worker 97 } 98 99 func (acct *AccountView) Logger() *log.Logger { 100 return acct.logger 101 } 102 103 func (acct *AccountView) Name() string { 104 return acct.acct.Name 105 } 106 107 func (acct *AccountView) Children() []ui.Drawable { 108 return acct.grid.Children() 109 } 110 111 func (acct *AccountView) OnInvalidate(onInvalidate func(d ui.Drawable)) { 112 acct.grid.OnInvalidate(func(_ ui.Drawable) { 113 onInvalidate(acct) 114 }) 115 } 116 117 func (acct *AccountView) Invalidate() { 118 acct.grid.Invalidate() 119 } 120 121 func (acct *AccountView) Draw(ctx *ui.Context) { 122 acct.grid.Draw(ctx) 123 } 124 125 func (acct *AccountView) Focus(focus bool) { 126 // TODO: Unfocus children I guess 127 } 128 129 func (acct *AccountView) connected(msg types.WorkerMessage) { 130 switch msg.(type) { 131 case *types.Done: 132 acct.host.SetStatus("Listing mailboxes...") 133 acct.logger.Println("Listing mailboxes...") 134 acct.dirlist.UpdateList(func(dirs []string) { 135 var dir string 136 for _, _dir := range dirs { 137 if _dir == acct.acct.Default { 138 dir = _dir 139 break 140 } 141 } 142 if dir == "" { 143 dir = dirs[0] 144 } 145 acct.dirlist.Select(dir) 146 acct.logger.Println("Connected.") 147 acct.host.SetStatus("Connected.") 148 }) 149 } 150 } 151 152 func (acct *AccountView) Directories() *DirectoryList { 153 return acct.dirlist 154 } 155 156 func (acct *AccountView) Messages() *MessageList { 157 return acct.msglist 158 } 159 160 func (acct *AccountView) Store() *lib.MessageStore { 161 return acct.msglist.Store() 162 } 163 164 func (acct *AccountView) SelectedMessage() *types.MessageInfo { 165 return acct.msglist.Selected() 166 } 167 168 func (acct *AccountView) SelectedAccount() *AccountView { 169 return acct 170 } 171 172 func (acct *AccountView) onMessage(msg types.WorkerMessage) { 173 switch msg := msg.(type) { 174 case *types.Done: 175 switch msg.InResponseTo().(type) { 176 case *types.OpenDirectory: 177 if store, ok := acct.msgStores[acct.dirlist.selected]; ok { 178 // If we've opened this dir before, we can re-render it from 179 // memory while we wait for the update and the UI feels 180 // snappier. If not, we'll unset the store and show the spinner 181 // while we download the UID list. 182 acct.msglist.SetStore(store) 183 } else { 184 acct.msglist.SetStore(nil) 185 } 186 } 187 case *types.DirectoryInfo: 188 if store, ok := acct.msgStores[msg.Name]; ok { 189 store.Update(msg) 190 } else { 191 store = lib.NewMessageStore(acct.worker, msg) 192 acct.msgStores[msg.Name] = store 193 store.OnUpdate(func(_ *lib.MessageStore) { 194 store.OnUpdate(nil) 195 acct.msglist.SetStore(store) 196 }) 197 } 198 case *types.DirectoryContents: 199 if store, ok := acct.msgStores[acct.dirlist.selected]; ok { 200 store.Update(msg) 201 } 202 case *types.FullMessage: 203 if store, ok := acct.msgStores[acct.dirlist.selected]; ok { 204 store.Update(msg) 205 } 206 case *types.MessageInfo: 207 if store, ok := acct.msgStores[acct.dirlist.selected]; ok { 208 store.Update(msg) 209 } 210 case *types.MessagesDeleted: 211 if store, ok := acct.msgStores[acct.dirlist.selected]; ok { 212 store.Update(msg) 213 } 214 case *types.Error: 215 acct.logger.Printf("%v", msg.Error) 216 acct.host.SetStatus(fmt.Sprintf("%v", msg.Error)). 217 Color(tcell.ColorDefault, tcell.ColorRed) 218 } 219 }