This commit is contained in:
Linus Rath
2026-05-01 21:35:53 +02:00
18 changed files with 63 additions and 11 deletions
+45 -9
View File
@@ -40,17 +40,53 @@ export async function GET(request: NextRequest) {
return NextResponse.json({ error: 'Incomplete JMAP session' }, { status: 502 });
}
// Find the inbox, then pull the most recent unread message in it. We use
// a single batched JMAP request with back-references so this round-trip
// is one POST regardless of how many messages exist.
const inboxRes = await fetch(apiUrl, {
method: 'POST',
headers: {
Authorization: creds.authHeader,
'Content-Type': 'application/json',
},
body: JSON.stringify({
using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail'],
methodCalls: [
[
'Mailbox/query',
{ accountId, filter: { role: 'inbox' }, limit: 1 },
'mb',
],
],
}),
});
if (!inboxRes.ok) {
return NextResponse.json({ error: 'JMAP mailbox query failed' }, { status: 502 });
}
const inboxData = (await inboxRes.json()) as {
methodResponses: [string, Record<string, unknown>, string][];
};
const inboxBody = inboxData.methodResponses.find(
([method]) => method === 'Mailbox/query',
)?.[1] as { ids?: string[] } | undefined;
const inboxId = inboxBody?.ids?.[0];
if (!inboxId) {
return NextResponse.json({
email: null,
unreadTotal: 0,
}, {
headers: {
'Cache-Control': 'no-store',
},
});
}
// Pull the most recent unread message from the resolved Inbox mailbox.
const requestBody = {
using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail'],
methodCalls: [
[
'Mailbox/query',
{ accountId, filter: { role: 'inbox' }, limit: 1 },
'mb',
],
[
'Email/query',
{
@@ -58,7 +94,7 @@ export async function GET(request: NextRequest) {
filter: {
operator: 'AND',
conditions: [
{ inMailbox: { resultOf: 'mb', name: 'Mailbox/query', path: '/ids/0' } },
{ inMailbox: inboxId },
{ notKeyword: '$seen' },
],
},
+2 -2
View File
@@ -283,7 +283,7 @@ export function EmailList({
<div className="px-4 py-2 border-b bg-accent/30 border-border flex items-center justify-between">
<div className="flex items-center gap-2 animate-in fade-in slide-in-from-left-3 duration-300">
<span className="text-sm font-medium text-foreground">
{selectedEmailIds.size} {selectedEmailIds.size === 1 ? 'email' : 'emails'} selected
{t('batch_actions.selected_messages', { count: selectedEmailIds.size })}
</span>
</div>
<div className="flex items-center gap-1 animate-in fade-in slide-in-from-right-3 duration-300">
@@ -338,7 +338,7 @@ export function EmailList({
disabled={isProcessing}
className="text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50"
>
Cancel
{t('batch_actions.clear_selection')}
</Button>
</div>
</div>
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "Vybrat zprávy",
"select_all": "Vybrat vše",
"selected_messages": "{count, plural, one {Vybraný 1 e-mail} other {Vybrané # e-maily}}",
"mark_read": "Označit jako přečtené",
"mark_unread": "Označit jako nepřečtené",
"delete": "Odstranit",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "E-Mails auswählen",
"select_all": "Alle auswählen",
"selected_messages": "{count, plural, one {1 E-Mail} other {# E-Mails}} ausgewählt",
"mark_read": "Als gelesen markieren",
"mark_unread": "Als ungelesen markieren",
"delete": "Löschen",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "Select emails",
"select_all": "Select all",
"selected_messages": "{count, plural, one {1 email} other {# emails}} selected",
"mark_read": "Mark as read",
"mark_unread": "Mark as unread",
"delete": "Delete",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "Seleccionar correos",
"select_all": "Seleccionar todo",
"selected_messages": "{count, plural, one {1 correo electrónico seleccionado} other {# correos electrónicos seleccionados}}",
"mark_read": "Marcar como leído",
"mark_unread": "Marcar como no leído",
"delete": "Eliminar",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "Sélectionner les e-mails",
"select_all": "Tout sélectionner",
"selected_messages": "{count, plural, one {1 e-mail sélectionné} other {# e-mails sélectionnés}}",
"mark_read": "Marquer comme lu",
"mark_unread": "Marquer comme non lu",
"delete": "Supprimer",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "Seleziona e-mail",
"select_all": "Seleziona tutto",
"selected_messages": "{count} email {count, plural, one {selezionata} other {selezionate}}",
"mark_read": "Segna come letto",
"mark_unread": "Segna come non letto",
"delete": "Elimina",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "メールを選択",
"select_all": "すべて選択",
"selected_messages": "{count, plural, one {1} other {#}}通のメールが選択されました",
"mark_read": "既読にする",
"mark_unread": "未読にする",
"delete": "削除",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "메일 선택",
"select_all": "모두 선택",
"selected_messages": "이메일 {count, plural, one {1개} other {#건}} 선택됨",
"mark_read": "읽은 상태로 표시",
"mark_unread": "읽지 않은 상태로 표시",
"delete": "삭제",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "Atlasīt vēstules",
"select_all": "Atlasīt visas",
"selected_messages": "{count, plural, one {Izvēlēta 1 e-pasta vēstule} other {Izvēlētas # e-pasta vēstules}}",
"mark_read": "Atzīmēt kā izlasītu",
"mark_unread": "Atzīmēt kā nelasītu",
"delete": "Dzēst",
+1
View File
@@ -185,6 +185,7 @@
"select": "E-mails selecteren",
"select_all": "Alles selecteren",
"mark_read": "Markeren als gelezen",
"selected_messages": "{count, plural, one {1 e-mail} other {# e-mails}} geselecteerd",
"mark_unread": "Markeren als ongelezen",
"delete": "Verwijderen",
"delete_confirm_title": "E-mails verwijderen",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "Zaznacz wiadomości",
"select_all": "Zaznacz wszystkie",
"selected_messages": "Wybrano {count, plural, one {1 wiadomość} other {# wiadomości}} e-mail",
"mark_read": "Oznacz jako przeczytane",
"mark_unread": "Oznacz jako nieprzeczytane",
"delete": "Usuń",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "Selecionar e-mails",
"select_all": "Selecionar tudo",
"selected_messages": "{count, plural, one {1 e-mail selecionado} other {# e-mails selecionados}}",
"mark_read": "Marcar como lido",
"mark_unread": "Marcar como não lido",
"delete": "Excluir",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "Выбрать письма",
"select_all": "Выбрать все",
"selected_messages": "Выбрано {count, plural, one {1 письмо} other {# письма}}",
"mark_read": "Отметить прочитанными",
"mark_unread": "Отметить непрочитанными",
"delete": "Удалить",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "E-postaları seç",
"select_all": "Tümünü seç",
"selected_messages": "{count, plural, one {1 e-posta} other {# e-posta}} seçildi",
"mark_read": "Okundu olarak işaretle",
"mark_unread": "Okunmadı olarak işaretle",
"delete": "Sil",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "Виберіть електронні листи",
"select_all": "Вибрати все",
"selected_messages": "Вибрано {count, plural, one {1 електронний лист} other {# електронні листи}}",
"mark_read": "Позначити як прочитане",
"mark_unread": "Позначити як непрочитане",
"delete": "Видалити",
+1
View File
@@ -184,6 +184,7 @@
"batch_actions": {
"select": "选择邮件",
"select_all": "全选",
"selected_messages": "已選取 {count, plural, one {1} other {#}} 封電子郵件",
"mark_read": "标记为已读",
"mark_unread": "标记为未读",
"delete": "删除",