Implement mailbox subscriptions

This commit is contained in:
Drew DeVault 2020-11-19 13:14:12 -05:00
parent 8cc742f45d
commit 51d762ac5f
4 changed files with 108 additions and 41 deletions

View File

@ -80,6 +80,7 @@ type IMAPBaseRenderData struct {
Mailboxes []MailboxInfo Mailboxes []MailboxInfo
Mailbox *MailboxStatus Mailbox *MailboxStatus
Inbox *MailboxStatus Inbox *MailboxStatus
Subscriptions map[string]*MailboxStatus
} }
type MailboxRenderData struct { type MailboxRenderData struct {
@ -89,17 +90,22 @@ type MailboxRenderData struct {
Query string Query string
} }
type MailboxDetails struct {
Info *MailboxInfo
Status *MailboxStatus
}
// Organizes mailboxes into common/uncommon categories // Organizes mailboxes into common/uncommon categories
type CategorizedMailboxes struct { type CategorizedMailboxes struct {
Common struct { Common struct {
Inbox *MailboxInfo Inbox MailboxDetails
Drafts *MailboxInfo Drafts MailboxDetails
Sent *MailboxInfo Sent MailboxDetails
Junk *MailboxInfo Junk MailboxDetails
Trash *MailboxInfo Trash MailboxDetails
Archive *MailboxInfo Archive MailboxDetails
} }
Additional []*MailboxInfo Additional []MailboxDetails
} }
func newIMAPBaseRenderData(ctx *alps.Context, func newIMAPBaseRenderData(ctx *alps.Context,
@ -110,6 +116,12 @@ func newIMAPBaseRenderData(ctx *alps.Context,
return nil, echo.NewHTTPError(http.StatusBadRequest, err) return nil, echo.NewHTTPError(http.StatusBadRequest, err)
} }
settings, err := loadSettings(ctx.Session.Store())
if err != nil {
return nil, fmt.Errorf("failed to load settings: %v", err)
}
subscriptions := make(map[string]*MailboxStatus)
var mailboxes []MailboxInfo var mailboxes []MailboxInfo
var active, inbox *MailboxStatus var active, inbox *MailboxStatus
err = ctx.Session.DoIMAP(func(c *imapclient.Client) error { err = ctx.Session.DoIMAP(func(c *imapclient.Client) error {
@ -117,11 +129,13 @@ func newIMAPBaseRenderData(ctx *alps.Context,
if mailboxes, err = listMailboxes(c); err != nil { if mailboxes, err = listMailboxes(c); err != nil {
return err return err
} }
if mboxName != "" { if mboxName != "" {
if active, err = getMailboxStatus(c, mboxName); err != nil { if active, err = getMailboxStatus(c, mboxName); err != nil {
return echo.NewHTTPError(http.StatusNotFound, err) return echo.NewHTTPError(http.StatusNotFound, err)
} }
} }
if mboxName == "INBOX" { if mboxName == "INBOX" {
inbox = active inbox = active
} else { } else {
@ -129,6 +143,14 @@ func newIMAPBaseRenderData(ctx *alps.Context,
return err return err
} }
} }
for _, sub := range settings.Subscriptions {
if status, err := getMailboxStatus(c, sub); err != nil {
return err
} else {
subscriptions[sub] = status
}
}
return nil return nil
}) })
if err != nil { if err != nil {
@ -136,7 +158,7 @@ func newIMAPBaseRenderData(ctx *alps.Context,
} }
var categorized CategorizedMailboxes var categorized CategorizedMailboxes
mmap := map[string]**MailboxInfo{ mmap := map[string]*MailboxDetails{
"INBOX": &categorized.Common.Inbox, "INBOX": &categorized.Common.Inbox,
"Drafts": &categorized.Common.Drafts, "Drafts": &categorized.Common.Drafts,
"Sent": &categorized.Common.Sent, "Sent": &categorized.Common.Sent,
@ -157,11 +179,16 @@ func newIMAPBaseRenderData(ctx *alps.Context,
mailboxes[i].Total = int(inbox.Messages) mailboxes[i].Total = int(inbox.Messages)
} }
status, _ := subscriptions[mailboxes[i].Name]
if ptr, ok := mmap[mailboxes[i].Name]; ok { if ptr, ok := mmap[mailboxes[i].Name]; ok {
*ptr = &mailboxes[i] ptr.Info = &mailboxes[i]
ptr.Status = status
} else { } else {
categorized.Additional = append( categorized.Additional = append(categorized.Additional,
categorized.Additional, &mailboxes[i]) MailboxDetails{
Info: &mailboxes[i],
Status: status,
})
} }
} }
@ -171,6 +198,7 @@ func newIMAPBaseRenderData(ctx *alps.Context,
Mailboxes: mailboxes, Mailboxes: mailboxes,
Inbox: inbox, Inbox: inbox,
Mailbox: active, Mailbox: active,
Subscriptions: subscriptions,
}, nil }, nil
} }
@ -1156,6 +1184,7 @@ type Settings struct {
MessagesPerPage int MessagesPerPage int
Signature string Signature string
From string From string
Subscriptions []string
} }
func loadSettings(s alps.Store) (*Settings, error) { func loadSettings(s alps.Store) (*Settings, error) {
@ -1186,7 +1215,20 @@ func (s *Settings) check() error {
type SettingsRenderData struct { type SettingsRenderData struct {
alps.BaseRenderData alps.BaseRenderData
Settings *Settings Mailboxes []MailboxInfo
Settings *Settings
Subscriptions Subscriptions
}
type Subscriptions []string
func (s Subscriptions) Has(sub string) bool {
for _, cand := range s {
if cand == sub {
return true
}
}
return false
} }
func handleSettings(ctx *alps.Context) error { func handleSettings(ctx *alps.Context) error {
@ -1195,6 +1237,15 @@ func handleSettings(ctx *alps.Context) error {
return fmt.Errorf("failed to load settings: %v", err) return fmt.Errorf("failed to load settings: %v", err)
} }
var mailboxes []MailboxInfo
err = ctx.Session.DoIMAP(func(c *imapclient.Client) error {
mailboxes, err = listMailboxes(c)
return err
})
if err != nil {
return err
}
if ctx.Request().Method == http.MethodPost { if ctx.Request().Method == http.MethodPost {
settings.MessagesPerPage, err = strconv.Atoi(ctx.FormValue("messages_per_page")) settings.MessagesPerPage, err = strconv.Atoi(ctx.FormValue("messages_per_page"))
if err != nil { if err != nil {
@ -1203,6 +1254,12 @@ func handleSettings(ctx *alps.Context) error {
settings.Signature = ctx.FormValue("signature") settings.Signature = ctx.FormValue("signature")
settings.From = ctx.FormValue("from") settings.From = ctx.FormValue("from")
params, err := ctx.FormParams()
if err != nil {
return err
}
settings.Subscriptions = params["subscriptions"]
if err := settings.check(); err != nil { if err := settings.check(); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err) return echo.NewHTTPError(http.StatusBadRequest, err)
} }
@ -1216,5 +1273,7 @@ func handleSettings(ctx *alps.Context) error {
return ctx.Render(http.StatusOK, "settings.html", &SettingsRenderData{ return ctx.Render(http.StatusOK, "settings.html", &SettingsRenderData{
BaseRenderData: *alps.NewBaseRenderData(ctx), BaseRenderData: *alps.NewBaseRenderData(ctx),
Settings: settings, Settings: settings,
Mailboxes: mailboxes,
Subscriptions: Subscriptions(settings.Subscriptions),
}) })
} }

View File

@ -88,7 +88,8 @@ input[type="file"],
input[type="number"], input[type="number"],
input[type="date"], input[type="date"],
input[type="time"], input[type="time"],
textarea { textarea,
select {
margin: 0; margin: 0;
border: none; border: none;
border: 1px solid #e0e0e0; border: 1px solid #e0e0e0;
@ -157,7 +158,7 @@ aside ul {
aside li { aside li {
width: 100%; width: 100%;
display: flex; display: flex;
padding: 0.4rem 0 0.4rem 0.5rem; padding: 0.4rem 0.5rem;
} }
aside li a { aside li a {
@ -654,7 +655,8 @@ main table tfoot {
.action-group label, .action-group label,
.action-group input, .action-group input,
.action-group textarea { .action-group textarea,
.action-group select {
display: block; display: block;
width: 100%; width: 100%;
} }
@ -665,6 +667,10 @@ main table tfoot {
float: left; float: left;
} }
.action-group select {
height: 10rem;
}
.actions-message, .actions-message,
.actions-contacts { .actions-contacts {
display: flex; display: flex;

View File

@ -32,6 +32,23 @@
>{{.Settings.Signature}}</textarea> >{{.Settings.Signature}}</textarea>
</div> </div>
<div class="action-group">
<label for="subscriptions">Subscribed folders</label>
<select name="subscriptions" id="subscriptions" multiple>
{{ $subs := .Subscriptions }}
{{ range .Mailboxes }}
{{ if and (ne .Name "INBOX") (not (.HasAttr "\\Noselect")) }}
<option
value="{{.Name}}"
{{ if $subs.Has .Name }}
selected
{{ end }}
>{{.Name}}</option>
{{ end }}
{{ end }}
</select>
</div>
<div class="action-group"> <div class="action-group">
<label for="messages_per_page">Messages per page</label> <label for="messages_per_page">Messages per page</label>
<input <input

View File

@ -1,43 +1,28 @@
{{ define "mbox-link" }} {{ define "mbox-link" }}
{{ if not (.HasAttr "\\Noselect") }} {{ if not (.Info.HasAttr "\\Noselect") }}
<li {{ if .Active }}class="active"{{ end }}> <li {{ if .Info.Active }}class="active"{{ end }}>
<a href="{{.URL}}"> <a href="{{.Info.URL}}">
{{- if eq .Name "INBOX" -}} {{- if eq .Info.Name "INBOX" -}}
Inbox Inbox
{{- else -}} {{- else -}}
{{ .Name }} {{ .Info.Name }}
{{- end -}} {{- end -}}
{{- if .HasAttr "\\HasChildren" }}/{{ end }} {{- if .Info.HasAttr "\\HasChildren" }}/{{ end }}
</a> </a>
<!-- TODO {{ if .Status }}
<button {{ if .Status.Unseen }}
type="submit" <span class="unseen">({{.Status.Unseen}})</span>
name="subscribe" {{ end }}
value="{{.Name}}"
form="subscribe-form"
{{ if eq .Name "INBOX" }}
title="Unsubscribe"
{{ else }}
title="Subscribe"
{{ end }}
>
{{ if eq .Name "INBOX" }}
{{ else }}
{{ end }} {{ end }}
</button>
-->
</li> </li>
{{ else }} {{ else }}
<li class="noselect"> <li class="noselect">
{{.Name}}{{- if .HasAttr "\\HasChildren" }}/{{ end }} {{.Info.Name}}{{- if .Info.HasAttr "\\HasChildren" }}/{{ end }}
</li> </li>
{{ end }} {{ end }}
{{ end }} {{ end }}
{{ define "aside" }} {{ define "aside" }}
<form id="subscribe-form" method="POST"></form>
<aside> <aside>
<ul> <ul>
<!-- the logo image, dimensions 200x32 may be present or not --> <!-- the logo image, dimensions 200x32 may be present or not -->