Add basic search

Closes: https://todo.sr.ht/~sircmpwn/koushin/23
This commit is contained in:
Simon Ser 2019-12-16 14:36:43 +01:00
parent 1a658fc22c
commit 1c5e17472d
No known key found for this signature in database
GPG key ID: 0FDE7BE0E88F5E48
5 changed files with 74 additions and 9 deletions

View file

@ -23,6 +23,7 @@ type MailboxRenderData struct {
Mailboxes []*imap.MailboxInfo
Messages []imapMessage
PrevPage, NextPage int
Query string
}
func handleGetMailbox(ectx echo.Context) error {
@ -41,6 +42,8 @@ func handleGetMailbox(ectx echo.Context) error {
}
}
query := ctx.FormValue("query")
var mailboxes []*imap.MailboxInfo
var msgs []imapMessage
var mbox *imap.MailboxStatus
@ -49,7 +52,12 @@ func handleGetMailbox(ectx echo.Context) error {
if mailboxes, err = listMailboxes(c); err != nil {
return err
}
if msgs, err = listMessages(c, mboxName, page); err != nil {
if query != "" {
msgs, err = searchMessages(c, mboxName, query)
} else {
msgs, err = listMessages(c, mboxName, page)
}
if err != nil {
return err
}
mbox = c.Mailbox()
@ -60,11 +68,14 @@ func handleGetMailbox(ectx echo.Context) error {
}
prevPage, nextPage := -1, -1
if page > 0 {
prevPage = page - 1
}
if (page+1)*messagesPerPage < int(mbox.Messages) {
nextPage = page + 1
if query == "" {
// TODO: paging for search
if page > 0 {
prevPage = page - 1
}
if (page+1)*messagesPerPage < int(mbox.Messages) {
nextPage = page + 1
}
}
return ctx.Render(http.StatusOK, "mailbox.html", &MailboxRenderData{
@ -74,6 +85,7 @@ func handleGetMailbox(ectx echo.Context) error {
Messages: msgs,
PrevPage: prevPage,
NextPage: nextPage,
Query: query,
})
}

View file

@ -189,7 +189,7 @@ func listMessages(conn *imapclient.Client, mboxName string, page int) ([]imapMes
return nil, nil
}
seqSet := new(imap.SeqSet)
var seqSet imap.SeqSet
seqSet.AddRange(uint32(from), uint32(to))
fetch := []imap.FetchItem{imap.FetchEnvelope, imap.FetchUid, imap.FetchBodyStructure}
@ -197,7 +197,7 @@ func listMessages(conn *imapclient.Client, mboxName string, page int) ([]imapMes
ch := make(chan *imap.Message, 10)
done := make(chan error, 1)
go func() {
done <- conn.Fetch(seqSet, fetch, ch)
done <- conn.Fetch(&seqSet, fetch, ch)
}()
msgs := make([]imapMessage, 0, to-from)
@ -218,6 +218,53 @@ func listMessages(conn *imapclient.Client, mboxName string, page int) ([]imapMes
return msgs, nil
}
func searchMessages(conn *imapclient.Client, mboxName, query string) ([]imapMessage, error) {
if err := ensureMailboxSelected(conn, mboxName); err != nil {
return nil, err
}
criteria := imap.SearchCriteria{Text: []string{query}}
nums, err := conn.Search(&criteria)
if err != nil {
return nil, fmt.Errorf("UID SEARCH failed: %v", err)
}
if len(nums) == 0 {
return nil, nil
}
indexes := make(map[uint32]int)
for i, num := range nums {
indexes[num] = i
}
// TODO: paging
var seqSet imap.SeqSet
seqSet.AddNum(nums...)
fetch := []imap.FetchItem{imap.FetchEnvelope, imap.FetchUid, imap.FetchBodyStructure}
ch := make(chan *imap.Message, 10)
done := make(chan error, 1)
go func() {
done <- conn.Fetch(&seqSet, fetch, ch)
}()
msgs := make([]imapMessage, len(nums))
for msg := range ch {
i, ok := indexes[msg.SeqNum]
if !ok {
continue
}
msgs[i] = imapMessage{msg}
}
if err := <-done; err != nil {
return nil, fmt.Errorf("failed to fetch message list: %v", err)
}
return msgs, nil
}
func getMessagePart(conn *imapclient.Client, mboxName string, uid uint32, partPath []int) (*imapMessage, *message.Entity, error) {
if err := ensureMailboxSelected(conn, mboxName); err != nil {
return nil, nil, err

View file

@ -23,6 +23,7 @@ func init() {
})
p.GET("/mailbox/:mbox", handleGetMailbox)
p.POST("/mailbox/:mbox", handleGetMailbox)
p.GET("/message/:mbox/:uid", func(ectx echo.Context) error {
ctx := ectx.(*koushin.Context)

View file

@ -2,7 +2,7 @@
<h1>koushin</h1>
<form method="post" action="/login">
<form method="post" action="">
<label for="username">Username:</label>
<input type="text" name="username" id="username"/>
<br><br>

View file

@ -8,6 +8,11 @@
<h2>{{.Mailbox.Name}}</h2>
<form method="post" action="">
<input type="search" name="query" value="{{.Query}}">
<input type="submit" value="Search">
</form>
<p>Mailboxes:</p>
<ul>
{{range .Mailboxes}}