From 066d2e292dfe120cf6b27e7bf43a61409a280310 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Wed, 28 Oct 2026 02:32:29 +0100 Subject: [PATCH 01/64] fix: open text, Markdown and PDF previews in the Files demo instead of failing --- stores/__tests__/file-store.test.ts | 17 +++++++++++++++++ stores/file-store.ts | 8 ++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/stores/__tests__/file-store.test.ts b/stores/__tests__/file-store.test.ts index 651eb4a67..ec499372f 100644 --- a/stores/__tests__/file-store.test.ts +++ b/stores/__tests__/file-store.test.ts @@ -1019,3 +1019,20 @@ describe('file-store actions by id', () => { expect(useFileStore.getState().recentFiles.map(r => r.id)).toEqual(['2']); }); }); + +describe('file-store preview content', () => { + it('reads a file through the client, so the demo, which has no download URL, can preview it', async () => { + const client = makeMockClient([file('a', 'notes.txt', null)]); + Object.assign(client, { + getBlobDownloadUrl: () => 'data:application/octet-stream;demo-blob=b-a', + getAuthHeader: () => '', + fetchBlob: async (blobId: string) => new Blob([`content of ${blobId}`], { type: 'text/plain' }), + }); + useFileStore.getState().initClient(client); + await useFileStore.getState().navigate(null); + + const { blob, contentType } = await useFileStore.getState().getFileContent('notes.txt'); + expect(await blob.text()).toBe('content of b-a'); + expect(contentType).toBe('text/plain'); + }); +}); diff --git a/stores/file-store.ts b/stores/file-store.ts index 712bea0a1..bf4b1d832 100644 --- a/stores/file-store.ts +++ b/stores/file-store.ts @@ -1227,12 +1227,8 @@ export const useFileStore = create((set, get) => ({ const resource = resources.find(r => r.name === name); if (!resource?.blobId) throw new Error('No blob'); - const url = client.getBlobDownloadUrl(resource.blobId, resource.name, resource.contentType); - const response = await fetch(url, { - headers: { 'Authorization': client.getAuthHeader() }, - }); - if (!response.ok) throw new Error(`Failed to fetch file: ${response.status}`); - const blob = await response.blob(); + // Through the client, like getImageUrl: the demo has no download URL to fetch. + const blob = await client.fetchBlob(resource.blobId, resource.name, resource.contentType); return { blob, contentType: resource.contentType || 'application/octet-stream' }; }, From 893696791d2b16ec4ed1c16f835c7548fb48f429 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Wed, 28 Oct 2026 02:40:48 +0100 Subject: [PATCH 02/64] fix: keep the hidden Contacts and Files sidebar from covering the nav rail on tablets --- .../contacts/__tests__/contacts-app.test.tsx | 86 +++++++++++++++++++ components/contacts/contacts-app.tsx | 12 ++- .../file-browser-shared-folders.test.tsx | 9 ++ components/files/file-browser.tsx | 11 ++- 4 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 components/contacts/__tests__/contacts-app.test.tsx diff --git a/components/contacts/__tests__/contacts-app.test.tsx b/components/contacts/__tests__/contacts-app.test.tsx new file mode 100644 index 000000000..27749049f --- /dev/null +++ b/components/contacts/__tests__/contacts-app.test.tsx @@ -0,0 +1,86 @@ +import { act, fireEvent, render, screen, within } from '@testing-library/react'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import type { ContactCard } from '@/lib/jmap/types'; +import type { IJMAPClient } from '@/lib/jmap/client-interface'; + +// The whole Contacts app in local mode (no JMAP client): the stores hold the +// contacts, and the shell around it is stubbed out. + +vi.mock('next/navigation', () => ({ + useRouter: () => ({ push: vi.fn(), back: vi.fn() }), + useParams: () => ({ locale: 'en' }), + usePathname: () => '/en/contacts', + useSearchParams: () => new URLSearchParams(), +})); +vi.mock('@/components/layout/navigation-rail', () => ({ NavigationRail: () =>