From f59ca1976a1075e9e8fdf1e5fcdb7cfc853493b8 Mon Sep 17 00:00:00 2001 From: Bobby <30593201+luciferreeves@users.noreply.github.com> Date: Mon, 29 Dec 2025 10:46:00 +0530 Subject: feat: Enhance email viewer with new UI actions, sender profile pictures, and raw header display. --- static/css/main.css | 685 +++++++++++++++++++++++++++++++++++++++++++++++--- static/js/dropdown.js | 18 +- static/js/icons.js | 24 ++ static/js/mail.js | 390 +++++++++++++++++----------- static/js/shadow.js | 50 ++-- static/js/utils.js | 113 +++++++++ 6 files changed, 1064 insertions(+), 216 deletions(-) create mode 100644 static/js/icons.js create mode 100644 static/js/utils.js (limited to 'static') diff --git a/static/css/main.css b/static/css/main.css index de658ba..4e26802 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -716,41 +716,303 @@ input[type="date"]:focus { .preview { background: var(--bg-primary); overflow-y: auto; - padding: 12px; + padding: 16px; } .email-header { + background: var(--bg-secondary); + border: 2px solid var(--accent-primary); + margin-bottom: 0; +} + +.email-header-main { display: flex; - justify-content: space-between; - margin-bottom: 12px; - padding-bottom: 8px; - border-bottom: 1px solid var(--border-color); + padding: 16px; + gap: 16px; + border-bottom: 1px solid var(--border-accent); } -.email-actions { +.email-profile-section { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; +} + +.profile-initials { + font-size: 24px; + font-weight: bold; + color: var(--accent-primary); + font-family: 'MS PGothic', monospace; +} + +.profile-domain { + font-size: 9px; + color: var(--accent-tertiary); + font-family: 'MS PGothic', monospace; + text-transform: uppercase; +} + +.email-info-section { + flex: 1; + min-width: 0; +} + +.email-meta-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 6px 12px; +} + +.email-meta-label { + color: var(--accent-tertiary); + font-weight: bold; + min-width: 40px; +} + +.email-iframe { + width: 100%; + border: none; + display: block; + background: #ffffff; + min-height: 400px; +} + +.email-meta-item { display: flex; + gap: 6px; + font-size: 10px; + font-family: 'MS PGothic', monospace; + align-items: flex-start; +} + +.email-meta-full .email-meta-value { + white-space: normal; + word-wrap: break-word; +} + +.email-meta-value { + color: var(--text-primary); + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: flex; + align-items: center; gap: 4px; } -.btn-icon { +.email-meta-value span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; + min-width: 0; +} + +.add-contact-btn { + background: none; + border: 1px solid var(--border-color); + color: var(--text-muted); + cursor: pointer; + padding: 2px 4px; + font-size: 10px; + flex-shrink: 0; +} + +.add-contact-btn:hover { + border-color: var(--accent-primary); + color: var(--accent-primary); +} + +.action-section { + display: flex; + border-right: 1px solid var(--border-accent); +} + +.action-section:last-child { + border-right: none; +} + + + +.email-body-container { + background: var(--bg-secondary); + border: 2px solid var(--accent-primary); + border-top: none; + padding: 0; +} + + + +.attachment { + display: inline-block; + margin: 3px 4px 3px 0; + padding: 4px 8px; background: var(--bg-secondary); + color: var(--accent-primary); + font-size: 10px; border: 1px solid var(--border-color); +} + +.attachment:hover { + border-color: var(--accent-primary); +} + +.email-summary { + font-size: 11px; color: var(--text-secondary); - font-size: 12px; - padding: 5px; + margin-top: 8px; +} + +.sender-name { + color: var(--accent-primary); cursor: pointer; - width: 26px; - height: 26px; + text-decoration: underline; +} + +.sender-name:hover { + color: var(--accent-secondary); +} + +.email-details-container { + margin-top: 12px; +} + +.email-detail-label { + font-weight: bold; + color: var(--text-secondary); + min-width: 60px; +} + +.email-profile { + width: 64px; + height: 64px; + border: 2px solid var(--accent-primary); + background: #ffffff; display: flex; align-items: center; justify-content: center; + padding: 4px; } -.btn-icon:hover { - border-color: var(--accent-primary); +.profile-img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.email-meta-full { + grid-column: 1 / -1; +} + +.email-body { + padding: 12px; + overflow-x: auto; +} + +.email-body pre { + margin: 0; + font-family: 'MS PGothic', monospace; + font-size: 11px; + line-height: 1.4; + color: var(--text-primary); + white-space: pre; + overflow-x: auto; +} + +.email-body pre.word-wrap-enabled { + white-space: pre-wrap; + word-wrap: break-word; + overflow-wrap: break-word; + overflow-x: hidden; +} + +.email-body pre a { color: var(--accent-primary); + text-decoration: underline; +} + +.email-body pre a:hover { + color: var(--accent-secondary); +} + +.no-email-selected { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + color: var(--text-muted); + font-size: 13px; +} + +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.8); + display: flex; + align-items: center; + justify-content: center; + z-index: 10000; +} + +.modal-dialog { + background: var(--bg-secondary); + border: 2px solid var(--accent-primary); + padding: 20px; + max-width: 700px; + width: 90%; + max-height: 80vh; + overflow: auto; +} + +.modal-title { + color: var(--accent-primary); + margin-bottom: 12px; + font-size: 13px; + font-weight: bold; +} + +.modal-content { + background: var(--bg-primary); + padding: 12px; + border: 1px solid var(--border-color); + color: var(--text-primary); + font-size: 10px; + font-family: 'MS PGothic', monospace; + margin-bottom: 12px; + white-space: pre-wrap; + word-wrap: break-word; + max-height: 500px; + overflow: auto; +} + +.btn-close { + padding: 8px 16px; + background: var(--accent-primary); + color: var(--bg-primary); + border: 1px solid var(--accent-primary); + cursor: pointer; + font-size: 11px; + font-family: inherit; +} + +.btn-close:hover { + background: var(--accent-secondary); + border-color: var(--accent-secondary); } +.email-actions { + display: flex; + gap: 4px; +} + + + .email-sender { display: flex; justify-content: space-between; @@ -774,43 +1036,404 @@ input[type="date"]:focus { color: var(--text-secondary); } -.email-attachments { - margin-bottom: 8px; - padding: 8px; - background: var(--bg-tertiary); +.email-top-row { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; +} + +.email-subject-display { + font-size: 16px; + font-weight: bold; + color: var(--accent-secondary); + flex: 1; +} + +.email-actions-bar { + display: flex; + gap: 1px; + flex-shrink: 0; +} + +.action-btn { + background: var(--bg-secondary); border: 1px solid var(--border-color); + color: var(--text-primary); + cursor: pointer; + padding: 2px 4px; + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; +} + +.action-btn:hover { + background: var(--accent-primary); + border-color: var(--accent-primary); +} + +.action-btn.active { + background: var(--accent-secondary); + border-color: var(--accent-secondary); +} + +.action-btn svg { + width: 14px; + height: 14px; +} + +.email-summary { font-size: 11px; + color: var(--text-secondary); + margin-top: 8px; } -.attachment { - display: inline-block; - margin: 3px 4px; - padding: 4px 8px; - background: var(--bg-secondary); +.sender-name { color: var(--accent-primary); + cursor: pointer; + text-decoration: underline; +} + +.sender-name:hover { + color: var(--accent-secondary); +} + +.email-detail-item { + display: flex; + gap: 8px; font-size: 10px; + font-family: 'MS PGothic', monospace; + margin-bottom: 4px; +} + +.email-detail-value { + color: var(--text-primary); + word-wrap: break-word; +} + +.sender-menu { + position: fixed; + background: var(--bg-primary); border: 1px solid var(--border-color); + box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.3); + z-index: 1000; + min-width: 180px; } -.attachment:hover { - border-color: var(--accent-primary); +.sender-menu-item { + padding: 6px 10px; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + font-size: 11px; + font-family: 'MS PGothic', monospace; } -.email-body { - line-height: 1.6; - padding: 10px; +.sender-menu-item:hover { + background: var(--accent-primary); + color: var(--bg-primary); +} + +.sender-menu-item svg { + flex-shrink: 0; +} + +/* Email Header Redesign */ +.email-header { + border-bottom: 1px solid var(--border-color); + padding: 12px; + background: var(--bg-primary); +} + +.email-header-main { + display: flex; + gap: 16px; +} + +.email-profile-section { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; +} + +.email-profile { + width: 64px; + height: 64px; + border: 2px solid var(--border-color); + display: flex; + align-items: center; + justify-content: center; background: var(--bg-secondary); - border: 1px solid var(--border-color); +} + +.profile-img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.profile-initials { + font-size: 24px; + font-weight: bold; + font-family: 'MS PGothic', monospace; + color: var(--text-primary); +} + +.profile-domain { + font-size: 9px; + color: var(--text-secondary); + font-family: 'MS PGothic', monospace; + text-align: center; + width: 68px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.email-content-section { + flex: 1; + min-width: 0; +} + + + +.email-subject-display { + font-size: 14px; + font-weight: bold; + color: var(--accent-secondary); + font-family: 'MS PGothic', monospace; + margin-bottom: 6px; + word-wrap: break-word; +} + +.email-summary { + font-size: 10px; + color: var(--text-secondary); + font-family: 'MS PGothic', monospace; +} + +.sender-name { + color: var(--accent-primary); + cursor: pointer; + text-decoration: underline; +} + +.sender-name:hover { + color: var(--accent-secondary); +} + +.email-details-container { + margin-top: 8px; +} + +.email-detail-item { + display: flex; + gap: 6px; + font-size: 10px; + font-family: 'MS PGothic', monospace; + margin-bottom: 3px; +} + +.email-detail-label { + font-weight: bold; + color: var(--text-secondary); + min-width: 50px; +} + +.email-detail-value { + color: var(--text-primary); + word-wrap: break-word; +} + +.sender-menu { + position: fixed; + background: var(--bg-primary); + border: 2px solid var(--border-color); + z-index: 1000; + min-width: 160px; +} + +.sender-menu-item { + padding: 6px 8px; + cursor: pointer; + display: flex; + align-items: center; + gap: 6px; + font-size: 10px; + font-family: 'MS PGothic', monospace; + border-bottom: 1px solid var(--border-color); +} + +.sender-menu-item:last-child { + border-bottom: none; +} + +.sender-menu-item:hover { + background: var(--accent-primary); + color: var(--bg-primary); +} + +.sender-menu-item svg { + flex-shrink: 0; +} + +.email-card-header { + background: var(--bg-primary); +} + +.email-card { + background: var(--bg-secondary); + border: 2px solid var(--accent-primary); + padding: 0; +} + +.card-field { + display: grid; + grid-template-columns: 80px 1fr; + border-bottom: 2px solid var(--border-color); + padding: 10px 12px; + align-items: center; +} + +.card-field-large { + background: var(--bg-tertiary); +} + +.card-field:last-of-type { + border-bottom: none; +} + +.field-label { + font-size: 9px; + font-weight: bold; + color: var(--accent-tertiary); + font-family: 'MS PGothic', monospace; + letter-spacing: 1px; +} + +.field-value { font-size: 12px; + color: var(--text-primary); + font-family: 'MS PGothic', monospace; } -.no-email-selected { +.card-field-with-pic { + grid-template-columns: 80px 1fr; +} + +.field-value-with-pic { + display: flex; + gap: 12px; + align-items: center; +} + +.card-pic { + width: 48px; + height: 48px; + border: 2px solid var(--border-color); + object-fit: cover; +} + +.card-pic-init { + width: 48px; + height: 48px; + border: 2px solid var(--border-color); + background: var(--bg-tertiary); display: flex; align-items: center; justify-content: center; - height: 100%; - color: var(--text-muted); + font-size: 20px; + font-weight: bold; +} + +.sender-details { + flex: 1; +} + +.sender-name-card { font-size: 13px; + font-weight: bold; + color: var(--accent-primary); + cursor: pointer; + text-decoration: underline; + margin-bottom: 2px; +} + +.sender-name-card:hover { + color: var(--accent-secondary); +} + +.sender-email-card { + font-size: 10px; + color: var(--text-secondary); +} + + + +.email-card-header { + position: relative; + padding-bottom: 20px; +} + +.header-top-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 12px; + gap: 8px; + border-bottom: 2px solid var(--border-color); +} + +.subject-container { + flex: 1; + display: grid; + grid-template-columns: 80px 1fr; + align-items: center; +} + +.card-actions-grid { + display: flex; + flex-direction: row; + align-items: center; + gap: 4px; + /* Removed absolute positioning */ +} + +.card-action-btn { + background: transparent; + border: none; + padding: 6px; + cursor: pointer; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 2px; + border-radius: 4px; +} + +.card-action-btn:hover { + background: var(--bg-tertiary); +} + +.card-action-btn.active { + background: var(--accent-primary); +} + +.card-action-btn svg { + width: 20px; + height: 20px; +} + + + +.subject-text { + font-weight: bold; + color: var(--accent-secondary); } .login-page { diff --git a/static/js/dropdown.js b/static/js/dropdown.js index 00b7ee6..80efd19 100644 --- a/static/js/dropdown.js +++ b/static/js/dropdown.js @@ -1,11 +1,12 @@ document.addEventListener('DOMContentLoaded', function () { - // Handle dropdown clicks - document.querySelectorAll('.options-subitem > a').forEach(function (item) { - item.addEventListener('click', function (e) { + document.addEventListener('click', function (e) { + const toggleLink = e.target.closest('a'); + + if (toggleLink && toggleLink.parentElement && toggleLink.parentElement.classList.contains('options-subitem')) { e.preventDefault(); - const parent = this.parentElement; + const parent = toggleLink.parentElement; - if (parent.classList.contains('disabled')) { + if (parent.classList.contains('disabled') || parent.getAttribute('disabled') !== null) { return; } @@ -16,11 +17,10 @@ document.addEventListener('DOMContentLoaded', function () { }); parent.classList.toggle('open'); - }); - }); + return; + } - // Close dropdowns when clicking outside - document.addEventListener('click', function (e) { + // Handle clicking outside if (!e.target.closest('.options-subitem')) { document.querySelectorAll('.options-subitem.open').forEach(function (item) { item.classList.remove('open'); diff --git a/static/js/icons.js b/static/js/icons.js new file mode 100644 index 0000000..d445ed0 --- /dev/null +++ b/static/js/icons.js @@ -0,0 +1,24 @@ +const Icons = { + Html: ``, + + Plain: ``, + + Reply: ``, + + Forward: ``, + + Details: ``, + + Wrap: ``, + + Headers: ``, + + Summary: ``, + + Window: ``, + + addContact: '', + + composeMail: '' + +}; \ No newline at end of file diff --git a/static/js/mail.js b/static/js/mail.js index fd02120..1de18ca 100644 --- a/static/js/mail.js +++ b/static/js/mail.js @@ -5,8 +5,11 @@ document.addEventListener('DOMContentLoaded', function () { let currentEmailId = null; let markAsReadTimer = null; + let currentEmail = null; + let viewMode = 'html'; + let wordWrap = true; + let showDetails = false; - // Parse preferences from data attributes const prefs = { MarkMessagesAsRead: prefsData ? prefsData.dataset.markAsRead : 'Immediately', ShowEmailAddressWithDisplayName: prefsData ? prefsData.dataset.showAddress === 'true' : true, @@ -30,7 +33,6 @@ document.addEventListener('DOMContentLoaded', function () { currentEmailId = emailId; - // Clear previous timer if (markAsReadTimer) { clearTimeout(markAsReadTimer); } @@ -40,9 +42,15 @@ document.addEventListener('DOMContentLoaded', function () { if (!response.ok) throw new Error('Failed to fetch email'); const email = await response.json(); + currentEmail = email; + showDetails = false; renderEmail(email); - // Handle mark as read based on preference + document.querySelectorAll('.subnav .nav-subitem').forEach(item => { + item.removeAttribute('disabled'); + item.classList.remove('disabled'); + }); + if (!email.IsRead) { handleMarkAsRead(emailId, this); } @@ -53,7 +61,6 @@ document.addEventListener('DOMContentLoaded', function () { }); }); - // Handle flag clicks document.querySelectorAll('.email-flag').forEach(flag => { flag.addEventListener('click', async function (e) { e.stopPropagation(); @@ -97,9 +104,7 @@ document.addEventListener('DOMContentLoaded', function () { const delay = delays[markOption]; - if (delay === null) { - return; // Never mark as read - } + if (delay === null) return; markAsReadTimer = setTimeout(async () => { try { @@ -116,182 +121,285 @@ document.addEventListener('DOMContentLoaded', function () { }, delay); } - function renderEmail(email) { + async function renderEmail(email) { preview.innerHTML = ''; - // Header - const header = createHeader(email); + const header = await createHeader(email); preview.appendChild(header); - // Sender info - const sender = createSenderInfo(email); - preview.appendChild(sender); - - // Recipients - const recipients = createRecipients(email); - preview.appendChild(recipients); - - // Attachments - if (email.Attachments && email.Attachments.length > 0) { - const attachments = createAttachments(email.Attachments); - preview.appendChild(attachments); - } - - // Body const body = createBody(email); preview.appendChild(body); } - function createHeader(email) { + async function createHeader(email) { const header = document.createElement('div'); - header.className = 'email-header'; + header.className = 'email-card-header'; + + // Card container + const card = document.createElement('div'); + card.className = 'email-card'; + + // Header Top Row (Subject + Actions) + const headerRow = document.createElement('div'); + headerRow.className = 'header-top-row'; + + // Subject container + const subjectContainer = document.createElement('div'); + subjectContainer.className = 'subject-container'; + subjectContainer.innerHTML = ` +