docs: tick the parity rows the platform pass fixed

This commit is contained in:
Linus Rath
2026-10-31 04:54:57 +01:00
parent 01dd7461f5
commit 88d19db6ef
6 changed files with 29 additions and 29 deletions
+4 -4
View File
@@ -60,12 +60,12 @@ Findings:
- What RN does: .maestro/subflows/stalwart-login-page.yaml types and taps Sign in, then inbox.yaml waits for the tab bar. The app doesn't depend on the order (oauth-native.ts never sends `prompt=consent`). The iOS e2e server (`vars.E2E_SERVER`) is the 0.16 server, so CI isn't affected today.
- Fix hint: optional `runFlow when visible "Allow"` tap at the end of the subflow.
- [ ] **A `consentRequired` answer reads as a wrong password** — `P3` — `bug`
- [x] **A `consentRequired` answer reads as a wrong password** — `P3` — `bug` — fixed in 1bef4719
- What Stalwart does: new answer `{type:'consentRequired', token}` and request `{type:'authConsent', token, query, consent}` in 1.0.1.
- What RN does: readStalwartLoginResponse maps it to invalid credentials (src/lib/totp-login.ts:344-380). Not reachable while `consent: true` is sent. The webmail has the same gap (lib/auth/stalwart-login-request.ts).
- Fix hint: answer it with `authConsent` and the same query, or a clear error.
- [ ] **The encrypt-on-upload copy no longer matches the server** — `P3` — `docs`
- [x] **The encrypt-on-upload copy no longer matches the server** — `P3` — `docs` — fixed in 853cb5d0 (comment; the live compose check is not written)
- What Stalwart does: 1.0.1 stores JMAP `$draft` messages and the Sent copy unencrypted (crates/email/src/message/ingest.rs:608-611).
- What RN does: no workaround for encrypted drafts; the comment at src/api/account-security.ts:249-254 says sent mail is encrypted too.
- Fix hint: fix the comment; a live check in live-stalwart-compose that a draft and the Sent copy read back as text with encryption on.
@@ -134,9 +134,9 @@ current lines is in "Webmail bugs" at the end of this file.
### UX audit deferred (docs/ux-audit-2026-10.md)
- [ ] **U-133 no way out of a hung "Signing in"** — `P2` — `missing` — src/screens/LoginScreen.tsx:218 swallows Back while busy, SigningInStep has no cancel; abortable sign-in, M
- [x] **U-133 no way out of a hung "Signing in"** — `P2` — `missing` — src/screens/LoginScreen.tsx:218 swallows Back while busy, SigningInStep has no cancel; abortable sign-in, M — fixed in 55d008a2
- [ ] **U-069 recipient suggestions hidden in phone landscape** — `P2` — `missing` — needs a full-screen picker, M (feature)
- [ ] **U-045 German Prev/Next read "Zurück"/"Weiter"** — `P3` — `bug` — override in locales/rn/de.json
- [x] **U-045 German Prev/Next read "Zurück"/"Weiter"** — `P3` — `bug` — override in locales/rn/de.json — fixed in 25f9b810
- [ ] **Plain-text mail dark background fixed at #0a0a0a** — `P3` — `partial` — src/lib/email-html.ts:963 ignores the theme
- [ ] **U-017 system alert vs Dialog, U-136 empty all-day strip, U-124 language order** — product calls, kept deferred unless you say otherwise
- U-016 scrim token: won't fix (no visible change). U-018 font scale resets navigation: React Native limitation. U-131: done (776447dd).
+1 -1
View File
@@ -171,7 +171,7 @@ RN toggles that are stored but never read (fix: either wire them or remove the c
- What RN does: `setupPushNotificationsInner` calls `native.getToken()` directly (not the guarded `getFcmToken`) so a Firebase rejection (`fcm_token_failed`, typical on de-Googled devices or right after `deleteToken()` during Disable) bubbles up raw; `registerWithRelay` throws `Relay register failed: <status>` without the body (`RN: src/lib/push-notifications.ts:312-313,215-217`); `pollVerificationCode` times out after 75 s with the same message for every cause (`:268`). The UI shows `error.message` verbatim (`NotificationSettings.tsx:95-99`). Disable then immediately Enable also races: `teardownPushNotificationsForAccount` deletes the FCM token when it was the last account (`:477-481`) and the next `getToken()` may be rejected until Firebase re-registers.
- Fix hint: read the relay response body into the error, distinguish "no Google Play services" (map to the UnifiedPush finding), skip `deleteToken()` unless the user disables push for all accounts, and log each phase (`token`, `relay`, `jmap create`, `verify`) so the settings pane can show which step failed. Needs a repro on the reporter's device to confirm the exact failure.
- [ ] **iOS push missing entirely** — `P2` — `missing` — deferred: needs an APNs transport in the relay + iOS token module; the settings pane now states Android-only
- [x] **iOS push missing entirely** — `P2` — `missing` — deferred: needs an APNs transport in the relay + iOS token module; the settings pane now states Android-only — fixed in f62849d1
- What WEB does: Web Push works on iOS 16.4+ once installed to the home screen (`lib/web-push.ts:157-164,394-398`).
- What RN does: `getNative()` returns `null` off Android and setup throws "Push notifications require Android" (`RN: src/lib/push-notifications.ts:125-128,303-304`); the relay has only FCM and Web Push transports (`repos/relay/README.md` endpoints).
- Fix hint: needs an APNs transport in the relay (`POST /api/push/register/apns` storing a device token, HTTP/2 to `api.push.apple.com` with a `.p8` key) plus an iOS native module (or `expo-notifications`) for the token and a Notification Service Extension to fetch sender/subject over JMAP. Content-blind payload stays the same.
+1 -1
View File
@@ -231,7 +231,7 @@ RN's JMAP client (`src/api/jmap-client.ts`, 615 lines) is a thin transport: sess
- What RN does: `onShouldStartLoadWithRequest` returns `true` for any `data:` URL (`src/components/EmailBodyView.tsx:715-718`), so `<a href="data:text/html,...">` replaces the message with attacker HTML rendered inside the app chrome (phishing form); the WebView is otherwise well hardened (`originWhitelist=['about:blank']`, file access off, `mixedContentMode="never"`, `setSupportMultipleWindows={false}`, `incognito`, `domStorageEnabled={false}`, `EmailBodyView.tsx:675-712`).
- Fix hint: allow `data:` only for `request.isTopFrame === false` or block it entirely (the body is loaded via `source={{html}}`, not a data: URL).
- [ ] **No screenshot / recent-apps protection option** — `P3` — `missing`
- [ ] **No screenshot / recent-apps protection option** — `P3` — `missing` — not done: needs a new native module (expo-screen-capture)
- What WEB does: N/A.
- What RN does: no `FLAG_SECURE`/`expo-screen-capture` anywhere; mail content appears in the Android recents thumbnail.
- Fix hint: optional "Hide content in app switcher" setting using `expo-screen-capture` `preventScreenCaptureAsync`.
+7 -7
View File
@@ -70,15 +70,15 @@ Stalwart 1.0.0 items found in the same pass (1.0 SSO scopes, Files on 1.0, #1385
- What RN does: clearAllFeatureStores (`src/stores/auth-store.ts:126-134`) never clears `src/stores/search-history-store.ts:44`.
- Fix hint: clear it, and check the other persisted stores in the same pass.
- [ ] **Internationalized domains** — `P3` — `missing`
- [x] **Internationalized domains** — `P3` — `missing` — fixed in f5875f87 (sign-in, discovery, account rows; the composer's recipient check goes with the compose branch)
- What WEB does: `lib/idn.ts:25` (#1100). What RN does: ASCII-only checks (`src/lib/recipients.ts:31`, `src/lib/server-discovery.ts:78`).
- Fix hint: toASCII on the wire, Unicode on screen.
- [ ] **Sign-out doesn't end the identity provider session** — `P3` — `missing`
- [ ] **Sign-out doesn't end the identity provider session** — `P3` — `missing` — not done: needs a decision; the app keeps the shared browser session for a first sign-in on purpose, an ephemeral session helps on iOS only, and end_session needs the id_token the app does not keep plus a registered post-logout redirect
- What WEB does: `app/api/auth/token/route.ts:90` (#905). What RN does: `src/lib/oauth.ts:219`, `src/lib/oauth-native.ts:158`.
- Fix hint: open end_session_endpoint, or use an ephemeral browser session.
- [ ] **Explain a refused token exchange on TOTP sign-in** — `P3` — `partial`
- [x] **Explain a refused token exchange on TOTP sign-in** — `P3` — `partial` — fixed in 37e72c12
- What WEB does: `stores/auth-store.ts:153`, `:1432`. What RN does: `src/lib/totp-login.ts:148`, `:156` throw token_exchange_failed; `src/lib/login-errors.ts:129` has no copy.
- [ ] **Shared account settings scope for calendar and contacts** — `P3` — `missing` (old 01 item)
@@ -419,16 +419,16 @@ Stalwart 1.0.0 items found in the same pass (1.0 SSO scopes, Files on 1.0, #1385
- What WEB does: Mailbox/AddressBook/Calendar shareWith in one batch (`lib/delegation/delegation.ts:96`).
- What RN does: no mailbox sharing (`src/components/settings/FolderSettings.tsx`).
- [ ] **Shared accounts treated as capable when their capabilities say otherwise (#1239)** — `P3` — `bug`
- [x] **Shared accounts treated as capable when their capabilities say otherwise (#1239)** — `P3` — `bug` — fixed in 67732348 (filters and auto-reply; contacts and files go with the calendar branch)
- What WEB does: `lib/jmap/client.ts:6431`. What RN does: `src/api/contacts.ts:76`, `src/api/files.ts:98`, `src/api/sieve.ts:43`, `src/api/vacation.ts:77`.
- [ ] **Storage quota ignores softLimit (#1242)** — `P3` — `bug`
- [x] **Storage quota ignores softLimit (#1242)** — `P3` — `bug` — fixed in af86339b
- What WEB does: `lib/quota.ts:32`. What RN does: `src/api/quota.ts:50`.
- [ ] **Push extras: Inbox-only switch (#983), one ring per message (#1141), generic notification when the lookup fails (#1041), re-check on return (#1148)** — `P3` — `missing`/`partial`
- [ ] **Push extras: Inbox-only switch (#983), one ring per message (#1141), generic notification when the lookup fails (#1041), re-check on return (#1148)** — `P3` — `missing`/`partial` — Inbox-only done in 1821fda7, generic notification in f6e6f8f5, re-check on return in 327b2cae; not done: one ring per message (#1141) needs a change in the Android notification builder (BulwarkFcmModule.kt) and a device
- What RN does: `src/lib/notification-mutes.ts`, `src/lib/push-background-task.ts:468-488`, `:518`, `src/components/settings/NotificationSettings.tsx:115-134`.
- [ ] **Settings extras: language search, date locale override, font size everywhere, nav-bar icon style, sidebar apps never rendered, screenshot protection, Flat fields theme** — `P3` — `missing`/`partial` (old 08/09 items)
- [ ] **Settings extras: language search, date locale override, font size everywhere, nav-bar icon style, sidebar apps never rendered, screenshot protection, Flat fields theme** — `P3` — `missing`/`partial` (old 08/09 items) — screenshot protection not done: needs a new native module (expo-screen-capture); text size widened in c9b3bdfe
- What RN does: `src/components/settings/LanguageSettings.tsx:87`, `src/theme/dynamic.ts:48`, `src/lib/sidebar-apps.ts:10`, `src/theme/builtin-themes.ts:18`.
- [ ] **Search snippets, size filter, mail folder sharing, global search (1.10.0)** — `P3` — `missing`
+14 -14
View File
@@ -130,22 +130,22 @@ Classification: `bug` = fixed in the webmail, still live in the app; `missing`;
### Accessibility
- [ ] **No Accessibility page or presets** — `P2` — `missing`
- [x] **No Accessibility page or presets** — `P2` — `missing` — fixed in c9b3bdfe
- What WEB does: Settings > Accessibility with presets (screen reader, low vision, dyslexia, focus, motor, motion, hearing) and text size 87.5% to 200% (`components/settings/accessibility-settings.tsx`, `lib/accessibility-presets.ts:23`, `lib/accessibility-settings.ts:13-22`; 8008393e5, 0731d24f6).
- What RN does: no page (`src/screens/SettingsScreen.tsx:150-174`); text size 0.92/1/1.12 (`src/theme/dynamic.ts:14-18`). Text follows the system font scale; reduce motion follows the system (`src/theme/dynamic.ts:88-120`).
- Fix hint: a page that groups the existing switches, widens the text size range and takes the presets that make sense on a phone.
- [ ] **Form fields have no name for screen readers** — `P2` — `bug`
- [x] **Form fields have no name for screen readers** — `P2` — `bug` — fixed in ff5a8c91 (settings and sign-in; contact form and event editor are listed for their branches)
- What WEB does: every field has a label tied to it (`components/ui/field.tsx:42`; ef13556a0, 0e2a05e0e, dcc21acd0, 4e2affe40, f73643bbb).
- What RN does: the shared Input draws its label as separate Text (`src/components/Input.tsx:40-55`); the Security password and code fields have no label at all (`src/components/settings/AccountSecuritySettings.tsx:181`, `:191`, `:198`); contact fields rely on placeholders (`src/screens/ContactFormScreen.tsx:823-834`); the reminder field's only name is "30" (`src/components/calendar/EventModal.tsx:772`, `:817`).
- Fix hint: Input passes its label as accessibilityLabel; every bare TextInput gets one.
- [ ] **Toasts and Undo are not read out on iOS; the Undo bar isn't read out anywhere** — `P2` — `bug`
- [x] **Toasts and Undo are not read out on iOS; the Undo bar isn't read out anywhere** — `P2` — `bug` — fixed in d812caf0, 01dd7461
- What WEB does: live regions and announcements for toasts, loads, new mail, saved drafts and offline (`lib/announce.ts:124`, `components/ui/toast.tsx:471`; 36d13b76b, e2fc2c750, 23813be9b, d713d31af, 4c1f24947).
- What RN does: no `announceForAccessibility` call in src; ToastHost and OfflineBanner use accessibilityLiveRegion, which only Android honours (`src/components/ToastHost.tsx:194`, `src/components/OfflineBanner.tsx:33`); the Undo bar has neither (`src/components/UndoSnackbar.tsx:129-160`).
- Fix hint: announce through AccessibilityInfo on iOS, live region on the Undo bar.
- [ ] **Grey text is below 4.5:1 contrast** — `P2` — `bug`
- [x] **Grey text is below 4.5:1 contrast** — `P2` — `bug` — fixed in 11522995
- What WEB does: every built-in theme at 4.5:1 or more, with a contrast test (`lib/builtin-themes.ts:16`, `lib/__tests__/theme-contrast.test.ts`; 9f6c7b99c).
- What RN does: textMuted is `#94a3b8` on white in the light theme (2.56:1) and `#737373` in dark (4.18:1 on the background, 3.19:1 on surfaces) (`src/theme/tokens.ts:236`, `:140`); used in 456 places, also as placeholder colour.
- Fix hint: adjust both, add a contrast test over tokens.ts and builtin-themes.ts.
@@ -155,21 +155,21 @@ Classification: `bug` = fixed in the webmail, still live in the app; `missing`;
- What RN does: month day cells read only the number and the dots are silent (`src/components/calendar/MonthView.tsx:199-240`); event blocks have no role, label or date (`src/components/calendar/EventBlock.tsx:72-104`).
- Fix hint: label day cells with date and event count; event blocks get a button role and a label with title, date and time.
- [ ] **No reading fonts, text spacing, more contrast or underlined links** — `P3` — `missing`
- [ ] **No reading fonts, text spacing, more contrast or underlined links** — `P3` — `missing` — not done: own feature, not in this run
- What WEB does: Atkinson Hyperlegible Next, Lexend, OpenDyslexic, spacing settings, more contrast, underlined links (`lib/accessibility-settings.ts:53-68`; 8008393e5, da215b8c0).
- What RN does: none of these settings (`src/stores/settings-store.ts`).
- Fix hint: the dyslexia font and line spacing first.
- [ ] **No colour-blind palette and no names next to colours** — `P3` — `missing`
- [ ] **No colour-blind palette and no names next to colours** — `P3` — `missing` — not done: own feature, not in this run
- What WEB does: a colour-blind-safe palette, a tag's letter in its dot, the calendar's name on events (cf4d02b49, d5d7d39ef, 396290003).
- What RN does: colours only (`src/components/calendar/EventBlock.tsx:72-104`, `src/components/calendar/MonthView.tsx:233-240`).
- [ ] **Toast and Undo times are fixed and short** — `P3` — `missing`
- [x] **Toast and Undo times are fixed and short** — `P3` — `missing` — fixed in c9b3bdfe
- What WEB does: one duration setting, 5 to 60 s or until closed (`lib/toast-duration.ts:7-15`; 8d2ebccf0, be5e2dbc5).
- What RN does: 5 s, errors 10 s (`src/stores/toast-store.ts:44`), Undo 5 s (`src/components/UndoSnackbar.tsx:15`).
- Fix hint: a duration setting and a longer default while a screen reader is on.
- [ ] **No plain language and no explanations for technical terms** — `P3` — `missing`
- [ ] **No plain language and no explanations for technical terms** — `P3` — `missing` — not done: own feature, not in this run
- What WEB does: simpler English and German wording (`i18n/plain-language.ts`; bb739c744, 9861af6be, 5ffecf692) and term explanations (`components/ui/term.tsx`; 724c45d6e).
- What RN does: none (`src/i18n/index.ts:79-140`).
@@ -180,12 +180,12 @@ Classification: `bug` = fixed in the webmail, still live in the app; `missing`;
### Accounts and sign-in
- [ ] **The two-factor prompt shows as a red error (#1422)** — `P3` — `bug`
- [x] **The two-factor prompt shows as a red error (#1422)** — `P3` — `bug` — fixed in b205c13a
- What WEB does: an info box with role status, no shake (`app/(main)/[locale]/login/page.tsx:1393`; 141190708).
- What RN does: TotpRequiredError sets a notice (`src/screens/LoginScreen.tsx:389-391`) shown in the error tone (`src/screens/login/PasswordStep.tsx:151`, `src/screens/login/LoginNotice.tsx:15-24`).
- Fix hint: `tone="info"` for the TOTP step.
- [ ] **Two accounts with the same name look alike in the switcher** — `P3` — `bug`
- [x] **Two accounts with the same name look alike in the switcher** — `P3` — `bug` — fixed in 505fbc59 (Settings > Account and the shared accountRowText; the switcher row in SidebarDrawer goes with the reader branch; the port waits for question 9)
- What WEB does: the address becomes the title when another account shares the name (`lib/account-utils.ts:159`; 3bb9f7df8).
- What RN does: name as title, address below (`src/components/SidebarDrawer.tsx:884-891`, `src/components/settings/AccountSettings.tsx:186`). Report question 9 (port in the label) is the same area.
- Fix hint: port accountRowText.
@@ -197,11 +197,11 @@ Classification: `bug` = fixed in the webmail, still live in the app; `missing`;
### Settings
- [ ] **The density sample shows previews while "Show preview text" is off** — `P3` — `bug`
- [x] **The density sample shows previews while "Show preview text" is off** — `P3` — `bug` — fixed in c8f417dd
- What WEB does: c166651d1 (`components/settings/appearance-settings.tsx:14-16`).
- What RN does: showPreview comes from the density only (`src/components/settings/AppearanceSettings.tsx:11-15`, `:24-27`).
- [ ] **The identity editor closes without asking about unsaved changes** — `P3` — `bug`
- [x] **The identity editor closes without asking about unsaved changes** — `P3` — `bug` — fixed in 7ecac4ab
- What WEB does: identity, rule and Sieve editors ask (`components/settings/use-discard-changes.tsx`; 9d3ae2203).
- What RN does: Close, Cancel and Back call closeEditor (`src/components/settings/IdentitySettings.tsx:122`, `:304`, `:311`, `:389`). FilterRuleModal already asks.
@@ -231,12 +231,12 @@ Classification: `bug` = fixed in the webmail, still live in the app; `missing`;
### i18n
- [ ] **No Thai or Estonian, and the vendored catalogs are stale** — `P3` — `missing`
- [x] **No Thai or Estonian, and the vendored catalogs are stale** — `P3` — `missing` — fixed in d23af55e, e8bc54eb
- What WEB does: 29 languages, Thai new in 1.19.0 (#1379, be52744a5; `i18n/routing.ts:15`).
- What RN does: 27 (`src/i18n/index.ts:25-53`); `locales/et/common.json` is vendored but not registered; no `th`. German lacks 1283 of the webmail's keys (last sync 7c914183).
- Fix hint: `node scripts/sync-locales.mjs`, register th and et, add overlays.
- [ ] **Some German phone strings still say "du"** — `P3` — `bug`
- [x] **Some German phone strings still say "du"** — `P3` — `bug` — fixed in 25f9b810
- What WEB does: formal everywhere (7b720dd22).
- What RN does: 12 overlay strings (`locales/rn/de.json`, e.g. settings.privacy.*, settings.backup.description); 18 more in the stale vendored catalog, which the sync fixes.
- Fix hint: rewrite with "Sie".
+2 -2
View File
@@ -364,7 +364,7 @@ carries the commit that fixed it.
- [x] **U-044 · P2 · PP (German, 1.3) · The toolbar overflows and pushes More off screen.** *(fixed in 975ed08)*
- "Stern hinzufügen" is long (`d_de_thread`).
- Fix: drop the toolbar labels when they don't fit; keep the a11y labels.
- [ ] **U-045 · P3 · German · Prev/Next read "Zurück"/"Weiter".** *(deferred: the strings come from the vendored webmail catalog (locales/de/common.json); the fix belongs in the webmail's German translation)*
- [x] **U-045 · P3 · German · Prev/Next read "Zurück"/"Weiter".** *(deferred: the strings come from the vendored webmail catalog (locales/de/common.json); the fix belongs in the webmail's German translation)* — fixed in 25f9b810
- "Zurück" reads as Back. "Allen antworten" wraps in the bottom bar.
- Fix: "Vorherige"/"Nächste" (locale), and a single line with shrinking text.
- [x] **U-046 · P2 · all · The tag sheet doesn't scroll.** *(fixed in b59d00f)*
@@ -510,7 +510,7 @@ carries the commit that fixed it.
- [x] **U-132 · P2 · all · The password step's return keys don't move on.** *(fixed in ae1ef5b)*
- Username has no `next`. With TOTP on, the password field shows Next and does nothing.
- Where: `PasswordStep.tsx:56,78`.
- [ ] **U-133 · P2 · all · There is no way out while "Signing in" hangs.** *(deferred: a cancel needs an abortable sign-in in the auth flow, which is more than a UI fix; see product questions)* Hardware back is
- [x] **U-133 · P2 · all · There is no way out while "Signing in" hangs.** — fixed in 55d008a2 *(deferred: a cancel needs an abortable sign-in in the auth flow, which is more than a UI fix; see product questions)* Hardware back is
swallowed. Where: `SigningInStep.tsx`, `LoginScreen.tsx:165`.
### Found in the re-walk