From 1e8f64f36e1b2ab35c080d887468a4c2e7686920 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 1 Nov 2026 19:40:21 +0100 Subject: [PATCH] feat: show the mail skeleton while a mailto link opens --- app/(main)/protocol/mailto/page.tsx | 10 ++++++- .../__tests__/mailto-protocol-client.test.tsx | 26 +++++++++++++++++++ .../protocol/mailto-protocol-client.tsx | 14 +++++----- 3 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 components/protocol/__tests__/mailto-protocol-client.test.tsx diff --git a/app/(main)/protocol/mailto/page.tsx b/app/(main)/protocol/mailto/page.tsx index f2797d699..bef4cbd4c 100644 --- a/app/(main)/protocol/mailto/page.tsx +++ b/app/(main)/protocol/mailto/page.tsx @@ -3,6 +3,14 @@ import { MailtoProtocolClient } from "@/components/protocol/mailto-protocol-clie export default async function MailtoProtocolPage() { const t = await getTranslations("protocol_handlers"); + const tSidebar = await getTranslations("sidebar"); + const labels = { + mail: tSidebar("mail"), + calendar: tSidebar("calendar"), + contacts: tSidebar("contacts"), + files: tSidebar("files"), + settings: tSidebar("settings"), + }; - return ; + return ; } diff --git a/components/protocol/__tests__/mailto-protocol-client.test.tsx b/components/protocol/__tests__/mailto-protocol-client.test.tsx new file mode 100644 index 000000000..ef594d63d --- /dev/null +++ b/components/protocol/__tests__/mailto-protocol-client.test.tsx @@ -0,0 +1,26 @@ +import { render } from '@testing-library/react'; +import { afterEach, describe, expect, it, vi } from 'vitest'; + +vi.mock('@/lib/protocol-handlers/session', () => ({ + // Still asking an open tab whether it takes the message. + requestOpenMailtoInExistingClient: vi.fn(() => new Promise(() => {})), + savePendingMailto: vi.fn(), +})); + +const { MailtoProtocolClient } = await import('../mailto-protocol-client'); + +afterEach(() => { + window.history.replaceState(null, '', '/'); +}); + +describe('mailto handler page', () => { + // It hands over to the mail page, where the composer opens: it draws that + // page's skeleton instead of a line of text on a blank page. + it('draws the mail skeleton with the opening text as its status', () => { + window.history.replaceState(null, '', '/protocol/mailto?url=mailto%3Aa%40example.com'); + const { container } = render(); + const region = container.querySelector('[data-boot-skeleton="mail"]'); + expect(region).not.toBeNull(); + expect(region!.querySelector('[role="status"]')?.textContent).toBe('Opening email…'); + }); +}); diff --git a/components/protocol/mailto-protocol-client.tsx b/components/protocol/mailto-protocol-client.tsx index e6b8e2b1d..70bd5ed54 100644 --- a/components/protocol/mailto-protocol-client.tsx +++ b/components/protocol/mailto-protocol-client.tsx @@ -1,6 +1,8 @@ "use client"; import { useEffect } from "react"; +import { MailSkeleton } from "@/components/boot-skeleton/mail-skeleton"; +import type { RailLabels } from "@/components/boot-skeleton/shell"; import { parseMailto } from "@/lib/protocol-handlers/mailto"; import { requestOpenMailtoInExistingClient, savePendingMailto } from "@/lib/protocol-handlers/session"; import { useSettingsStore } from "@/stores/settings-store"; @@ -51,9 +53,11 @@ async function focusExistingClient() { interface MailtoProtocolClientProps { openingText: string; + /** The phone bottom bar's labels for the skeleton. */ + labels?: RailLabels; } -export function MailtoProtocolClient({ openingText }: MailtoProtocolClientProps) { +export function MailtoProtocolClient({ openingText, labels }: MailtoProtocolClientProps) { useEffect(() => { let cancelled = false; @@ -99,9 +103,7 @@ export function MailtoProtocolClient({ openingText }: MailtoProtocolClientProps) }; }, []); - return ( -
-

{openingText}

-
- ); + // The mail page this hands over to, where the composer opens: its skeleton, + // with the opening text as the status message. + return ; }