diff --git a/apps/browser-extension/src/entrypoints/popup/pages/emails/EmailsList.tsx b/apps/browser-extension/src/entrypoints/popup/pages/emails/EmailsList.tsx index 204c8de76..085d99d61 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/emails/EmailsList.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/emails/EmailsList.tsx @@ -29,16 +29,24 @@ const EmailsList: React.FC = () => { const [error, setError] = useState(null); const [emails, setEmails] = useState([]); const { setIsInitialLoading } = useLoading(); + const [currentPage, setCurrentPage] = useState(1); + const [totalRecords, setTotalRecords] = useState(0); + const [isLoadingMore, setIsLoadingMore] = useState(false); /** * Loading state with minimum duration for more fluid UX. */ const [isLoading, setIsLoading] = useMinDurationLoading(true, 100); + /** + * Page size for pagination. + */ + const PAGE_SIZE = 50; + /** * Loads emails from the web API. */ - const loadEmails = useCallback(async () : Promise => { + const loadEmails = useCallback(async (reset: boolean = true) : Promise => { try { setIsLoading(true); setError(null); @@ -58,11 +66,10 @@ const EmailsList: React.FC = () => { const emailAddresses = dbContext.sqliteClient.items.getAllEmailAddresses(); try { - // For now we only show the latest 50 emails. No pagination. const data = await webApi.post('EmailBox/bulk', { addresses: emailAddresses, page: 1, - pageSize: 50, + pageSize: PAGE_SIZE, }); // Decrypt emails locally using private key associated with the email address. @@ -71,7 +78,11 @@ const EmailsList: React.FC = () => { // Decrypt emails locally using public/private key pairs. const decryptedEmails = await EncryptionUtility.decryptEmailList(data.mails, encryptionKeys); - setEmails(decryptedEmails); + if (reset) { + setEmails(decryptedEmails); + setCurrentPage(data.currentPage); + setTotalRecords(data.totalRecords); + } } catch (error) { console.error(error); throw new Error(t('common.errors.unknownError')); @@ -82,7 +93,44 @@ const EmailsList: React.FC = () => { setIsLoading(false); setIsInitialLoading(false); } - }, [dbContext?.sqliteClient, dbContext.isOffline, webApi, setIsLoading, setIsInitialLoading, t]); + }, [dbContext?.sqliteClient, dbContext.isOffline, webApi, setIsLoading, setIsInitialLoading, t, PAGE_SIZE]); + + /** + * Loads more emails (next page). + */ + const loadMoreEmails = useCallback(async () : Promise => { + if (isLoadingMore || !dbContext?.sqliteClient || dbContext.isOffline) { + return; + } + + try { + setIsLoadingMore(true); + setError(null); + + const emailAddresses = dbContext.sqliteClient.items.getAllEmailAddresses(); + const nextPage = currentPage + 1; + + const data = await webApi.post('EmailBox/bulk', { + addresses: emailAddresses, + page: nextPage, + pageSize: PAGE_SIZE, + }); + + // Decrypt emails locally + const encryptionKeys = dbContext.sqliteClient.settings.getAllEncryptionKeys(); + const decryptedEmails = await EncryptionUtility.decryptEmailList(data.mails, encryptionKeys); + + // Append to existing emails + setEmails((prevEmails) => [...prevEmails, ...decryptedEmails]); + setCurrentPage(data.currentPage); + setTotalRecords(data.totalRecords); + } catch (err) { + setError(err instanceof Error ? err.message : t('common.errors.unknownError')); + console.error('Failed to load more emails:', err); + } finally { + setIsLoadingMore(false); + } + }, [isLoadingMore, dbContext?.sqliteClient, dbContext.isOffline, webApi, currentPage, PAGE_SIZE, t]); useEffect(() => { loadEmails(); @@ -187,11 +235,13 @@ const EmailsList: React.FC = () => { ); } + const hasMoreEmails = totalRecords > emails.length; + return (
{t('emails.title')} - + loadEmails(true)} />
{emails.map((email) => ( @@ -214,6 +264,29 @@ const EmailsList: React.FC = () => { ))}
+ + {/* Load More Button */} + {hasMoreEmails && emails.length > 0 && ( +
+ +
+ )}
); }; diff --git a/apps/browser-extension/src/i18n/locales/en.json b/apps/browser-extension/src/i18n/locales/en.json index 395e41d89..c9180cced 100644 --- a/apps/browser-extension/src/i18n/locales/en.json +++ b/apps/browser-extension/src/i18n/locales/en.json @@ -326,6 +326,7 @@ "noEmails": "No emails found", "noEmailsDescription": "You have not received any emails at your private email addresses yet. When you receive a new email, it will appear here.", "offlineMessage": "You are offline. Please connect to the internet to load your emails.", + "loadMore": "Load More ({{count}} remaining)", "dateFormat": { "justNow": "just now", "minutesAgo_single": "{{count}} min ago",