Auto-load selected source mailbox messages

This commit is contained in:
DonVoo 2026-07-13 03:10:40 +02:00
parent 2f337ee200
commit 561450662c

View file

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"html" "html"
"io"
"net/http" "net/http"
"net/mail" "net/mail"
"net/url" "net/url"
@ -306,8 +307,10 @@ func emailBoxHandler(w http.ResponseWriter, r *http.Request) {
switch kind { switch kind {
case "source": case "source":
if r.Header.Get("HX-Request") == "true" { if r.Header.Get("HX-Request") == "true" {
selected := selectedSourceAccount(accounts, r.URL.Query().Get("source"))
w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, renderEmailBoxSourcePane(accounts, r.URL.Query().Get("source"))) fmt.Fprint(w, renderEmailBoxSourcePane(accounts, selected.Name))
fmt.Fprintf(w, `<div id="list" hx-swap-oob="innerHTML">%s</div>`, renderInitialSourceMailboxList(selected))
return return
} }
renderSourceEmailBoxPage(w, r, accounts, r.URL.Query().Get("source")) renderSourceEmailBoxPage(w, r, accounts, r.URL.Query().Get("source"))
@ -847,6 +850,7 @@ func renderTransferPage(w http.ResponseWriter, r *http.Request, mode transferMod
func renderSourceEmailBoxPage(w http.ResponseWriter, r *http.Request, accounts []Account, selected string) { func renderSourceEmailBoxPage(w http.ResponseWriter, r *http.Request, accounts []Account, selected string) {
user := CurrentUser(r) user := CurrentUser(r)
selectedAccount := selectedSourceAccount(accounts, selected)
w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8"> fmt.Fprintf(w, `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
@ -879,12 +883,12 @@ func renderSourceEmailBoxPage(w http.ResponseWriter, r *http.Request, accounts [
<div class="three-pane email-box-view"> <div class="three-pane email-box-view">
<aside class="pane pane-tree" id="source-tree">%s</aside> <aside class="pane pane-tree" id="source-tree">%s</aside>
<div class="pane-resizer" data-resize-index="0"></div> <div class="pane-resizer" data-resize-index="0"></div>
<section class="pane pane-list"><div class="pane-head">Nachrichten</div><div class="ef-empty">Quell-Postfach waehlen.</div></section> <section class="pane pane-list" id="list">%s</section>
<div class="pane-resizer" data-resize-index="1"></div> <div class="pane-resizer" data-resize-index="1"></div>
<section class="pane pane-read"><div class="ef-empty">Nachricht waehlen.</div></section> <section class="pane pane-read"><div class="ef-empty">Nachricht waehlen.</div></section>
</div> </div>
<footer class="statusbar"><span id="mig-status" hx-get="/migrate/status" hx-trigger="load,every 3s" hx-swap="innerHTML">bereit</span></footer></body></html>`, <footer class="statusbar"><span id="mig-status" hx-get="/migrate/status" hx-trigger="load,every 3s" hx-swap="innerHTML">bereit</span></footer></body></html>`,
renderRibbonEmailBoxMenu("source"), renderRibbonTransferMenu(""), renderRibbonActions(false, user), renderEmailBoxSourcePane(accounts, selected)) renderRibbonEmailBoxMenu("source"), renderRibbonTransferMenu(""), renderRibbonActions(false, user), renderEmailBoxSourcePane(accounts, selectedAccount.Name), renderInitialSourceMailboxList(selectedAccount))
} }
func renderTargetEmailBoxPage(w http.ResponseWriter, r *http.Request, accounts []Account) { func renderTargetEmailBoxPage(w http.ResponseWriter, r *http.Request, accounts []Account) {
@ -936,15 +940,7 @@ func renderEmailBoxSourcePane(accounts []Account, selected string) string {
b.WriteString(`<div class="pane-head archive-head"><span>Quell-Postfach</span></div><div class="ef-empty">Keine Quell-Postfaecher eingetragen.</div>`) b.WriteString(`<div class="pane-head archive-head"><span>Quell-Postfach</span></div><div class="ef-empty">Keine Quell-Postfaecher eingetragen.</div>`)
return b.String() return b.String()
} }
selectedAccount := sourceAccounts[0] selectedAccount := selectedSourceAccount(accounts, selected)
if selected != "" {
for _, a := range sourceAccounts {
if a.Name == selected {
selectedAccount = a
break
}
}
}
b.WriteString(`<div class="pane-head archive-head"><span>Quell-Postfach</span><form class="archive-select-form" hx-get="/boxes/source" hx-target="#source-tree" hx-swap="innerHTML" hx-trigger="change from:#source-mailbox-select"><select id="source-mailbox-select" class="archive-select" name="source">`) b.WriteString(`<div class="pane-head archive-head"><span>Quell-Postfach</span><form class="archive-select-form" hx-get="/boxes/source" hx-target="#source-tree" hx-swap="innerHTML" hx-trigger="change from:#source-mailbox-select"><select id="source-mailbox-select" class="archive-select" name="source">`)
for _, a := range sourceAccounts { for _, a := range sourceAccounts {
label := fmt.Sprintf("%s (%s)", a.SrcUser, strings.ToUpper(accountProto(a))) label := fmt.Sprintf("%s (%s)", a.SrcUser, strings.ToUpper(accountProto(a)))
@ -959,6 +955,21 @@ func renderEmailBoxSourcePane(accounts []Account, selected string) string {
return b.String() return b.String()
} }
func selectedSourceAccount(accounts []Account, selected string) Account {
sourceAccounts := accountsBySource(accounts)
if len(sourceAccounts) == 0 {
return Account{}
}
if selected != "" {
for _, a := range sourceAccounts {
if a.Name == selected {
return a
}
}
}
return sourceAccounts[0]
}
func renderSourceFolders(b *strings.Builder, account Account) { func renderSourceFolders(b *strings.Builder, account Account) {
fmt.Fprintf(b, `<div class="tree-node account">%s</div>`, html.EscapeString(account.SrcUser)) fmt.Fprintf(b, `<div class="tree-node account">%s</div>`, html.EscapeString(account.SrcUser))
if accountProto(account) == "pop3" { if accountProto(account) == "pop3" {
@ -1003,7 +1014,48 @@ func fetchSourceFolderMessages(account Account, folder string) ([]RawMessage, er
return src.Fetch(folder) return src.Fetch(folder)
} }
func renderSourceMailboxMessageList(w http.ResponseWriter, account Account, folder string, messages []RawMessage) { func renderInitialSourceMailboxList(account Account) string {
if account.Name == "" {
return `<div class="pane-head">Nachrichten</div><div class="ef-empty">Quell-Postfach waehlen.</div>`
}
folder, err := firstSourceFolder(account)
if err != nil {
return fmt.Sprintf(`<div class="pane-head">Nachrichten</div><div class="ef-empty">%s</div>`, html.EscapeString(err.Error()))
}
messages, err := fetchSourceFolderMessages(account, folder)
if err != nil {
return fmt.Sprintf(`<div class="pane-head">Nachrichten</div><div class="ef-empty">%s</div>`, html.EscapeString(err.Error()))
}
var b strings.Builder
renderSourceMailboxMessageList(&b, account, folder, messages)
return b.String()
}
func firstSourceFolder(account Account) (string, error) {
if accountProto(account) == "pop3" {
return "INBOX", nil
}
src, err := OpenIMAPSource(account)
if err != nil {
return "", err
}
defer src.Close()
folders, err := src.Folders()
if err != nil {
return "", err
}
for _, folder := range folders {
if strings.EqualFold(folder.Name, "INBOX") {
return folder.Name, nil
}
}
if len(folders) == 0 {
return "", fmt.Errorf("Keine Ordner in diesem Quell-Postfach.")
}
return folders[0].Name, nil
}
func renderSourceMailboxMessageList(w io.Writer, account Account, folder string, messages []RawMessage) {
fmt.Fprintf(w, `<div class="pane-head">%s / %s (%d)</div>`, html.EscapeString(account.SrcUser), html.EscapeString(folder), len(messages)) fmt.Fprintf(w, `<div class="pane-head">%s / %s (%d)</div>`, html.EscapeString(account.SrcUser), html.EscapeString(folder), len(messages))
for i, raw := range messages { for i, raw := range messages {
subject := "(ohne Betreff)" subject := "(ohne Betreff)"