Implement mailbox subscriptions
This commit is contained in:
parent
8cc742f45d
commit
51d762ac5f
4 changed files with 108 additions and 41 deletions
|
@ -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),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 -->
|
||||||
|
|
Loading…
Reference in a new issue