plugins/base: add settings page
Add a settings page where the user can change the number of messages displayed per page.
This commit is contained in:
parent
bdf1a8b02b
commit
4d68400036
5 changed files with 100 additions and 7 deletions
|
@ -237,7 +237,7 @@ func (msg *IMAPMessage) HasFlag(flag string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func listMessages(conn *imapclient.Client, mboxName string, page int) ([]IMAPMessage, error) {
|
func listMessages(conn *imapclient.Client, mboxName string, page, messagesPerPage int) ([]IMAPMessage, error) {
|
||||||
if err := ensureMailboxSelected(conn, mboxName); err != nil {
|
if err := ensureMailboxSelected(conn, mboxName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -281,7 +281,7 @@ func listMessages(conn *imapclient.Client, mboxName string, page int) ([]IMAPMes
|
||||||
return msgs, nil
|
return msgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func searchMessages(conn *imapclient.Client, mboxName, query string, page int) (msgs []IMAPMessage, total int, err error) {
|
func searchMessages(conn *imapclient.Client, mboxName, query string, page, messagesPerPage int) (msgs []IMAPMessage, total int, err error) {
|
||||||
if err := ensureMailboxSelected(conn, mboxName); err != nil {
|
if err := ensureMailboxSelected(conn, mboxName); err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@ import (
|
||||||
"git.sr.ht/~emersion/koushin"
|
"git.sr.ht/~emersion/koushin"
|
||||||
)
|
)
|
||||||
|
|
||||||
const messagesPerPage = 50
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
p := koushin.GoPlugin{Name: "base"}
|
p := koushin.GoPlugin{Name: "base"}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
<h1>koushin</h1>
|
<h1>koushin</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="/logout">Logout</a> · <a href="/compose">Compose</a>
|
<a href="/logout">Logout</a>
|
||||||
|
· <a href="/compose">Compose</a>
|
||||||
|
· <a href="/settings">Settings</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>{{.Mailbox.Name}}</h2>
|
<h2>{{.Mailbox.Name}}</h2>
|
||||||
|
|
18
plugins/base/public/settings.html
Normal file
18
plugins/base/public/settings.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{{template "head.html"}}
|
||||||
|
|
||||||
|
<h1>koushin</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="/mailbox/INBOX">Back</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Settings</h2>
|
||||||
|
|
||||||
|
<form method="post" action="">
|
||||||
|
<label for="messages_per_page">Messages per page:</label>
|
||||||
|
<input type="number" name="messages_per_page" id="messages_per_page" required value="{{.Settings.MessagesPerPage}}">
|
||||||
|
<br><br>
|
||||||
|
<input type="submit" value="Save">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{{template "foot.html"}}
|
|
@ -55,6 +55,9 @@ func registerRoutes(p *koushin.GoPlugin) {
|
||||||
p.POST("/message/:mbox/:uid/delete", handleDelete)
|
p.POST("/message/:mbox/:uid/delete", handleDelete)
|
||||||
|
|
||||||
p.POST("/message/:mbox/:uid/flag", handleSetFlags)
|
p.POST("/message/:mbox/:uid/flag", handleSetFlags)
|
||||||
|
|
||||||
|
p.GET("/settings", handleSettings)
|
||||||
|
p.POST("/settings", handleSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
type MailboxRenderData struct {
|
type MailboxRenderData struct {
|
||||||
|
@ -80,6 +83,12 @@ func handleGetMailbox(ctx *koushin.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settings, err := loadSettings(ctx.Session.Store())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
messagesPerPage := settings.MessagesPerPage
|
||||||
|
|
||||||
query := ctx.QueryParam("query")
|
query := ctx.QueryParam("query")
|
||||||
|
|
||||||
var mailboxes []*imap.MailboxInfo
|
var mailboxes []*imap.MailboxInfo
|
||||||
|
@ -92,9 +101,9 @@ func handleGetMailbox(ctx *koushin.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if query != "" {
|
if query != "" {
|
||||||
msgs, total, err = searchMessages(c, mboxName, query, page)
|
msgs, total, err = searchMessages(c, mboxName, query, page, messagesPerPage)
|
||||||
} else {
|
} else {
|
||||||
msgs, err = listMessages(c, mboxName, page)
|
msgs, err = listMessages(c, mboxName, page, messagesPerPage)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -185,6 +194,12 @@ func handleGetPart(ctx *koushin.Context, raw bool) error {
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, err)
|
return echo.NewHTTPError(http.StatusBadRequest, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settings, err := loadSettings(ctx.Session.Store())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
messagesPerPage := settings.MessagesPerPage
|
||||||
|
|
||||||
var mailboxes []*imap.MailboxInfo
|
var mailboxes []*imap.MailboxInfo
|
||||||
var msg *IMAPMessage
|
var msg *IMAPMessage
|
||||||
var part *message.Entity
|
var part *message.Entity
|
||||||
|
@ -685,3 +700,63 @@ func handleSetFlags(ctx *koushin.Context) error {
|
||||||
|
|
||||||
return ctx.Redirect(http.StatusFound, fmt.Sprintf("/message/%v/%v", url.PathEscape(mboxName), uid))
|
return ctx.Redirect(http.StatusFound, fmt.Sprintf("/message/%v/%v", url.PathEscape(mboxName), uid))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const settingsKey = "base.settings"
|
||||||
|
const maxMessagesPerPage = 100
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
MessagesPerPage int
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSettings(s koushin.Store) (*Settings, error) {
|
||||||
|
settings := &Settings{
|
||||||
|
MessagesPerPage: 50,
|
||||||
|
}
|
||||||
|
if err := s.Get(settingsKey, settings); err != nil && err != koushin.ErrNoStoreEntry {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := settings.check(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return settings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Settings) check() error {
|
||||||
|
if s.MessagesPerPage <= 0 || s.MessagesPerPage > maxMessagesPerPage {
|
||||||
|
return fmt.Errorf("messages per page out of bounds: %v", s.MessagesPerPage)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SettingsRenderData struct {
|
||||||
|
koushin.BaseRenderData
|
||||||
|
Settings *Settings
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSettings(ctx *koushin.Context) error {
|
||||||
|
settings, err := loadSettings(ctx.Session.Store())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to load settings: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Request().Method == http.MethodPost {
|
||||||
|
settings.MessagesPerPage, err = strconv.Atoi(ctx.FormValue("messages_per_page"))
|
||||||
|
if err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, "invalid messages per page: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := settings.check(); err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, err)
|
||||||
|
}
|
||||||
|
if err := ctx.Session.Store().Put(settingsKey, settings); err != nil {
|
||||||
|
return fmt.Errorf("failed to save settings: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.Redirect(http.StatusFound, "/mailbox/INBOX")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.Render(http.StatusOK, "settings.html", &SettingsRenderData{
|
||||||
|
BaseRenderData: *koushin.NewBaseRenderData(ctx),
|
||||||
|
Settings: settings,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue