mirror of
https://github.com/Crazyco-xyz/48hr.email.git
synced 2026-02-14 17:19:35 +01:00
Compare commits
No commits in common. "cc4e3ddfbd586198ede9fed2269cae9d296d2d8a" and "633d9c9b29a5f211d3cee695995ea6bc486eb943" have entirely different histories.
cc4e3ddfbd
...
633d9c9b29
10 changed files with 21 additions and 209 deletions
10
app.js
10
app.js
|
|
@ -46,20 +46,14 @@ const mailProcessingService = new MailProcessingService(
|
|||
)
|
||||
debug('Mail processing service initialized')
|
||||
|
||||
// Track IMAP initialization state
|
||||
let isImapReady = false
|
||||
app.set('isImapReady', false)
|
||||
|
||||
// Put everything together:
|
||||
imapService.on(ImapService.EVENT_NEW_MAIL, mail =>
|
||||
mailProcessingService.onNewMail(mail)
|
||||
)
|
||||
debug('Bound IMAP new mail event handler')
|
||||
imapService.on(ImapService.EVENT_INITIAL_LOAD_DONE, () => {
|
||||
imapService.on(ImapService.EVENT_INITIAL_LOAD_DONE, () =>
|
||||
mailProcessingService.onInitialLoadDone()
|
||||
isImapReady = true
|
||||
app.set('isImapReady', true)
|
||||
})
|
||||
)
|
||||
debug('Bound IMAP initial load done event handler')
|
||||
imapService.on(ImapService.EVENT_DELETED_MAIL, mail =>
|
||||
mailProcessingService.onMailDeleted(mail)
|
||||
|
|
|
|||
|
|
@ -281,41 +281,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
});
|
||||
}
|
||||
|
||||
function initHamburgerMenu() {
|
||||
const actionLinks = document.querySelector('.action-links');
|
||||
if (!actionLinks) return;
|
||||
|
||||
// Create hamburger button
|
||||
const hamburger = document.createElement('button');
|
||||
hamburger.className = 'hamburger-menu';
|
||||
hamburger.setAttribute('aria-label', 'Toggle menu');
|
||||
hamburger.innerHTML = '<span></span><span></span><span></span>';
|
||||
|
||||
// Insert as first child
|
||||
actionLinks.insertBefore(hamburger, actionLinks.firstChild);
|
||||
actionLinks.classList.add('mobile-hidden');
|
||||
|
||||
hamburger.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
actionLinks.classList.toggle('mobile-hidden');
|
||||
actionLinks.classList.toggle('mobile-open');
|
||||
});
|
||||
|
||||
// Close menu when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
if (actionLinks.classList.contains('mobile-open') && !actionLinks.contains(e.target)) {
|
||||
actionLinks.classList.remove('mobile-open');
|
||||
actionLinks.classList.add('mobile-hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Expose utilities and run them
|
||||
window.utils = { formatEmailDates, formatMailDate, initLockModals, initCopyAddress, initExpiryTimers, initQrModal, initHamburgerMenu };
|
||||
window.utils = { formatEmailDates, formatMailDate, initLockModals, initCopyAddress, initExpiryTimers, initQrModal };
|
||||
formatEmailDates();
|
||||
formatMailDate();
|
||||
initLockModals();
|
||||
initCopyAddress();
|
||||
initQrModal();
|
||||
initHamburgerMenu();
|
||||
});
|
||||
|
|
@ -158,39 +158,6 @@ text-muted {
|
|||
flex-wrap: nowrap;
|
||||
/* ensures they stay in one line */
|
||||
text-align: right;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.hamburger-menu {
|
||||
display: none;
|
||||
background: var(--overlay-purple-20);
|
||||
border: 1px solid var(--overlay-purple-30);
|
||||
border-radius: 12px;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
color: var(--color-accent-purple-light);
|
||||
transition: all 0.3s ease;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hamburger-menu span {
|
||||
width: 24px;
|
||||
height: 2px;
|
||||
background: currentColor;
|
||||
border-radius: 2px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.hamburger-menu:hover {
|
||||
background: var(--overlay-purple-30);
|
||||
border-color: var(--overlay-purple-40);
|
||||
box-shadow: 0 4px 15px var(--overlay-purple-25);
|
||||
}
|
||||
|
||||
.action-links a {
|
||||
|
|
@ -374,11 +341,9 @@ label {
|
|||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.inbox-title {
|
||||
|
|
@ -770,9 +735,7 @@ label {
|
|||
display: none;
|
||||
color: var(--color-accent-purple-alt);
|
||||
font-size: 0.9em;
|
||||
position: absolute;
|
||||
margin-left: 0;
|
||||
margin-top: 50px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.email-expiry .expiry-timer[style*="color: #b00"] {
|
||||
|
|
@ -872,7 +835,6 @@ label {
|
|||
}
|
||||
|
||||
.qr-modal-content h3 {
|
||||
margin-left: 16.516px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
|
|
@ -891,82 +853,4 @@ label {
|
|||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
|
||||
/* Loading Page */
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 80vh;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.loading-title {
|
||||
color: var(--color-text-primary);
|
||||
margin-bottom: 15px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.loading-message {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.loading-submessage {
|
||||
color: var(--color-text-muted);
|
||||
font-size: 1rem;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
body.loading-page .logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* Responsive Styles */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.action-links {
|
||||
position: relative;
|
||||
}
|
||||
.hamburger-menu {
|
||||
display: flex;
|
||||
}
|
||||
.action-links.mobile-hidden>a,
|
||||
.action-links.mobile-hidden>button:not(.hamburger-menu) {
|
||||
display: none;
|
||||
}
|
||||
.action-links.mobile-open {
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
background: var(--color-bg-dark);
|
||||
border: 1px solid var(--overlay-purple-30);
|
||||
border-radius: 15px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 10px 40px var(--overlay-black-40);
|
||||
z-index: 1000;
|
||||
min-width: 200px;
|
||||
}
|
||||
.action-links.mobile-open>.hamburger-menu {
|
||||
display: none;
|
||||
}
|
||||
.action-links.mobile-open>a,
|
||||
.action-links.mobile-open>button:not(.hamburger-menu) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.qr-icon-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,9 @@
|
|||
{% block header %}
|
||||
<div class="action-links">
|
||||
{% if showUnlockButton %}
|
||||
<a href="#" id="unlockBtn" aria-label="Unlock inbox">Unlock</a>
|
||||
<a href="#" id="unlockBtn">Unlock</a>
|
||||
{% endif %}
|
||||
<a href="/" aria-label="Return to home">Logout</a>
|
||||
<a href="/">Logout</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,18 +4,18 @@
|
|||
<div class="action-links">
|
||||
{% if lockEnabled %}
|
||||
{% if isLocked and hasAccess %}
|
||||
<a href="#" id="removeLockBtn" aria-label="Remove password lock">Remove Lock</a>
|
||||
<a href="#" id="removeLockBtn">Remove Lock</a>
|
||||
{% elseif isLocked %}
|
||||
<a href="#" id="unlockBtn" aria-label="Unlock inbox">Unlock</a>
|
||||
<a href="#" id="unlockBtn">Unlock</a>
|
||||
{% else %}
|
||||
<a href="#" id="lockBtn" aria-label="Protect inbox with password">Protect Inbox</a>
|
||||
<a href="#" id="lockBtn">Protect Inbox</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<a href="/inbox/{{ address }}/delete-all" aria-label="Delete all emails">Wipe Inbox</a>
|
||||
<a href="/inbox/{{ address }}/delete-all">Wipe Inbox</a>
|
||||
{% if lockEnabled and hasAccess %}
|
||||
<a href="/lock/logout" aria-label="Logout">Logout</a>
|
||||
<a href="/lock/logout">Logout</a>
|
||||
{% else %}
|
||||
<a href="/logout" aria-label="Logout">Logout</a>
|
||||
<a href="/logout">Logout</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -75,11 +75,11 @@
|
|||
<script src="/javascripts/notifications.js" defer="true"></script>
|
||||
|
||||
</head>
|
||||
<body{% if bodyClass %} class="{{ bodyClass }}"{% endif %}>
|
||||
<body>
|
||||
<main>
|
||||
<div class="header">
|
||||
<a href="/" aria-label="48hr.email home">
|
||||
<img src="/images/logo.png" class="logo" alt="48hr.email logo" style="max-width: 75px">
|
||||
<a href="/">
|
||||
<img src="/images/logo.png" class="logo" style="max-width: 75px">
|
||||
</a>
|
||||
{% block header %}{% endblock %}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
{% extends "layout.twig" %}
|
||||
|
||||
{% set bodyClass = 'loading-page' %}
|
||||
|
||||
{% block header %}{% endblock %}
|
||||
{% block footer %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="loading-container">
|
||||
<div class="loading-spinner">
|
||||
<svg width="80" height="80" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="50" cy="50" r="40" stroke="var(--color-accent-purple-light)" stroke-width="8" fill="none" stroke-dasharray="63 188" stroke-linecap="round">
|
||||
<animateTransform attributeName="transform" type="rotate" from="0 50 50" to="360 50 50" dur="1.5s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 class="loading-title">Loading Mail Service</h2>
|
||||
<p class="loading-message">Connecting to IMAP server and loading messages...</p>
|
||||
<p class="loading-submessage">This may take a few moments on first startup</p>
|
||||
</div>
|
||||
<script>
|
||||
// Auto-refresh every 2 seconds to check if IMAP is ready
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
{% block header %}
|
||||
<div class="action-links">
|
||||
<a href="/inbox/{{ address }}" aria-label="Return to inbox">← Return to inbox</a>
|
||||
<a href="/inbox/{{ address }}/{{ uid }}/delete" aria-label="Delete this email">Delete Email</a>
|
||||
<a href="/inbox/{{ address }}/{{ uid }}/raw" target="_blank" aria-label="View raw email">View Raw</a>
|
||||
<a href="/inbox/{{ address }}">← Return to inbox</a>
|
||||
<a href="/inbox/{{ address }}/{{ uid }}/delete">Delete Email</a>
|
||||
<a href="/inbox/{{ address }}/{{ uid }}/raw" target="_blank">View Raw</a>
|
||||
{% if lockEnabled and isLocked and hasAccess %}
|
||||
<a href="/lock/logout" aria-label="Logout">Logout</a>
|
||||
<a href="/lock/logout">Logout</a>
|
||||
{% else %}
|
||||
<a href="/logout" aria-label="Logout">Logout</a>
|
||||
<a href="/logout">Logout</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -85,15 +85,6 @@ app.use(
|
|||
)
|
||||
Twig.extendFilter('sanitizeHtml', sanitizeHtmlTwigFilter)
|
||||
|
||||
// Middleware to show loading page until IMAP is ready
|
||||
app.use((req, res, next) => {
|
||||
const isImapReady = req.app.get('isImapReady')
|
||||
if (!isImapReady && !req.path.startsWith('/images') && !req.path.startsWith('/javascripts') && !req.path.startsWith('/stylesheets') && !req.path.startsWith('/dependencies')) {
|
||||
return res.render('loading')
|
||||
}
|
||||
next()
|
||||
})
|
||||
|
||||
app.use('/', loginRouter)
|
||||
app.use('/inbox', inboxRouter)
|
||||
app.use('/error', errorRouter)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "48hr.email",
|
||||
"version": "1.7.3",
|
||||
"version": "1.7.2",
|
||||
"private": false,
|
||||
"description": "48hr.email is your favorite open-source tempmail client.",
|
||||
"keywords": [
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue