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),