From eb557d16dd3dab466f58134da7e55a8702117127 Mon Sep 17 00:00:00 2001 From: alma Date: Wed, 30 Apr 2025 14:40:01 +0200 Subject: [PATCH] courrier multi account restore compose --- hooks/use-email-state.ts | 187 +++++++++++---------------------------- 1 file changed, 52 insertions(+), 135 deletions(-) diff --git a/hooks/use-email-state.ts b/hooks/use-email-state.ts index ec03e0c3..1092d41a 100644 --- a/hooks/use-email-state.ts +++ b/hooks/use-email-state.ts @@ -614,141 +614,6 @@ export const useEmailState = () => { } }, [session?.user?.id, state.page, state.currentFolder, loadEmails, logEmailOp]); - // Calculate and update unread counts from emails - const updateUnreadCounts = useCallback(() => { - // This function will count unread emails in each folder and update the unreadCountMap - if (state.emails.length === 0) return; - - // Create a temporary count map - const tempUnreadMap: Record> = {}; - - // Initialize with existing accounts and folders to avoid losing counts - // when switching folders - state.accounts.forEach(account => { - tempUnreadMap[account.id] = { ...state.unreadCountMap[account.id] || {} }; - - // Initialize each folder with 0 if it doesn't exist - account.folders.forEach(folder => { - // Extract base folder name if it has a prefix - const baseFolderName = folder.includes(':') ? folder.split(':')[1] : folder; - const prefixedFolder = folder.includes(':') ? folder : `${account.id}:${baseFolderName}`; - - // Initialize both prefixed and unprefixed versions - if (tempUnreadMap[account.id][baseFolderName] === undefined) { - tempUnreadMap[account.id][baseFolderName] = 0; - } - if (tempUnreadMap[account.id][prefixedFolder] === undefined) { - tempUnreadMap[account.id][prefixedFolder] = 0; - } - }); - }); - - // Count unread emails from current folder - state.emails.forEach(email => { - // Check if email is unread - const isUnread = email.flags && !email.flags.seen; - - // Only count if it's unread - if (isUnread && email.accountId) { - // Get folder information - let folder = email.folder; - let accountId = email.accountId; - - // Make sure the account exists in our map - if (!tempUnreadMap[accountId]) { - tempUnreadMap[accountId] = {}; - } - - // Extract folder name if it has a prefix - let baseFolderName = folder; - if (folder.includes(':')) { - const parts = folder.split(':'); - // Don't override accountId from the folder prefix - baseFolderName = parts[1]; - } - - // Ensure we have entries for both formats - if (!tempUnreadMap[accountId][baseFolderName]) { - tempUnreadMap[accountId][baseFolderName] = 0; - } - if (!tempUnreadMap[accountId][folder]) { - tempUnreadMap[accountId][folder] = 0; - } - - // Increment both formats to ensure they're both available - tempUnreadMap[accountId][baseFolderName]++; - tempUnreadMap[accountId][folder]++; - } - }); - - // Store the current update for comparison - if (!(window as any).__lastUnreadUpdate) { - (window as any).__lastUnreadUpdate = { - timestamp: 0, - map: {} - }; - } - - // Check if the unread counts have actually changed before updating state - let hasChanged = false; - - // Compare with current unread count map - Object.entries(tempUnreadMap).forEach(([accountId, folderCounts]) => { - Object.entries(folderCounts).forEach(([folder, count]) => { - if (state.unreadCountMap[accountId]?.[folder] !== count) { - hasChanged = true; - } - }); - }); - - // Check if we've updated recently - const now = Date.now(); - const lastUpdate = (window as any).__lastUnreadUpdate; - const timeSinceLastUpdate = now - lastUpdate.timestamp; - - // If changes found and not updated too recently, update state - if (hasChanged && timeSinceLastUpdate > 500) { - // Log only on actual change - logEmailOp('UNREAD_COUNTS', 'Updated unread counts:', tempUnreadMap); - - // Create a single dispatch to update all counts at once - dispatch({ - type: 'SET_UNREAD_COUNTS', - payload: tempUnreadMap - }); - - // Update timestamp of last update - lastUpdate.timestamp = now; - lastUpdate.map = tempUnreadMap; - } - }, [state.emails, state.accounts, state.unreadCountMap, dispatch, logEmailOp]); - - // Call updateUnreadCounts when relevant state changes - useEffect(() => { - if (!state.emails || state.emails.length === 0) return; - - // Debounce unread count updates to prevent rapid multiple updates - let updateTimeoutId: ReturnType; - - const debounceMs = 500; // 500ms debounce - - // Function to call after debounce period - const debouncedUpdate = () => { - updateTimeoutId = setTimeout(() => { - updateUnreadCounts(); - }, debounceMs); - }; - - // Clear any existing timeout and start a new one - debouncedUpdate(); - - // Cleanup timeout on unmount or state change - return () => { - clearTimeout(updateTimeoutId); - }; - // Deliberately exclude unreadCountMap to prevent infinite loops - }, [state.emails, updateUnreadCounts]); - // Fetch unread counts from API const fetchUnreadCounts = useCallback(async () => { // Don't fetch if user is not logged in @@ -827,7 +692,59 @@ export const useEmailState = () => { dispatch({ type: 'SET_LOADING_UNREAD_COUNTS', payload: false }); } }, [dispatch, session?.user, state.isLoadingUnreadCounts, logEmailOp]); + + // Calculate and update unread counts + const updateUnreadCounts = useCallback(() => { + // Skip if no emails or accounts + if (state.emails.length === 0 || state.accounts.length === 0) return; + + // To avoid running this too frequently, check the timestamp of last update + if (!(window as any).__lastUnreadUpdate) { + (window as any).__lastUnreadUpdate = { timestamp: 0 }; + } + + const now = Date.now(); + const lastUpdate = (window as any).__lastUnreadUpdate; + const MIN_UPDATE_INTERVAL = 2000; // 2 seconds minimum between updates + + if (now - lastUpdate.timestamp < MIN_UPDATE_INTERVAL) { + return; // Skip if updated too recently + } + + // Rather than calculating locally, let's fetch from the API + // This ensures we get accurate server-side counts + fetchUnreadCounts(); + + // Update timestamp of last update + lastUpdate.timestamp = now; + }, [state.emails.length, state.accounts.length, fetchUnreadCounts]); + // Call updateUnreadCounts when relevant state changes + useEffect(() => { + if (!state.emails || state.emails.length === 0) return; + + // Debounce unread count updates to prevent rapid multiple updates + let updateTimeoutId: ReturnType; + + const debounceMs = 2000; // Increase debounce to 2 seconds + + // Function to call after debounce period + const debouncedUpdate = () => { + updateTimeoutId = setTimeout(() => { + updateUnreadCounts(); + }, debounceMs); + }; + + // Clear any existing timeout and start a new one + debouncedUpdate(); + + // Cleanup timeout on unmount or state change + return () => { + clearTimeout(updateTimeoutId); + }; + // Deliberately exclude unreadCountMap to prevent infinite loops + }, [state.emails, updateUnreadCounts]); + // Tracking when an email is viewed to optimize unread count refreshes const lastViewedEmailRef = useRef(null); const fetchFailuresRef = useRef(0);