From 28f6fff9b73268187aebd7e6d9c15a5c5ff3a7b6 Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+xabg2@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:58:15 +0200 Subject: [PATCH 1/5] feat: select mails one-by-one --- package-lock.json | 8 ++++---- package.json | 2 +- src/features/mail/MailView.tsx | 3 ++- src/hooks/mail/useMailSelection.ts | 9 +++++++++ 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index fc38327..37acc98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@internxt/css-config": "^1.1.0", "@internxt/lib": "^1.4.1", "@internxt/sdk": "^1.17.7", - "@internxt/ui": "^0.1.16", + "@internxt/ui": "^0.1.24", "@phosphor-icons/react": "^2.1.10", "@reduxjs/toolkit": "^2.11.2", "@tailwindcss/vite": "^4.2.1", @@ -1464,9 +1464,9 @@ } }, "node_modules/@internxt/ui": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/@internxt/ui/-/ui-0.1.16.tgz", - "integrity": "sha512-V9VQrUVZehvx5L3FChkCfwmOq/wnQsFSSjcfGHMb70SVKZQAfCSv3ZyjcnL5iIaLInWGwtwspmtMfl3qh0zVWQ==", + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/@internxt/ui/-/ui-0.1.24.tgz", + "integrity": "sha512-nPZ2kePXUA/1sT8Zi7BB7PlPHsZyWyupymcvSwUtt9UtegfK899I17zNqy0IXDwBeLHCXkIL9ZaASksnLUEn+A==", "license": "MIT", "dependencies": { "@internxt/css-config": "1.1.0", diff --git a/package.json b/package.json index 5059b5f..8b04760 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@internxt/css-config": "^1.1.0", "@internxt/lib": "^1.4.1", "@internxt/sdk": "^1.17.7", - "@internxt/ui": "^0.1.16", + "@internxt/ui": "^0.1.24", "@phosphor-icons/react": "^2.1.10", "@reduxjs/toolkit": "^2.11.2", "@tailwindcss/vite": "^4.2.1", diff --git a/src/features/mail/MailView.tsx b/src/features/mail/MailView.tsx index 2d0a4bd..aba3fff 100644 --- a/src/features/mail/MailView.tsx +++ b/src/features/mail/MailView.tsx @@ -49,7 +49,7 @@ const MailView = ({ folder }: MailViewProps) => { toggleUnreadFilter, } = useListFolderPaginated(folder); - const { selectedEmails, selectAll, selectNone, selectRead, selectUnread, toggleSelectAll } = + const { selectedEmails, selectAll, selectEmail, selectNone, selectRead, selectUnread, toggleSelectAll } = useMailSelection(listFolderEmails); const { listActionContext, bulkActionContext } = useListActionContext(folder, selectedEmails, { selectAll, @@ -127,6 +127,7 @@ const MailView = ({ folder }: MailViewProps) => { onLoadMore={onLoadMore} emptyState={} onMailSelected={onSelectEmail} + onMailChecked={selectEmail} /> diff --git a/src/hooks/mail/useMailSelection.ts b/src/hooks/mail/useMailSelection.ts index 3891fe0..1d55f73 100644 --- a/src/hooks/mail/useMailSelection.ts +++ b/src/hooks/mail/useMailSelection.ts @@ -10,6 +10,14 @@ export const useMailSelection = (emails: EmailListResponse['emails'] | undefined const selectNone = () => setSelectedEmails([]); + const selectEmail = (emailId: string) => { + setSelectedEmails((prev) => { + if (prev.includes(emailId)) return prev.filter((id) => id !== emailId); + if (!emails?.some((email) => email.id === emailId)) return prev; + return [...prev, emailId]; + }); + }; + const selectRead = () => { setSelectedEmails(emails?.filter((email) => email.isRead).map((email) => email.id) ?? []); }; @@ -31,6 +39,7 @@ export const useMailSelection = (emails: EmailListResponse['emails'] | undefined return { selectedEmails, selectAll, + selectEmail, selectNone, selectRead, selectUnread, From dae30deca02554c93f6801f743adace78876090e Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+xabg2@users.noreply.github.com> Date: Thu, 25 Jun 2026 08:15:01 +0200 Subject: [PATCH 2/5] fix: missing data for fixtures --- src/test-utils/fixtures.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test-utils/fixtures.ts b/src/test-utils/fixtures.ts index 7425cb9..f2f59bf 100644 --- a/src/test-utils/fixtures.ts +++ b/src/test-utils/fixtures.ts @@ -169,6 +169,7 @@ export const getMockedMail = (overrides?: Partial): EmailResponse hasAttachment: faker.datatype.boolean(), size: faker.number.int({ min: 1024, max: 16384 }), attachments: faker.datatype.boolean() ? [getMockedAttachment()] : [], + isDraft: false, ...overrides, }); @@ -176,6 +177,7 @@ export const getMockedMails = (count = 3): EmailListResponse => ({ emails: Array.from({ length: count }, () => { const mail = getMockedMail(); return { + isDraft: false, id: mail.id, mailboxIds: mail.mailboxIds, threadId: mail.threadId, From b5e8b31284792df5ff0bcf22783e5c58fbed91d3 Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+xabg2@users.noreply.github.com> Date: Thu, 25 Jun 2026 15:20:49 +0200 Subject: [PATCH 3/5] chore(deps): bump sdk and UI versions --- package-lock.json | 16 ++++++++-------- package.json | 4 ++-- src/utils/format-emails/index.test.ts | 25 ++++++++++++++++++++++++- src/utils/format-emails/index.ts | 14 +++++++++----- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 694cfa2..8fed374 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,8 @@ "dependencies": { "@internxt/css-config": "^1.1.0", "@internxt/lib": "^1.4.1", - "@internxt/sdk": "^1.17.10", - "@internxt/ui": "^0.1.24", + "@internxt/sdk": "^1.17.11", + "@internxt/ui": "^0.1.25", "@phosphor-icons/react": "^2.1.10", "@reduxjs/toolkit": "^2.11.2", "@tailwindcss/vite": "^4.2.1", @@ -1455,18 +1455,18 @@ "license": "MIT" }, "node_modules/@internxt/sdk": { - "version": "1.17.10", - "resolved": "https://registry.npmjs.org/@internxt/sdk/-/sdk-1.17.10.tgz", - "integrity": "sha512-gxpXc70c9pYVcQepTDwyhkB6zJ8i7Ea87FJNArMYwIliRNJllsNjdwQ8DV/MqH6iVY5EqbOfNHP3ZfurCdCq/w==", + "version": "1.17.11", + "resolved": "https://registry.npmjs.org/@internxt/sdk/-/sdk-1.17.11.tgz", + "integrity": "sha512-t9rTaNNli2GdnFTEJPgcI2MAX2LouKzVbFIBj6QUVlCk7Ivcj+hbkkZqVlHDNTvsw3UObq2ehSNYWmzkMgcbfQ==", "license": "MIT", "dependencies": { "axios": "^1.16.0" } }, "node_modules/@internxt/ui": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/@internxt/ui/-/ui-0.1.24.tgz", - "integrity": "sha512-nPZ2kePXUA/1sT8Zi7BB7PlPHsZyWyupymcvSwUtt9UtegfK899I17zNqy0IXDwBeLHCXkIL9ZaASksnLUEn+A==", + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@internxt/ui/-/ui-0.1.25.tgz", + "integrity": "sha512-xtDHnzLA3ml4FBLuNY15wm8vPQ59J4qSzim7sb/bXKZpJqlpt+kHbDtpLzzB6P+/l1QthXVO4VGDH0m44rlpug==", "license": "MIT", "dependencies": { "@internxt/css-config": "1.1.0", diff --git a/package.json b/package.json index 64d3193..54c7d41 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,8 @@ "dependencies": { "@internxt/css-config": "^1.1.0", "@internxt/lib": "^1.4.1", - "@internxt/ui": "^0.1.24", - "@internxt/sdk": "^1.17.10", + "@internxt/sdk": "^1.17.11", + "@internxt/ui": "^0.1.25", "@phosphor-icons/react": "^2.1.10", "@reduxjs/toolkit": "^2.11.2", "@tailwindcss/vite": "^4.2.1", diff --git a/src/utils/format-emails/index.test.ts b/src/utils/format-emails/index.test.ts index 0a5df9c..567cb6b 100644 --- a/src/utils/format-emails/index.test.ts +++ b/src/utils/format-emails/index.test.ts @@ -28,11 +28,34 @@ describe('Formatting emails to list format', () => { expect(item.subject).toBe(emails[i].subject); expect(item.body).toBe(emails[i].preview); expect(item.read).toBe(emails[i].isRead); - expect(item.from.avatar).toBe(''); + expect(item.from).toEqual(expect.arrayContaining([expect.objectContaining({ avatar: '' })])); expect(DateService.formatMailTimestamp).toHaveBeenCalledWith(emails[0].receivedAt); }); }); + test('When the row carries thread participants, then those are used instead of the message sender', () => { + const { emails } = getMockedMails(1); + emails[0].from = [{ email: 'alice@inxt.me', name: 'Alice' }]; + (emails[0] as { participants?: { email: string; name?: string }[] }).participants = [ + { email: 'alice@inxt.me', name: 'Alice' }, + { email: 'bob@inxt.me', name: 'Bob' }, + ]; + + const result = formatEmailsToList(emails); + + expect(result?.[0].from.map((p) => p.name)).toEqual(['Alice', 'Bob']); + }); + + test('When the row has no participants, then the message sender is used as fallback', () => { + const { emails } = getMockedMails(1); + emails[0].from = [{ email: 'alice@inxt.me', name: 'Alice' }]; + delete (emails[0] as { participants?: unknown }).participants; + + const result = formatEmailsToList(emails); + + expect(result?.[0].from.map((p) => p.name)).toEqual(['Alice']); + }); + test('When a row is encrypted, then it uses the decrypted preview when available and never the ciphertext', () => { const { emails } = getMockedMails(2); emails.forEach((e) => { diff --git a/src/utils/format-emails/index.ts b/src/utils/format-emails/index.ts index f39c5a2..6f53ee2 100644 --- a/src/utils/format-emails/index.ts +++ b/src/utils/format-emails/index.ts @@ -1,18 +1,22 @@ import { DateService } from '@/services/date'; import type { EmailListResponse } from '@internxt/sdk/dist/mail/types'; +type EmailSummary = EmailListResponse['emails'][number]; + +const toParticipant = (sender: EmailSummary['from'][number]) => ({ + name: sender.name ?? sender.email.split('@')[0], + avatar: '', +}); + export const formatEmailsToList = ( listFolderEmails?: EmailListResponse['emails'], decryptedPreviews?: Record, ) => { return listFolderEmails?.map((mail) => ({ id: mail.id, - from: { - name: mail.from[0]?.name ?? mail.from[0]?.email ?? '', - avatar: '', - }, + from: (mail.participants ?? mail.from).map(toParticipant), subject: mail.subject, - createdAt: DateService.formatMailTimestamp(mail.receivedAt), + createdAt: DateService.formatMailTimestamp(mail.lastReceivedAt ?? mail.receivedAt), body: decryptedPreviews?.[mail.id] ?? (mail.encryption ? '' : mail.preview), read: mail.isRead, })); From f23280d38244dbc97813244e877e53e56a9783ca Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+xabg2@users.noreply.github.com> Date: Thu, 25 Jun 2026 15:26:05 +0200 Subject: [PATCH 4/5] fix: pass the correct props fro MessageCheap --- .../mail/components/tray/search/components/list/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/features/mail/components/tray/search/components/list/index.tsx b/src/features/mail/components/tray/search/components/list/index.tsx index 06bda4e..96f5ab5 100644 --- a/src/features/mail/components/tray/search/components/list/index.tsx +++ b/src/features/mail/components/tray/search/components/list/index.tsx @@ -42,7 +42,13 @@ const SearchEmailList = ({ mails, hasMoreItems, loading, onLoadMore, onMailSelec > {formattedMails.map((email) => (
- +
))} From ea1fef403092651f0fd1e6e87b564f20ed35f5c0 Mon Sep 17 00:00:00 2001 From: Xavier Abad <77491413+xabg2@users.noreply.github.com> Date: Thu, 25 Jun 2026 15:29:23 +0200 Subject: [PATCH 5/5] fix: add fallback for participants --- src/utils/format-emails/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/format-emails/index.ts b/src/utils/format-emails/index.ts index 6f53ee2..a6a8053 100644 --- a/src/utils/format-emails/index.ts +++ b/src/utils/format-emails/index.ts @@ -14,7 +14,7 @@ export const formatEmailsToList = ( ) => { return listFolderEmails?.map((mail) => ({ id: mail.id, - from: (mail.participants ?? mail.from).map(toParticipant), + from: (mail.participants?.length ? mail.participants : mail.from).map(toParticipant), subject: mail.subject, createdAt: DateService.formatMailTimestamp(mail.lastReceivedAt ?? mail.receivedAt), body: decryptedPreviews?.[mail.id] ?? (mail.encryption ? '' : mail.preview),