Files
gowebmail/web/templates/app.html

313 lines
20 KiB
HTML
Raw Normal View History

2026-03-07 06:20:39 +00:00
{{template "base" .}}
{{define "title"}}GoMail{{end}}
{{define "body_class"}}app-page{{end}}
{{define "body"}}
<div class="app">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<div class="logo">
<div class="logo-icon"><svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg></div>
<span class="logo-text">GoMail</span>
</div>
<button class="compose-btn" onclick="openCompose()">+ Compose</button>
</div>
<div class="nav-section">
<div class="nav-item active" id="nav-unified" onclick="selectFolder('unified','Unified Inbox')">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
Unified Inbox
<span class="unread-badge" id="unread-total" style="display:none"></span>
</div>
<div class="nav-item" id="nav-starred" onclick="selectFolder('starred','Starred')">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>
Starred
</div>
<div id="folders-by-account"></div>
</div>
<div class="sidebar-footer">
<div class="user-info">
<span class="user-name" id="user-display">...</span>
<a href="/admin" id="admin-link" style="display:none;font-size:11px;color:var(--accent);text-decoration:none">Admin</a>
</div>
<div class="footer-actions">
<button class="icon-btn" id="accounts-btn" onclick="toggleAccountsMenu(event)" title="Manage accounts">
<svg viewBox="0 0 24 24"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg>
</button>
2026-03-07 06:20:39 +00:00
<button class="icon-btn" onclick="openSettings()" title="Settings">
<svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
</button>
<button class="icon-btn" onclick="doLogout()" title="Sign out">
<svg viewBox="0 0 24 24"><path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"/></svg>
</button>
</div>
</div>
</aside>
<!-- Message list -->
<div class="message-list-panel">
<div class="panel-header">
<span class="panel-title" id="panel-title">Unified Inbox</span>
2026-03-07 16:49:23 +00:00
<div style="display:flex;align-items:center;gap:6px">
<span class="panel-count" id="panel-count"></span>
<div class="filter-dropdown" id="filter-dropdown">
<button class="filter-dropdown-btn" id="filter-dropdown-btn" title="Filter &amp; sort" onclick="var m=document.getElementById('filter-dropdown-menu');m.style.display=m.style.display==='block'?'none':'block';event.stopPropagation()">
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/></svg>
<span id="filter-label">Filter</span>
</button>
<div class="filter-dropdown-menu" id="filter-dropdown-menu" style="display:none">
<div class="filter-opt" id="fopt-default" onclick="goMailSetFilter('default');event.stopPropagation()">✓ Default order</div>
<div class="filter-sep-line"></div>
<div class="filter-opt" id="fopt-unread" onclick="goMailSetFilter('unread');event.stopPropagation()">○ Unread only</div>
<div class="filter-sep-line"></div>
<div class="filter-opt" id="fopt-date-desc" onclick="goMailSetFilter('date-desc');event.stopPropagation()">○ Newest first</div>
<div class="filter-opt" id="fopt-date-asc" onclick="goMailSetFilter('date-asc');event.stopPropagation()">○ Oldest first</div>
<div class="filter-opt" id="fopt-size-desc" onclick="goMailSetFilter('size-desc');event.stopPropagation()">○ Largest first</div>
</div>
</div>
</div>
2026-03-07 06:20:39 +00:00
</div>
<div class="search-bar">
<div class="search-wrap">
<svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
<input class="search-input" type="text" id="search-input" placeholder="Search emails..." oninput="handleSearch(this.value)">
</div>
</div>
<div class="message-list" id="message-list">
<div class="spinner" style="margin-top:60px"></div>
</div>
</div>
<!-- Message detail -->
<main class="message-detail" id="message-detail">
<div class="no-message">
<svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
<h3>Select a message</h3>
<p>Choose a message from the list to read it</p>
</div>
</main>
</div>
<!-- ── Accounts submenu popup ──────────────────────────────────────────────── -->
<div class="accounts-popup" id="accounts-popup">
<div class="accounts-popup-inner">
<div class="accounts-popup-header">
<span>Accounts</span>
<button class="icon-btn" onclick="closeAccountsMenu()" style="margin:-4px -4px -4px 0">
<svg viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</div>
<div id="accounts-popup-list"></div>
<button class="accounts-add-btn" onclick="closeAccountsMenu();openAddAccountModal()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
Connect new account
</button>
</div>
</div>
<div class="accounts-popup-backdrop" id="accounts-popup-backdrop" onclick="closeAccountsMenu()"></div>
<!-- ── Draggable Compose dialog ───────────────────────────────────────────── -->
<div class="compose-dialog" id="compose-dialog">
<div class="compose-dialog-header" id="compose-drag-handle">
<span class="compose-title" id="compose-title">New Message</span>
<div style="display:flex;align-items:center;gap:2px">
<button class="compose-close" onclick="minimizeCompose()" title="Minimise">&#8211;</button>
<button class="compose-close" onclick="closeCompose()" title="Close">&#215;</button>
2026-03-07 06:20:39 +00:00
</div>
</div>
<div class="compose-body-wrap" id="compose-body-wrap">
2026-03-07 06:20:39 +00:00
<div class="compose-field"><label>From</label><select id="compose-from"></select></div>
<div class="compose-field compose-tag-field"><label>To</label><div id="compose-to" class="tag-container"></div></div>
<div class="compose-field compose-tag-field" id="cc-row" style="display:none"><label>CC</label><div id="compose-cc-tags" class="tag-container"></div></div>
<div class="compose-field compose-tag-field" id="bcc-row" style="display:none"><label>BCC</label><div id="compose-bcc-tags" class="tag-container"></div></div>
<div class="compose-field"><label>Subject</label><input type="text" id="compose-subject" oninput="S.draftDirty=true"></div>
<div class="compose-toolbar">
<button class="fmt-btn" title="Bold" onclick="execFmt('bold')"><b>B</b></button>
<button class="fmt-btn" title="Italic" onclick="execFmt('italic')"><i>I</i></button>
<button class="fmt-btn" title="Underline" onclick="execFmt('underline')"><u>U</u></button>
<span class="fmt-sep"></span>
<button class="fmt-btn" title="Bullets" onclick="execFmt('insertUnorderedList')">&#8226;&#8212;</button>
<button class="fmt-btn" title="Numbers" onclick="execFmt('insertOrderedList')">1&#8212;</button>
<span class="fmt-sep"></span>
<button class="fmt-btn" title="Link" onclick="insertLink()">&#128279;</button>
<button class="fmt-btn" title="Clear format" onclick="execFmt('removeFormat')">T&#x20D7;</button>
</div>
<div id="compose-editor" contenteditable="true" class="compose-editor" placeholder="Write your message..."></div>
<div id="compose-attach-list" class="compose-attach-list"></div>
<div class="compose-footer">
<button class="send-btn" id="send-btn" onclick="sendMessage()">Send</button>
<div style="display:flex;gap:6px;margin-left:4px">
<button class="btn-secondary" style="font-size:12px" onclick="showCCRow()">+CC</button>
<button class="btn-secondary" style="font-size:12px" onclick="showBCCRow()">+BCC</button>
2026-03-07 06:20:39 +00:00
<button class="btn-secondary" style="font-size:12px" onclick="triggerAttach()">&#128206; Attach</button>
<button class="btn-secondary" style="font-size:12px" onclick="saveDraft()">&#9998; Draft</button>
</div>
<input type="file" id="compose-attach-input" multiple style="display:none" onchange="handleAttachFiles(this)">
</div>
</div>
<div class="compose-resize" data-dir="e"></div>
<div class="compose-resize" data-dir="s"></div>
<div class="compose-resize" data-dir="se"></div>
<div class="compose-resize" data-dir="w"></div>
<div class="compose-resize" data-dir="sw"></div>
<div class="compose-resize" data-dir="n"></div>
<div class="compose-resize" data-dir="ne"></div>
<div class="compose-resize" data-dir="nw"></div>
</div>
<!-- Minimised pill (shown when user clicks on header) -->
<div class="compose-minimised" id="compose-minimised" onclick="restoreCompose()">
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
<span id="compose-minimised-label">New Message</span>
</div>
<!-- ── Inline confirm (replaces browser confirm()) ───────────────────────── -->
<div class="inline-confirm" id="inline-confirm">
2026-03-07 16:49:23 +00:00
<p id="inline-confirm-msg" style="margin:0 0 14px;font-size:13px;line-height:1.5"></p>
<div style="display:flex;gap:8px;justify-content:flex-end">
<button class="btn-secondary" style="font-size:12px" id="inline-confirm-cancel">Cancel</button>
<button class="btn-danger" style="font-size:12px" id="inline-confirm-ok">Confirm</button>
</div>
2026-03-07 06:20:39 +00:00
</div>
<!-- ── Add Account Modal ──────────────────────────────────────────────────── -->
2026-03-07 06:20:39 +00:00
<div class="modal-overlay" id="add-account-modal">
<div class="modal">
<h2>Connect an account</h2>
<p>Connect Gmail or Outlook via OAuth, or any email via IMAP/SMTP.</p>
<div class="provider-btns">
<button class="provider-btn" id="btn-gmail" onclick="connectOAuth('gmail')">
<svg viewBox="0 0 24 24" width="18" height="18"><path fill="#EA4335" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path fill="#4285F4" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>
Gmail
</button>
<button class="provider-btn" id="btn-outlook" onclick="connectOAuth('outlook')">
<svg viewBox="0 0 24 24" width="18" height="18" fill="#0078D4"><path d="M21.179 4.781H11.25V12h9.929V4.781zM11.25 19.219h9.929V12H11.25v7.219zM2.821 12H11.25V4.781H2.821V12zm0 7.219H11.25V12H2.821v7.219z"/></svg>
Outlook
</button>
</div>
<div class="modal-divider"><span>or add IMAP account</span></div>
<div class="modal-field"><label>Email Address</label>
<div style="display:flex;gap:8px;flex:1">
<input type="email" id="imap-email" placeholder="you@example.com" style="flex:1">
<button class="btn-secondary" id="detect-btn" onclick="detectMailSettings()" style="white-space:nowrap;font-size:12px">Auto-detect</button>
</div>
</div>
2026-03-07 06:20:39 +00:00
<div class="modal-field"><label>Display Name</label><input type="text" id="imap-name" placeholder="Your Name"></div>
<div class="modal-field"><label>Password / App Password</label><input type="password" id="imap-password"></div>
<div style="font-size:11px;color:var(--muted);padding:0 0 8px;line-height:1.6">
Common ports — IMAP: <strong>993</strong> TLS/SSL, <strong>143</strong> STARTTLS/Plain &nbsp;·&nbsp;
SMTP: <strong>587</strong> STARTTLS, <strong>465</strong> TLS/SSL, <strong>25</strong> Plain
</div>
2026-03-07 06:20:39 +00:00
<div class="modal-row">
<div class="modal-field"><label>IMAP Host</label><input type="text" id="imap-host" placeholder="imap.example.com"></div>
<div class="modal-field"><label>IMAP Port</label><input type="number" id="imap-port" value="993"></div>
</div>
<div class="modal-row">
<div class="modal-field"><label>SMTP Host</label><input type="text" id="smtp-host" placeholder="smtp.example.com"></div>
<div class="modal-field"><label>SMTP Port</label><input type="number" id="smtp-port" value="587"></div>
</div>
<div class="test-result" id="test-result"></div>
<div class="modal-actions">
<button class="modal-cancel" onclick="closeModal('add-account-modal')">Cancel</button>
<button class="btn-secondary" onclick="testNewConnection()" id="test-btn">Test Connection</button>
<button class="modal-submit" onclick="addIMAPAccount()" id="save-acct-btn">Connect</button>
</div>
</div>
</div>
<!-- ── Edit Account Modal ─────────────────────────────────────────────────── -->
2026-03-07 06:20:39 +00:00
<div class="modal-overlay" id="edit-account-modal">
<div class="modal">
<h2>Account Settings</h2>
<p id="edit-account-email" style="font-weight:500;color:var(--text);margin-bottom:16px"></p>
<input type="hidden" id="edit-account-id">
<div class="modal-field"><label>Display Name</label><input type="text" id="edit-name"></div>
<div class="modal-field"><label>New Password (leave blank to keep current)</label><input type="password" id="edit-password"></div>
<div class="modal-row">
<div class="modal-field"><label>IMAP Host</label><input type="text" id="edit-imap-host"></div>
<div class="modal-field"><label>IMAP Port</label><input type="number" id="edit-imap-port"></div>
</div>
<div class="modal-row">
<div class="modal-field"><label>SMTP Host</label><input type="text" id="edit-smtp-host"></div>
<div class="modal-field"><label>SMTP Port</label><input type="number" id="edit-smtp-port"></div>
</div>
<div class="settings-group-title" style="margin:16px 0 8px">Sync Settings</div>
<div class="modal-field">
2026-03-07 20:29:20 +00:00
<label>Email history to sync</label>
2026-03-07 06:20:39 +00:00
<select id="edit-sync-mode" onchange="toggleSyncDaysField()" style="padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px;outline:none">
2026-03-07 20:29:20 +00:00
<option value="preset-30">Last 1 month</option>
<option value="preset-90">Last 3 months</option>
<option value="preset-180">Last 6 months</option>
<option value="preset-365">Last 1 year</option>
<option value="preset-730">Last 2 years</option>
<option value="preset-1825">Last 5 years</option>
<option value="all" selected>All emails (full mailbox)</option>
<option value="days">Custom (days)</option>
2026-03-07 06:20:39 +00:00
</select>
</div>
2026-03-07 20:29:20 +00:00
<div class="modal-row" id="edit-sync-days-row" style="display:none">
<div class="modal-field"><label>Custom days to fetch</label><input type="number" id="edit-sync-days" value="30" min="1" max="36500"></div>
2026-03-07 06:20:39 +00:00
</div>
<div id="edit-conn-result" class="test-result" style="display:none"></div>
<div id="edit-last-error" style="display:none" class="alert error"></div>
<div class="settings-group-title" style="margin:16px 0 8px">Hidden Folders</div>
<div id="edit-hidden-folders" style="font-size:12px;color:var(--muted)">Loading…</div>
2026-03-07 06:20:39 +00:00
<div class="modal-actions">
<button class="modal-cancel" onclick="closeModal('edit-account-modal')">Cancel</button>
<button class="btn-secondary" id="edit-test-btn" onclick="testEditConnection()">Test Connection</button>
<button class="modal-submit" onclick="saveAccountEdit()">Save</button>
</div>
</div>
</div>
<!-- ── Settings Modal ─────────────────────────────────────────────────────── -->
2026-03-07 06:20:39 +00:00
<div class="modal-overlay" id="settings-modal">
<div class="modal" style="width:520px">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:22px">
<h2 style="margin-bottom:0">Settings</h2>
<button onclick="closeModal('settings-modal')" class="icon-btn"><svg viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></button>
</div>
<div class="settings-group">
<div class="settings-group-title">Email Sync</div>
<div style="font-size:13px;color:var(--muted);margin-bottom:12px">How often to automatically check all your accounts for new mail.</div>
<div style="display:flex;gap:10px;align-items:center">
<select id="sync-interval-select" style="flex:1;padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:13px;outline:none">
<option value="0">Manual only</option>
<option value="1">Every 1 minute</option>
<option value="5">Every 5 minutes</option>
<option value="10">Every 10 minutes</option>
<option value="15">Every 15 minutes (default)</option>
<option value="30">Every 30 minutes</option>
<option value="60">Every 60 minutes</option>
</select>
<button class="btn-primary" onclick="saveSyncInterval()">Save</button>
</div>
</div>
<div class="settings-group">
<div class="settings-group-title">Change Password</div>
<div class="modal-field"><label>Current Password</label><input type="password" id="cur-pw"></div>
<div class="modal-field"><label>New Password</label><input type="password" id="new-pw" placeholder="Min. 8 characters"></div>
<button class="btn-primary" onclick="changePassword()">Update Password</button>
</div>
<div class="settings-group">
<div class="settings-group-title" style="display:flex;align-items:center;gap:10px">
Two-Factor Authentication <span id="mfa-badge"></span>
</div>
<div id="mfa-panel">Loading...</div>
</div>
</div>
</div>
<!-- Context menu -->
<div class="ctx-menu" id="ctx-menu"></div>
<div class="toast-container" id="toast-container"></div>
{{end}}
{{define "scripts"}}
2026-03-07 20:29:20 +00:00
<script src="/static/js/app.js?v=11"></script>
{{end}}