From 69d82f1c9633ed1be6db3830f98fa1d5024b64e8 Mon Sep 17 00:00:00 2001 From: nahakubuilder Date: Sun, 30 Aug 2026 08:54:10 +0100 Subject: [PATCH] update list of emails from/to preview --- internal/db/db.go | 22 ++++++++++++---------- internal/models/models.go | 34 ++++++++++++++++++---------------- web/static/css/gowebmail.css | 2 ++ web/static/js/app.js | 21 ++++++++++++++++++--- 4 files changed, 50 insertions(+), 29 deletions(-) diff --git a/internal/db/db.go b/internal/db/db.go index ce49922..7908088 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -1741,8 +1741,8 @@ func (d *DB) ListMessages(userID int64, folderIDs []int64, accountID int64, page args = append(args, pageSize, offset) rows, err := d.sql.Query(` - SELECT m.id, m.account_id, a.email_address, a.color, m.folder_id, f.name, - m.subject, m.from_name, m.from_email, m.body_text, + SELECT m.id, m.account_id, a.email_address, a.display_name, a.color, m.folder_id, f.name, + m.subject, m.from_name, m.from_email, m.to_list, m.body_text, m.date, m.is_read, m.is_starred, m.has_attachment FROM messages m JOIN email_accounts a ON a.id = m.account_id @@ -1759,10 +1759,10 @@ func (d *DB) ListMessages(userID int64, folderIDs []int64, accountID int64, page var summaries []models.MessageSummary for rows.Next() { s := models.MessageSummary{} - var subjectEnc, fromNameEnc, fromEmailEnc, bodyTextEnc string + var subjectEnc, fromNameEnc, fromEmailEnc, toListEnc, bodyTextEnc string if err := rows.Scan( - &s.ID, &s.AccountID, &s.AccountEmail, &s.AccountColor, &s.FolderID, &s.FolderName, - &subjectEnc, &fromNameEnc, &fromEmailEnc, &bodyTextEnc, + &s.ID, &s.AccountID, &s.AccountEmail, &s.AccountName, &s.AccountColor, &s.FolderID, &s.FolderName, + &subjectEnc, &fromNameEnc, &fromEmailEnc, &toListEnc, &bodyTextEnc, &s.Date, &s.IsRead, &s.IsStarred, &s.HasAttachment, ); err != nil { return nil, err @@ -1770,6 +1770,7 @@ func (d *DB) ListMessages(userID int64, folderIDs []int64, accountID int64, page s.Subject, _ = d.enc.Decrypt(subjectEnc) s.FromName, _ = d.enc.Decrypt(fromNameEnc) s.FromEmail, _ = d.enc.Decrypt(fromEmailEnc) + s.ToList, _ = d.enc.Decrypt(toListEnc) bodyText, _ := d.enc.Decrypt(bodyTextEnc) if len(bodyText) > 120 { bodyText = bodyText[:120] + "…" @@ -1874,8 +1875,8 @@ func (d *DB) SearchMessages(userID int64, q string, filters SearchFilters, page, qArgs := append(append([]interface{}{}, args...), pageSize, offset) rows, err := d.sql.Query(` - SELECT m.id, m.account_id, a.email_address, a.color, m.folder_id, f.name, - m.subject, m.from_name, m.from_email, m.body_text, + SELECT m.id, m.account_id, a.email_address, a.display_name, a.color, m.folder_id, f.name, + m.subject, m.from_name, m.from_email, m.to_list, m.body_text, m.date, m.is_read, m.is_starred, m.has_attachment, `+approxSizeExpr+` FROM messages m JOIN email_accounts a ON a.id=m.account_id @@ -1891,10 +1892,10 @@ func (d *DB) SearchMessages(userID int64, q string, filters SearchFilters, page, var summaries []models.MessageSummary for rows.Next() { s := models.MessageSummary{} - var subjectEnc, fromNameEnc, fromEmailEnc, bodyTextEnc string + var subjectEnc, fromNameEnc, fromEmailEnc, toListEnc, bodyTextEnc string if err := rows.Scan( - &s.ID, &s.AccountID, &s.AccountEmail, &s.AccountColor, &s.FolderID, &s.FolderName, - &subjectEnc, &fromNameEnc, &fromEmailEnc, &bodyTextEnc, + &s.ID, &s.AccountID, &s.AccountEmail, &s.AccountName, &s.AccountColor, &s.FolderID, &s.FolderName, + &subjectEnc, &fromNameEnc, &fromEmailEnc, &toListEnc, &bodyTextEnc, &s.Date, &s.IsRead, &s.IsStarred, &s.HasAttachment, &s.Size, ); err != nil { return nil, err @@ -1902,6 +1903,7 @@ func (d *DB) SearchMessages(userID int64, q string, filters SearchFilters, page, s.Subject, _ = d.enc.Decrypt(subjectEnc) s.FromName, _ = d.enc.Decrypt(fromNameEnc) s.FromEmail, _ = d.enc.Decrypt(fromEmailEnc) + s.ToList, _ = d.enc.Decrypt(toListEnc) bodyText, _ := d.enc.Decrypt(bodyTextEnc) if len(bodyText) > 120 { bodyText = bodyText[:120] + "…" diff --git a/internal/models/models.go b/internal/models/models.go index b8e9e3f..a006d59 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -205,23 +205,25 @@ type Message struct { // MessageSummary is a lightweight version for list views. type MessageSummary struct { - ID int64 `json:"id"` - AccountID int64 `json:"account_id"` - AccountEmail string `json:"account_email"` - AccountColor string `json:"account_color"` - FolderID int64 `json:"folder_id"` - FolderName string `json:"folder_name"` - Subject string `json:"subject"` - FromName string `json:"from_name"` - FromEmail string `json:"from_email"` - Preview string `json:"preview"` // first ~100 chars of body - Date time.Time `json:"date"` - IsRead bool `json:"is_read"` - IsStarred bool `json:"is_starred"` - HasAttachment bool `json:"has_attachment"` + ID int64 `json:"id"` + AccountID int64 `json:"account_id"` + AccountEmail string `json:"account_email"` + AccountName string `json:"account_name"` // account's own display_name (may be blank) + AccountColor string `json:"account_color"` + FolderID int64 `json:"folder_id"` + FolderName string `json:"folder_name"` + Subject string `json:"subject"` + FromName string `json:"from_name"` + FromEmail string `json:"from_email"` + ToList string `json:"to_list"` // comma-separated; only shown in the Sent folder view + Preview string `json:"preview"` // first ~100 chars of body + Date time.Time `json:"date"` + IsRead bool `json:"is_read"` + IsStarred bool `json:"is_starred"` + HasAttachment bool `json:"has_attachment"` SnoozedUntil *time.Time `json:"snoozed_until,omitempty"` - Size int64 `json:"size,omitempty"` // approximate; only populated by search results - Labels []Label `json:"labels,omitempty"` + Size int64 `json:"size,omitempty"` // approximate; only populated by search results + Labels []Label `json:"labels,omitempty"` } // ScheduledSend is a fully-composed message held until SendAt, delivered by the background diff --git a/web/static/css/gowebmail.css b/web/static/css/gowebmail.css index 9d383d4..6090b93 100644 --- a/web/static/css/gowebmail.css +++ b/web/static/css/gowebmail.css @@ -279,6 +279,8 @@ body.app-page{overflow:hidden} .msg-thread-count{color:var(--muted);font-size:11px;font-weight:600} .msg-preview{color:var(--muted)} .msg-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0} +.msg-account-name{font-size:10px;color:var(--muted);flex-shrink:0;max-width:110px;overflow:hidden; + text-overflow:ellipsis;white-space:nowrap;background:var(--surface3);padding:1px 6px;border-radius:4px} .msg-icons{display:flex;align-items:center;gap:4px;flex-shrink:0} .msg-size{font-size:10px;color:var(--muted)} .msg-star{color:var(--muted);font-size:15px;cursor:pointer} diff --git a/web/static/js/app.js b/web/static/js/app.js index 764c647..d0b4afb 100644 --- a/web/static/js/app.js +++ b/web/static/js/app.js @@ -1349,7 +1349,14 @@ function renderMessageList() { // Update bulk action bar updateBulkBar(); - list.innerHTML=msgs.map((m,i)=>` + const sentView = isSentFolderView(); + + list.innerHTML=msgs.map((m,i)=>{ + const acctLabel = m.account_name || m.account_email || ''; + const fromLine = sentView + ? `To: ${m.to_list || '(no recipient)'}` + : (m.from_name ? `${m.from_name} - ${m.from_email}` : (m.from_email||'')); + return `
- ${esc(m.from_name||m.from_email)} + + ${esc(fromLine)} ${formatDate(m.date)}
@@ -1373,7 +1381,8 @@ function renderMessageList() { ${m.is_starred?'★':'☆'}
- `).join('')+(S.messages.length`; + }).join('')+(S.messages.length`:''); // Enable drag-drop onto folder nav items @@ -1509,6 +1518,12 @@ async function openMessage(id) { function isDraftFolder(folderId) { return S.folders?.find(f=>f.id===folderId)?.folder_type==='drafts'; } +// Whether the currently open folder is a Sent folder — used by renderMessageList to show +// "To: " instead of the from-line, since a Sent row's "from" is always just your +// own account and tells you nothing about who the message actually went to. +function isSentFolderView() { + return S.folders?.find(f=>f.id===S.currentFolder)?.folder_type==='sent'; +} function resumeDraft(msg) { const toList=(msg.to||'').split(',').map(s=>s.trim()).filter(Boolean); const ccList=(msg.cc||'').split(',').map(s=>s.trim()).filter(Boolean);