From 72c22f9815bac22ed2a7351adff72a056a0f3ba4 Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Wed, 31 Dec 2025 22:40:53 +0100 Subject: [PATCH] [Chore] Sync timer to actual imap-reload --- app.js | 5 ++++ application/imap-service.js | 23 ++++++++++++++++++- infrastructure/web/client-notification.js | 23 +++++++++++++++++++ .../web/public/javascripts/notifications.js | 9 +++++++- .../web/public/javascripts/utils.js | 18 ++++++--------- 5 files changed, 65 insertions(+), 13 deletions(-) diff --git a/app.js b/app.js index e5ad092..184c615 100644 --- a/app.js +++ b/app.js @@ -43,6 +43,11 @@ const mailProcessingService = new MailProcessingService( ) debug('Mail processing service initialized') +// Set up timer sync broadcasting after IMAP is ready +imapService.on(ImapService.EVENT_INITIAL_LOAD_DONE, () => { + clientNotification.startTimerSync(imapService) +}) + // Track IMAP initialization state let isImapReady = false app.set('isImapReady', false) diff --git a/application/imap-service.js b/application/imap-service.js index 5efa787..8be19ba 100644 --- a/application/imap-service.js +++ b/application/imap-service.js @@ -97,6 +97,7 @@ class ImapService extends EventEmitter { this.connection = null this.initialLoadDone = false this.loadingInProgress = false + this.lastRefreshTime = null } async connectAndLoadMessages() { @@ -157,8 +158,14 @@ class ImapService extends EventEmitter { // for new mails on the server. This is done only after all the mails have been loaded for the // first time. (Note: set the refresh higher than the time it takes to download the mails). if (this.config.imap.refreshIntervalSeconds) { + // Track when refreshes happen + this.lastRefreshTime = Date.now() + setInterval( - () => this._loadMailSummariesAndEmitAsEvents(), + () => { + this.lastRefreshTime = Date.now() + this._loadMailSummariesAndEmitAsEvents() + }, this.config.imap.refreshIntervalSeconds * 1000 ) } @@ -306,6 +313,20 @@ class ImapService extends EventEmitter { } } + /** + * Get seconds remaining until next IMAP refresh + * @returns {number} Seconds until next refresh, or null if not started + */ + getSecondsUntilNextRefresh() { + if (!this.lastRefreshTime || !this.config.imap.refreshIntervalSeconds) { + return null + } + + const elapsed = (Date.now() - this.lastRefreshTime) / 1000 + const remaining = Math.max(0, this.config.imap.refreshIntervalSeconds - elapsed) + return Math.ceil(remaining) + } + /** * Helper method because ImapSimple#search also fetches each message. We just need the uids here. * diff --git a/infrastructure/web/client-notification.js b/infrastructure/web/client-notification.js index 3348a44..b27ade0 100644 --- a/infrastructure/web/client-notification.js +++ b/infrastructure/web/client-notification.js @@ -9,9 +9,13 @@ class ClientNotification extends EventEmitter { constructor() { super(); this.pendingNotifications = new Map(); // address -> count + this.io = null; + this.imapService = null; + this.timerSyncInterval = null; } use(io) { + this.io = io; io.on('connection', socket => { debug(`[SOCKET] New connection: id=${socket.id}`); socket.on('sign in', address => { @@ -24,6 +28,25 @@ class ClientNotification extends EventEmitter { }) } + startTimerSync(imapService) { + this.imapService = imapService; + + // Broadcast timer sync every second to all connected clients + if (this.timerSyncInterval) { + clearInterval(this.timerSyncInterval); + } + + this.timerSyncInterval = setInterval(() => { + const secondsRemaining = this.imapService.getSecondsUntilNextRefresh(); + if (secondsRemaining !== null && this.io) { + // Broadcast to all connected clients + this.io.emit('refresh-timer-sync', secondsRemaining); + } + }, 1000); + + debug('Started timer sync broadcasting'); + } + _signIn(socket, address) { debug(`socketio signed in: ${address}`) diff --git a/infrastructure/web/public/javascripts/notifications.js b/infrastructure/web/public/javascripts/notifications.js index 7aa800e..c639b0e 100644 --- a/infrastructure/web/public/javascripts/notifications.js +++ b/infrastructure/web/public/javascripts/notifications.js @@ -31,6 +31,13 @@ function enableNewMessageNotifications(address, reloadPage) { socket.on('new emails', () => { showNewMailsNotification(address, reloadPage) }) + + // Listen for timer sync from server + socket.on('refresh-timer-sync', (secondsRemaining) => { + if (window.updateRefreshTimer && typeof window.updateRefreshTimer === 'function') { + window.updateRefreshTimer(secondsRemaining) + } + }) } function enableNotifications() { @@ -54,4 +61,4 @@ function enableNotifications() { // Finally, if the user has denied notifications and you // want to be respectful there is no need to bother them any more. -} +} \ No newline at end of file diff --git a/infrastructure/web/public/javascripts/utils.js b/infrastructure/web/public/javascripts/utils.js index 328bd13..c31d88c 100644 --- a/infrastructure/web/public/javascripts/utils.js +++ b/infrastructure/web/public/javascripts/utils.js @@ -351,19 +351,15 @@ document.addEventListener('DOMContentLoaded', () => { const refreshTimer = document.getElementById('refreshTimer'); if (!refreshTimer || !refreshInterval) return; - let secondsLeft = refreshInterval; - - function updateTimer() { - refreshTimer.textContent = secondsLeft; - secondsLeft--; - - if (secondsLeft < 0) { - secondsLeft = refreshInterval; + // Function to update timer display + window.updateRefreshTimer = function(secondsLeft) { + if (refreshTimer) { + refreshTimer.textContent = secondsLeft; } - } + }; - updateTimer(); // Initial update - setInterval(updateTimer, 1000); + // Initialize with the configured interval + refreshTimer.textContent = refreshInterval; } // Expose utilities and run them