Skip to main content

max / goingson

3.1 KB · 64 lines History Blame Raw
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 // Use pre-computed field from backend (avoids JS date calculation)
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">&#x22EE;</button>
57 </div>
58 `;
59 }
60
61 GoingsOn.emailsRender = { renderEmailItem };
62
63 })();
64