@@ -19,6 +19,9 @@ import ( | |||
type MailboxInfo struct { | |||
*imap.MailboxInfo | |||
Active bool | |||
Unseen int | |||
} | |||
func (mbox *MailboxInfo) URL() *url.URL { | |||
@@ -27,6 +30,15 @@ func (mbox *MailboxInfo) URL() *url.URL { | |||
} | |||
} | |||
func (mbox *MailboxInfo) HasAttr(flag string) bool { | |||
for _, attr := range mbox.Attributes { | |||
if attr == flag { | |||
return true | |||
} | |||
} | |||
return false | |||
} | |||
func listMailboxes(conn *imapclient.Client) ([]MailboxInfo, error) { | |||
ch := make(chan *imap.MailboxInfo, 10) | |||
done := make(chan error, 1) | |||
@@ -36,7 +48,7 @@ func listMailboxes(conn *imapclient.Client) ([]MailboxInfo, error) { | |||
var mailboxes []MailboxInfo | |||
for mbox := range ch { | |||
mailboxes = append(mailboxes, MailboxInfo{mbox}) | |||
mailboxes = append(mailboxes, MailboxInfo{mbox, false, -1}) | |||
} | |||
if err := <-done; err != nil { | |||
@@ -133,7 +145,7 @@ func getMailboxByType(conn *imapclient.Client, mboxType mailboxType) (*MailboxIn | |||
if best == nil { | |||
return nil, nil | |||
} | |||
return &MailboxInfo{best}, nil | |||
return &MailboxInfo{best, false, -1}, nil | |||
} | |||
func ensureMailboxSelected(conn *imapclient.Client, mboxName string) error { | |||
@@ -65,12 +65,57 @@ func registerRoutes(p *alps.GoPlugin) { | |||
type MailboxRenderData struct { | |||
alps.BaseRenderData | |||
Mailbox *MailboxStatus | |||
Inbox *MailboxStatus | |||
Mailboxes []MailboxInfo | |||
Messages []IMAPMessage | |||
PrevPage, NextPage int | |||
Query string | |||
Mailbox *MailboxStatus | |||
Inbox *MailboxStatus | |||
CategorizedMailboxes CategorizedMailboxes | |||
Mailboxes []MailboxInfo | |||
Messages []IMAPMessage | |||
PrevPage, NextPage int | |||
Query string | |||
} | |||
// Organizes mailboxes into common/uncommon categories | |||
type CategorizedMailboxes struct { | |||
Common struct { | |||
Inbox *MailboxInfo | |||
Drafts *MailboxInfo | |||
Sent *MailboxInfo | |||
Junk *MailboxInfo | |||
Trash *MailboxInfo | |||
Archive *MailboxInfo | |||
} | |||
Additional []*MailboxInfo | |||
} | |||
func categorizeMailboxes(mailboxes []MailboxInfo, | |||
inbox *MailboxStatus, active *MailboxStatus) CategorizedMailboxes { | |||
var out CategorizedMailboxes | |||
mmap := map[string]**MailboxInfo{ | |||
"INBOX": &out.Common.Inbox, | |||
"Drafts": &out.Common.Drafts, | |||
"Sent": &out.Common.Sent, | |||
"Junk": &out.Common.Junk, | |||
"Trash": &out.Common.Trash, | |||
"Archive": &out.Common.Archive, | |||
} | |||
for i, _ := range mailboxes { | |||
// Populate unseen & active states | |||
if mailboxes[i].Name == active.Name { | |||
mailboxes[i].Unseen = int(active.Unseen) | |||
mailboxes[i].Active = true | |||
} | |||
if mailboxes[i].Name == inbox.Name { | |||
mailboxes[i].Unseen = int(inbox.Unseen) | |||
} | |||
if ptr, ok := mmap[mailboxes[i].Name]; ok { | |||
*ptr = &mailboxes[i] | |||
} else { | |||
out.Additional = append(out.Additional, &mailboxes[i]) | |||
} | |||
} | |||
return out | |||
} | |||
func handleGetMailbox(ctx *alps.Context) error { | |||
@@ -154,15 +199,18 @@ func handleGetMailbox(ctx *alps.Context) error { | |||
title = fmt.Sprintf("(%d) %s", mbox.Unseen, title) | |||
} | |||
categorized := categorizeMailboxes(mailboxes, inbox, mbox) | |||
return ctx.Render(http.StatusOK, "mailbox.html", &MailboxRenderData{ | |||
BaseRenderData: *alps.NewBaseRenderData(ctx).WithTitle(title), | |||
Mailbox: mbox, | |||
Inbox: inbox, | |||
Mailboxes: mailboxes, | |||
Messages: msgs, | |||
PrevPage: prevPage, | |||
NextPage: nextPage, | |||
Query: query, | |||
Mailbox: mbox, | |||
Inbox: inbox, | |||
CategorizedMailboxes: categorized, | |||
Mailboxes: mailboxes, | |||
Messages: msgs, | |||
PrevPage: prevPage, | |||
NextPage: nextPage, | |||
Query: query, | |||
}) | |||
} | |||
@@ -127,7 +127,8 @@ footer { text-align: right; } | |||
aside { flex: 0 0 180px; } | |||
aside a { width: 100%; display: block; padding: 0.4rem 0 0.4rem 0.5rem; } | |||
aside a, | |||
aside .noselect { width: 100%; display: block; padding: 0.4rem 0 0.4rem 0.5rem; } | |||
aside a.active { font-weight: bold; color: black; text-decoration: none; } | |||
aside img { display: block; } | |||
main { | |||
@@ -1,28 +1,43 @@ | |||
{{template "head.html" .}} | |||
{{template "nav.html" .}} | |||
{{ define "mbox-link" }} | |||
{{ if not (.HasAttr "\\Noselect") }} | |||
<a href="{{.URL}}" {{ if .Active }}class="active"{{ end }}> | |||
{{- if eq .Name "INBOX" -}} | |||
Inbox | |||
{{- else -}} | |||
{{ .Name }} | |||
{{- end -}} | |||
{{- if .HasAttr "\\HasChildren" }}/{{ end }} | |||
{{ if and (ne .Unseen -1) (ne .Unseen 0) }}({{ .Unseen }}){{ end }} | |||
</a> | |||
{{ else }} | |||
<span class="noselect"> | |||
{{.Name}}{{- if .HasAttr "\\HasChildren" }}/{{ end }} | |||
</span> | |||
{{ end }} | |||
{{ end }} | |||
<div class="page-wrap"> | |||
<aside> | |||
<!-- the logo image, dimensions 200x32 may be present or not --> | |||
<a href="/compose" class="new">Compose Mail</a> | |||
{{range .Mailboxes}} | |||
<a href="{{.URL}}" {{ if eq $.Mailbox.Name .Name }}class="active"{{ end }}> | |||
{{ if eq .Name "INBOX" }} | |||
Inbox | |||
{{ else }} | |||
{{ .Name }} | |||
{{ end }} | |||
{{ $unseen := 0 }} | |||
{{ if eq .Name "INBOX" }} | |||
{{ $unseen = $.Inbox.Unseen }} | |||
{{ end }} | |||
{{ if eq .Name $.Mailbox.Name }} | |||
{{ $unseen = $.Mailbox.Unseen }} | |||
{{ end }} | |||
{{ if $unseen }}({{ $unseen }}){{ end }} | |||
</a> | |||
{{end}} | |||
{{ with .CategorizedMailboxes }} | |||
{{ with .Common.Inbox }}{{ template "mbox-link" . }}{{ end}} | |||
{{ with .Common.Drafts }}{{ template "mbox-link" . }}{{ end}} | |||
{{ with .Common.Sent }}{{ template "mbox-link" . }}{{ end}} | |||
{{ with .Common.Junk }}{{ template "mbox-link" . }}{{ end}} | |||
{{ with .Common.Trash }}{{ template "mbox-link" . }}{{ end}} | |||
{{ with .Common.Archive }}{{ template "mbox-link" . }}{{ end}} | |||
{{ if .Additional }} | |||
<hr /> | |||
{{ range .Additional }} | |||
{{ template "mbox-link" . }} | |||
{{ end }} | |||
{{ end }} | |||
{{ end }} | |||
</aside> | |||
<div class="container"> | |||