| 1 |
|
| 2 |
* GoingsOn - Emails Render |
| 3 |
* Row markup for the email/thread list. Extracted from emails.js so the |
| 4 |
* controller stays focused on data flow. Consumes the shared selection manager |
| 5 |
* via GoingsOn.emails.selection (resolved lazily at render time). |
| 6 |
|
| 7 |
|
| 8 |
(function() { |
| 9 |
'use strict'; |
| 10 |
|
| 11 |
const esc = GoingsOn.utils.escapeHtml; |
| 12 |
const escAttr = GoingsOn.utils.escapeAttrValue; |
| 13 |
const escArg = GoingsOn.utils.escapeHandlerArg; |
| 14 |
|
| 15 |
|
| 16 |
* Render one thread row for the virtual scroller. |
| 17 |
* @param {Object} thread - Thread with mostRecentEmail + threadCount/hasUnread |
| 18 |
* @param {number} index - Row index (unused; kept for scroller signature parity) |
| 19 |
* @returns {string} HTML string |
| 20 |
|
| 21 |
function renderEmailItem(thread, index) { |
| 22 |
const e = thread.mostRecentEmail; |
| 23 |
|
| 24 |
const isSnoozed = e.isSnoozed; |
| 25 |
const isSelected = GoingsOn.emails.selection.isSelected(e.id); |
| 26 |
const threadBadge = thread.threadCount > 1 |
| 27 |
? `<span class="thread-badge" title="${thread.threadCount} messages in thread">${thread.threadCount}</span>` |
| 28 |
: ''; |
| 29 |
const labelBadges = (e.labels || []).map(l => |
| 30 |
`<span class="badge badge--xs badge--filled" data-color="blue">${esc(l)}</span>` |
| 31 |
).join(''); |
| 32 |
|
| 33 |
return ` |
| 34 |
<div class="email-item email-item-with-checkbox ${thread.hasUnread ? 'unread' : ''} ${isSnoozed ? 'email-snoozed' : ''}" |
| 35 |
data-id="${escAttr(e.id)}" |
| 36 |
data-contextmenu="contextMenus.showEmail" data-a1="@event" data-a2="${escAttr(e.id)}" |
| 37 |
data-email-archived="${e.isArchived}" data-email-read="${e.isRead}" |
| 38 |
data-email-snoozed="${isSnoozed}" |
| 39 |
tabindex="0" role="listitem" aria-label="Email from ${esc(e.from)}"> |
| 40 |
<div class="email-checkbox-cell" data-act="ui.noop"> |
| 41 |
<input type="checkbox" class="bulk-checkbox" data-id="${escAttr(e.id)}" |
| 42 |
${isSelected ? 'checked' : ''} |
| 43 |
data-change="emails.toggleSelection" data-a1="${escAttr(e.id)}" data-a2="@el" data-a3="@event" |
| 44 |
aria-label="Select email"> |
| 45 |
</div> |
| 46 |
<div class="email-content" data-act="emails.open" data-a1="${escAttr(e.id)}" role="button"> |
| 47 |
<div class="email-header"> |
| 48 |
<span class="email-from">${esc(e.from)}</span> |
| 49 |
${threadBadge} |
| 50 |
${isSnoozed ? `<span class="snooze-badge" title="Snoozed until ${escAttr(e.snoozedUntilFormatted || '')}" aria-label="Snoozed until ${escAttr(e.snoozedUntilFormatted || '')}">Snoozed</span>` : ''} |
| 51 |
<span class="email-date">${e.receivedFormatted}</span> |
| 52 |
</div> |
| 53 |
<div class="email-subject">${esc(e.subject)}${labelBadges ? ' ' + labelBadges : ''}</div> |
| 54 |
<div class="email-preview">${esc(e.bodyPreview)}...</div> |
| 55 |
</div> |
| 56 |
<button class="btn-icon kebab-btn" data-act="contextMenus.showEmail" data-a1="@event" data-a2="${escAttr(e.id)}" title="Actions" aria-label="Email actions">⋮</button> |
| 57 |
</div> |
| 58 |
`; |
| 59 |
} |
| 60 |
|
| 61 |
GoingsOn.emailsRender = { renderEmailItem }; |
| 62 |
|
| 63 |
})(); |
| 64 |
|