update list of emails from/to preview

This commit is contained in:
2026-08-30 08:54:10 +01:00
parent 77f4b04af6
commit 69d82f1c96
4 changed files with 50 additions and 29 deletions
+12 -10
View File
@@ -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] + "…"
+18 -16
View File
@@ -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
+2
View File
@@ -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}
+18 -3
View File
@@ -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 `
<div class="message-item ${m.id===S.selectedMessageId&&!SEL.ids.size?'active':''} ${!m.is_read?'unread':''} ${SEL.ids.has(m.id)?'selected':''}"
data-id="${m.id}" data-idx="${i}"
draggable="true"
@@ -1359,7 +1366,8 @@ function renderMessageList() {
<div class="msg-top">
<span class="msg-unread-dot" title="${m.is_read?'':'Unread'}"></span>
<span class="msg-dot" style="background:${m.account_color}" title="${esc(m.account_email||'')}"></span>
<span class="msg-from">${esc(m.from_name||m.from_email)}</span>
<span class="msg-account-name" title="${esc(m.account_email||'')}">${esc(acctLabel)}</span>
<span class="msg-from" title="${esc(fromLine)}">${esc(fromLine)}</span>
<span class="msg-date">${formatDate(m.date)}</span>
</div>
<div class="msg-line2">
@@ -1373,7 +1381,8 @@ function renderMessageList() {
<span class="msg-star ${m.is_starred?'on':''}" onclick="toggleStar(${m.id},event)">${m.is_starred?'★':'☆'}</span>
</span>
</div>
</div>`).join('')+(S.messages.length<S.totalMessages
</div>`;
}).join('')+(S.messages.length<S.totalMessages
?`<div class="load-more"><button class="load-more-btn" onclick="loadMoreMessages()">Load more</button></div>`:'');
// 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: <recipient>" 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);