mirror of
https://github.com/Crazyco-xyz/48hr.email.git
synced 2026-01-09 19:29:34 +01:00
[Feat]: Add loading animation on init
PRevents errors on very early inbox load (mostly by bots but oh well... perfectionTM)
This commit is contained in:
parent
5226cb3c6b
commit
12069300d0
5 changed files with 87 additions and 3 deletions
10
app.js
10
app.js
|
|
@ -46,14 +46,20 @@ const mailProcessingService = new MailProcessingService(
|
||||||
)
|
)
|
||||||
debug('Mail processing service initialized')
|
debug('Mail processing service initialized')
|
||||||
|
|
||||||
|
// Track IMAP initialization state
|
||||||
|
let isImapReady = false
|
||||||
|
app.set('isImapReady', false)
|
||||||
|
|
||||||
// Put everything together:
|
// Put everything together:
|
||||||
imapService.on(ImapService.EVENT_NEW_MAIL, mail =>
|
imapService.on(ImapService.EVENT_NEW_MAIL, mail =>
|
||||||
mailProcessingService.onNewMail(mail)
|
mailProcessingService.onNewMail(mail)
|
||||||
)
|
)
|
||||||
debug('Bound IMAP new mail event handler')
|
debug('Bound IMAP new mail event handler')
|
||||||
imapService.on(ImapService.EVENT_INITIAL_LOAD_DONE, () =>
|
imapService.on(ImapService.EVENT_INITIAL_LOAD_DONE, () => {
|
||||||
mailProcessingService.onInitialLoadDone()
|
mailProcessingService.onInitialLoadDone()
|
||||||
)
|
isImapReady = true
|
||||||
|
app.set('isImapReady', true)
|
||||||
|
})
|
||||||
debug('Bound IMAP initial load done event handler')
|
debug('Bound IMAP initial load done event handler')
|
||||||
imapService.on(ImapService.EVENT_DELETED_MAIL, mail =>
|
imapService.on(ImapService.EVENT_DELETED_MAIL, mail =>
|
||||||
mailProcessingService.onMailDeleted(mail)
|
mailProcessingService.onMailDeleted(mail)
|
||||||
|
|
|
||||||
|
|
@ -894,6 +894,43 @@ label {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 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 */
|
/* Responsive Styles */
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@
|
||||||
<script src="/javascripts/notifications.js" defer="true"></script>
|
<script src="/javascripts/notifications.js" defer="true"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body{% if bodyClass %} class="{{ bodyClass }}"{% endif %}>
|
||||||
<main>
|
<main>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<a href="/">
|
<a href="/">
|
||||||
|
|
|
||||||
27
infrastructure/web/views/loading.twig
Normal file
27
infrastructure/web/views/loading.twig
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{% 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 %}
|
||||||
|
|
@ -85,6 +85,20 @@ app.use(
|
||||||
)
|
)
|
||||||
Twig.extendFilter('sanitizeHtml', sanitizeHtmlTwigFilter)
|
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', {
|
||||||
|
branding: config.http.branding,
|
||||||
|
purgeTime: purgeTime,
|
||||||
|
count: "NaN Emails"
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
app.use('/', loginRouter)
|
app.use('/', loginRouter)
|
||||||
app.use('/inbox', inboxRouter)
|
app.use('/inbox', inboxRouter)
|
||||||
app.use('/error', errorRouter)
|
app.use('/error', errorRouter)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue