Browse Source

Generalize sidebar categorization logic

master
Drew DeVault 3 years ago
parent
commit
6ba418c9b2
5 changed files with 148 additions and 142 deletions
  1. +100
    -87
      plugins/base/routes.go
  2. +2
    -11
      themes/alps/compose.html
  3. +2
    -19
      themes/alps/mailbox.html
  4. +3
    -25
      themes/alps/message.html
  5. +41
    -0
      themes/alps/util.html

+ 100
- 87
plugins/base/routes.go View File

@@ -63,12 +63,16 @@ func registerRoutes(p *alps.GoPlugin) {
p.POST("/settings", handleSettings)
}

type MailboxRenderData struct {
type IMAPBaseRenderData struct {
alps.BaseRenderData
Mailbox *MailboxStatus
Inbox *MailboxStatus
CategorizedMailboxes CategorizedMailboxes
Mailboxes []MailboxInfo
Mailbox *MailboxStatus
Inbox *MailboxStatus
}

type MailboxRenderData struct {
IMAPBaseRenderData
Messages []IMAPMessage
PrevPage, NextPage int
Query string
@@ -87,21 +91,52 @@ type CategorizedMailboxes struct {
Additional []*MailboxInfo
}

func categorizeMailboxes(mailboxes []MailboxInfo,
inbox *MailboxStatus, active *MailboxStatus) CategorizedMailboxes {
func newIMAPBaseRenderData(ctx *alps.Context,
base *alps.BaseRenderData) (*IMAPBaseRenderData, error) {

mboxName, err := url.PathUnescape(ctx.Param("mbox"))
if err != nil {
return nil, echo.NewHTTPError(http.StatusBadRequest, err)
}

var out CategorizedMailboxes
var mailboxes []MailboxInfo
var active, inbox *MailboxStatus
err = ctx.Session.DoIMAP(func(c *imapclient.Client) error {
var err error
if mailboxes, err = listMailboxes(c); err != nil {
return err
}
if mboxName != "" {
if active, err = getMailboxStatus(c, mboxName); err != nil {
return err
}
}
if mboxName == "INBOX" {
inbox = active
} else {
if inbox, err = getMailboxStatus(c, "INBOX"); err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}

var categorized 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,
"INBOX": &categorized.Common.Inbox,
"Drafts": &categorized.Common.Drafts,
"Sent": &categorized.Common.Sent,
"Junk": &categorized.Common.Junk,
"Trash": &categorized.Common.Trash,
"Archive": &categorized.Common.Archive,
}

for i, _ := range mailboxes {
// Populate unseen & active states
if mailboxes[i].Name == active.Name {
if active != nil && mailboxes[i].Name == active.Name {
mailboxes[i].Unseen = int(active.Unseen)
mailboxes[i].Active = true
}
@@ -112,17 +147,35 @@ func categorizeMailboxes(mailboxes []MailboxInfo,
if ptr, ok := mmap[mailboxes[i].Name]; ok {
*ptr = &mailboxes[i]
} else {
out.Additional = append(out.Additional, &mailboxes[i])
categorized.Additional = append(
categorized.Additional, &mailboxes[i])
}
}
return out

return &IMAPBaseRenderData{
BaseRenderData: *base,
CategorizedMailboxes: categorized,
Mailboxes: mailboxes,
Inbox: inbox,
Mailbox: active,
}, nil
}

func handleGetMailbox(ctx *alps.Context) error {
mboxName, err := url.PathUnescape(ctx.Param("mbox"))
ibase, err := newIMAPBaseRenderData(ctx, alps.NewBaseRenderData(ctx))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
return err
}

mbox := ibase.Mailbox
title := mbox.Name
if title == "INBOX" {
title = "Inbox"
}
if mbox.Unseen > 0 {
title = fmt.Sprintf("(%d) %s", mbox.Unseen, title)
}
ibase.BaseRenderData.WithTitle(title)

page := 0
if pageStr := ctx.QueryParam("page"); pageStr != "" {
@@ -140,33 +193,20 @@ func handleGetMailbox(ctx *alps.Context) error {

query := ctx.QueryParam("query")

var mailboxes []MailboxInfo
var msgs []IMAPMessage
var mbox, inbox *MailboxStatus
var total int
var (
msgs []IMAPMessage
total int
)
err = ctx.Session.DoIMAP(func(c *imapclient.Client) error {
var err error
if mailboxes, err = listMailboxes(c); err != nil {
return err
}
if mbox, err = getMailboxStatus(c, mboxName); err != nil {
return err
}
var err error
if query != "" {
msgs, total, err = searchMessages(c, mboxName, query, page, messagesPerPage)
msgs, total, err = searchMessages(c, mbox.Name, query, page, messagesPerPage)
} else {
msgs, err = listMessages(c, mbox, page, messagesPerPage)
}
if err != nil {
return err
}
if mboxName == "INBOX" {
inbox = mbox
} else {
if inbox, err = getMailboxStatus(c, "INBOX"); err != nil {
return err
}
}
return nil
})
if err != nil {
@@ -190,23 +230,8 @@ func handleGetMailbox(ctx *alps.Context) error {
}
}

title := mbox.Name
if title == "INBOX" {
title = "Inbox"
}

if mbox.Unseen > 0 {
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,
CategorizedMailboxes: categorized,
Mailboxes: mailboxes,
IMAPBaseRenderData: *ibase,
Messages: msgs,
PrevPage: prevPage,
NextPage: nextPage,
@@ -262,10 +287,7 @@ func handleLogout(ctx *alps.Context) error {
}

type MessageRenderData struct {
alps.BaseRenderData
Mailboxes []MailboxInfo
Mailbox *MailboxStatus
Inbox *MailboxStatus
IMAPBaseRenderData
Message *IMAPMessage
Part *IMAPPartNode
View interface{}
@@ -274,10 +296,13 @@ type MessageRenderData struct {
}

func handleGetPart(ctx *alps.Context, raw bool) error {
mboxName, uid, err := parseMboxAndUid(ctx.Param("mbox"), ctx.Param("uid"))
_, uid, err := parseMboxAndUid(ctx.Param("mbox"), ctx.Param("uid"))
ibase, err := newIMAPBaseRenderData(ctx, alps.NewBaseRenderData(ctx))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
return err
}
mbox := ibase.Mailbox

partPath, err := parsePartPath(ctx.QueryParam("part"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
@@ -289,28 +314,13 @@ func handleGetPart(ctx *alps.Context, raw bool) error {
}
messagesPerPage := settings.MessagesPerPage

var mailboxes []MailboxInfo
var msg *IMAPMessage
var part *message.Entity
var mbox, inbox *MailboxStatus
err = ctx.Session.DoIMAP(func(c *imapclient.Client) error {
var err error
if mailboxes, err = listMailboxes(c); err != nil {
return err
}
if msg, part, err = getMessagePart(c, mboxName, uid, partPath); err != nil {
if msg, part, err = getMessagePart(c, mbox.Name, uid, partPath); err != nil {
return err
}
if mbox, err = getMailboxStatus(c, mboxName); err != nil {
return err
}
if mboxName == "INBOX" {
inbox = mbox
} else {
if inbox, err = getMailboxStatus(c, "INBOX"); err != nil {
return err
}
}
return nil
})
if err != nil {
@@ -367,22 +377,20 @@ func handleGetPart(ctx *alps.Context, raw bool) error {
flags[f] = msg.HasFlag(f)
}

ibase.BaseRenderData.WithTitle(msg.Envelope.Subject)

return ctx.Render(http.StatusOK, "message.html", &MessageRenderData{
BaseRenderData: *alps.NewBaseRenderData(ctx).
WithTitle(msg.Envelope.Subject),
Mailboxes: mailboxes,
Mailbox: mbox,
Inbox: inbox,
Message: msg,
Part: msg.PartByPath(partPath),
View: view,
MailboxPage: int(mbox.Messages-msg.SeqNum) / messagesPerPage,
Flags: flags,
IMAPBaseRenderData: *ibase,
Message: msg,
Part: msg.PartByPath(partPath),
View: view,
MailboxPage: int(mbox.Messages-msg.SeqNum) / messagesPerPage,
Flags: flags,
})
}

type ComposeRenderData struct {
alps.BaseRenderData
IMAPBaseRenderData
Message *OutgoingMessage
}

@@ -438,6 +446,11 @@ func submitCompose(ctx *alps.Context, msg *OutgoingMessage, options *composeOpti
}

func handleCompose(ctx *alps.Context, msg *OutgoingMessage, options *composeOptions) error {
ibase, err := newIMAPBaseRenderData(ctx, alps.NewBaseRenderData(ctx))
if err != nil {
return err
}

if msg.From == "" && strings.ContainsRune(ctx.Session.Username(), '@') {
msg.From = ctx.Session.Username()
}
@@ -537,8 +550,8 @@ func handleCompose(ctx *alps.Context, msg *OutgoingMessage, options *composeOpti
}

return ctx.Render(http.StatusOK, "compose.html", &ComposeRenderData{
BaseRenderData: *alps.NewBaseRenderData(ctx),
Message: msg,
IMAPBaseRenderData: *ibase,
Message: msg,
})
}



+ 2
- 11
themes/alps/compose.html View File

@@ -1,18 +1,9 @@
{{template "head.html" .}}
{{template "nav.html" .}}
{{template "util.html" .}}

<div class="page-wrap">
<aside>
<a href="/compose" class="new active">Compose&nbsp;Mail</a>
<!-- TODO: use mailbox list from template data -->
<a href="/mailbox/INBOX">Inbox</a>
<a href="/mailbox/Drafts">Drafts</a>
<a href="/mailbox/Sent">Sent</a>
<a href="/mailbox/Archive">Archive</a>
<a href="/mailbox/Junk">Junk</a>
<a href="/mailbox/Trash">Trash</a>
</aside>
{{ template "aside" . }}

<div class="container">
<main class="create-update">


+ 2
- 19
themes/alps/mailbox.html View File

@@ -1,5 +1,6 @@
{{template "head.html" .}}
{{template "nav.html" .}}
{{template "util.html" .}}

{{ define "mbox-link" }}
{{ if not (.HasAttr "\\Noselect") }}
@@ -21,25 +22,7 @@
{{ end }}

<div class="page-wrap">
<aside>
<!-- the logo image, dimensions 200x32 may be present or not -->
<a href="/compose" class="new">Compose&nbsp;Mail</a>
{{ 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>

{{ template "aside" . }}
<div class="container">
<form id="messages-form" method="post"></form>
<main class="message-list">


+ 3
- 25
themes/alps/message.html View File

@@ -1,5 +1,6 @@
{{template "head.html" .}}
{{template "nav.html" .}}
{{template "util.html" .}}

{{define "message-part-tree"}}
{{/* nested templates can't access the parent's context */}}
@@ -28,31 +29,8 @@
{{end}}

<div class="page-wrap">
{{$current := .Mailbox}}
<aside>
<!-- the logo image, dimensions 200x32 may be present or not -->
<a href="/compose" class="new">Compose&nbsp;Mail</a>
{{range .Mailboxes}}
<a href="{{.URL}}"
{{ if eq $current.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}}
</aside>

{{ $current := .Mailbox }}
{{ template "aside" . }}
<div class="container">
<main class="message">
<section class="actions">


+ 41
- 0
themes/alps/util.html View File

@@ -0,0 +1,41 @@
{{ 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 }}

{{ define "aside" }}
<aside>
<!-- the logo image, dimensions 200x32 may be present or not -->
<a href="/compose" class="new
{{ if eq $.GlobalData.URL.Path "/compose" }}active{{ end }}
">Compose&nbsp;Mail</a>
{{ 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>
{{ end }}

Loading…
Cancel
Save