From aba5c70b3e5becdb48ed9a0a21f767ab624a85bc Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 2 Oct 2026 13:45:57 +0200 Subject: [PATCH] docs: record the widget checks after the fixes on Stalwart 0.16 and 1.0 #52 Adds the phase 2 and 3 results to the verification checklist: the corrections to phase 1 (two link failures were the test rig), every action and link re-run on both servers, which commit fixes which cause, the layout review and what is still open. --- docs/widgets-verification.md | 159 +++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/docs/widgets-verification.md b/docs/widgets-verification.md index 98041244..eb315765 100644 --- a/docs/widgets-verification.md +++ b/docs/widgets-verification.md @@ -153,3 +153,162 @@ widget's own Reply, card and event taps were also tapped on the launcher. event link opens the Calendar tab and nothing else (or, with an id that exists in both accounts, the wrong event). In the foreground the event link landed on the Inbox. + +## Corrections to phase 1 + +- **L1 and L2 were faults of the test rig, not of the app.** `adb shell am + start -d ` handed the URL to the device shell unquoted, and the shell + cut it at the first `&`: Reply lost `&action=reply` (and `&thread=`), and + "Send wishes" lost `&subject=`. Quoted, Reply opens the composer over the + message and the subject arrives, cold, backgrounded and in the foreground, + on 0.16 and 1.0. +- **L4 is two app limits, not a widget fault.** A message link offline needs + the message on the device: with the offline mail cache off (the default) + a message is not kept across app starts and the reader says "Failed to + load email". A link for the other account needs the server for the account + switch, so offline it says "Couldn't open this". +- **R1 had a second half.** Starting the background refresh directly (no + timer) was not enough: fetch resolves every response through a zero-delay + timer, and with the activity paused the first request went out and its + answer was never read. The refresh now runs in a headless task of its own. + +## After the fixes (phase 2) + +Same rig. 1.0 was reached as `http://127.0.0.1:18191` through `adb +reverse`, so its accounts (`usera@127.0.0.1`) sit next to the 0.16 ones; +that path does not go through the emulator's network, so "offline" on 1.0 +was simulated with the proxy refusing `Email/set` (503) instead of airplane +mode. + +### Background actions + +| Action | BG | COLD | OFF | ACC2 | DBL | UPD | 1.0 | Notes | +|---|---|---|---|---|---|---|---|---| +| archive | pass | pass | pass | n/a | pass | pass | pass | offline: the message comes back on the card with "Couldn't archive. Tap to try again."; the tap archives it once online | +| trash | pass | pass | pass | n/a | pass | pass | pass | offline: "Couldn't delete. Tap to try again."; the tap moves it to Trash once online | +| triageNext | pass | pass | pass | n/a | pass | pass | pass | local only, no server call | +| rsvp | pass | pass | pass | pass (unit test) | not run | not run | call passes, see 1.0 below | offline: the invitation stays with "Couldn't send your answer"; the retry accepts it. DBL and UPD not run on the device: they go through the same serial lane and pending overlay as archive and the tick, and `actions.test.ts` covers both | +| toggleTask | pass | pass | pass | pass (unit test) | pass | not run | call passes, see 1.0 below | two taps 150 ms apart send `completed` twice; offline the tick reverts with a notice naming the task, and tapping the notice once online sends one `CalendarEvent/set` and the task shows done. UPD not run on the device (see rsvp) | +| any button | pass | pass | – | – | – | – | – | request codes unique (107 click intents, 107 codes) | + +DBL for archive: two taps 150 ms apart still both name the message the +drawing showed (the second is a harmless repeat); taps a second apart act on +consecutive messages. UPD: with `Email/set` delayed 4 s, two archives 1.5 s +apart leave the card on "5 of 5" for the whole run and nothing comes back +(before: the first archive's refresh brought the second message back). Trash +behaves the same: a double tap names the shown message twice, and two +deletes 1.5 s apart with `Email/set` delayed move two different messages +and nothing comes back. + +### Deep links + +| Link | FG | BG | COLD | OFF | ACC2 | 1.0 | +|---|---|---|---|---|---|---| +| inbox, unified, scheduled, search, compose | pass | pass | pass | pass | n/a | pass | +| message | pass | pass | pass | pass with the offline cache (L4) | pass | pass | +| reply | pass | pass | pass | pass with the offline cache (L4) | pass | pass | +| draft | pass | pass | pass | only if opened before (L4) | pass | not run (same route as message) | +| compose with to + subject | pass | pass | pass | not run (no server involved) | n/a | pass | +| calendar | pass | pass | pass | pass (no crash) | n/a | opens; see 1.0 below | +| event | pass | pass | pass | opens the Calendar tab; the event needs the server | pass (switches account) | see 1.0 below | +| contacts, settings | pass | pass | pass | pass (Vacation settings say the server does not support it while offline) | n/a | pass | +| files | pass | pass | pass | pass (no crash; the screen shows the raw network error) | n/a | pass | + +### Refresh + +| Case | Result | +|---|---| +| Account switched in the app, then Home | pass: the widgets show the new account within a second, both directions | +| App goes to the background | pass | +| Cold periodic update | pass: mail, calendar, tasks, files and scheduled (`appDataAt` set) | +| OAuth account with an expired access token, cold | pass: userb's token had expired an hour after sign-in; the cold refresh renewed it and loaded userb's inbox preview | + +### Stalwart 1.0 + +- Mail actions and links pass: the archive race, a refused write with its + notice and retry, trash, triage next, message and reply links cold and + backgrounded, and the inbox, unified, scheduled, search, compose, + calendar, contacts, files and settings links. +- The app on main does not load calendar events or tasks from 1.0: its + `CalendarEvent/get` asks for `recurrenceOverrides` together with + `utcStart`/`utcEnd`, which 1.0 refuses as a whole (`invalidArguments`). + The unmerged `feat/stalwart-1.0` branch fixes that. Until it is merged the + calendar and task widgets are empty against 1.0. The widgets' own calls + were sent to 1.0 directly: the task update (`progress`, + `percentComplete`) and the RSVP patch both come back `updated`. + +### Causes and their fixes + +| Cause | Commit | +|---|---| +| A0 dead buttons (shared request codes) | `fix: stop widget buttons from going dead when two are drawn in the same millisecond` | +| A3 refresh brings an archived message back, lost concurrent edits, out-of-order redraws | `fix: keep a widget archive when a second tap's refresh lands before it` | +| A1, A5 failures looked like success, no feedback | the commit above (a failed op is dropped) and `feat: show on the widget when an action could not reach the server` | +| A2, A4 tick flips back, item of another account | `fix: send widget RSVPs and task ticks to the item the tap was made on` | +| A6 22dp tick | `fix: make widget task ticks easy to hit` | +| R1 no refresh on leaving the app | `fix: refresh the widgets when the app goes to the background`, `fix: finish the widget refresh after leaving the app` | +| R2 cold refresh skipped calendar and tasks | `fix: load calendar and tasks into the widgets when the app is not running` | +| L3 offline crash from calendar and files widgets | `fix: keep the app from closing when a calendar widget opens it offline`, `fix: keep the app from closing when a files widget opens it offline` | +| L5 event links without account | `fix: open a widget's event in the account it belongs to` | + +## Phase 3: layout review + +All 32 widgets were drawn through a temporary in-app gallery +(`WidgetPreview`, removed again) at their smallest, default and largest +(4x5) size with sample data, light and dark, German, very long names and +counts (five-digit counts, 50-character names and subjects), empty data, +and the loading, signed-out, not-yet-loaded and unsupported states, and +with the tap areas highlighted. Fixed: + +- The Inbox header at two cells pushed the compose button off; header + titles now give way to the buttons, and the search icon goes at that + width. +- A long sender pushed the triage card's time and position out. +- German labels were cut on the triage buttons ("Archivie", "Lösc"); they + drop to icons when they do not fit. +- Five-digit counts overflowed Unread count, Tag and Mail and next, and the + total wrapped a character per line; counts from 10,000 are shortened. +- "ungelesen" was cut in Mail and next; it moves under the number. +- Buttons under 48dp (32–36dp) get a transparent 48dp tap area. +- The Month arrows and the Vacation switch looked like controls but only + opened the app. +- Folder counts with no folders showed a bare heading. +- A finished task past its due date read "Overdue". +- The triage card and Latest message did not use their height for the + preview. +- A failure notice about a long task title cut off "Tap to try again". +- Widget strings were English in every language; German added. +- Avatars showed initials only, worked out differently from the webmail. + Initials now follow the webmail everywhere in the app (first letters of + the first and last name, leading punctuation and emoji skipped), and the + widget avatars show the contact photo or the sender's logo first, as the + app's list does. Logos come from the same favicon service, are fetched + when the snapshot is built and are cached for offline drawing; a logo + over 40 KB, or one Android cannot decode, leaves the initials. +- The corners were rounder (22dp) than the launcher's own widgets; they are + 16dp now, previews included. + +Checked and left as they are: + +- Dark theme: no white fallbacks; the primary is white as in the webmail. +- Large sizes of the 2x2 widgets are mostly empty space. A resize limit + (`maxResizeWidth`) is given in dp, and on launchers with large cells it + would pin a widget below two cells, so there is none. +- RTL is not supported: the widget library does not mirror layouts, and + there are no right-to-left widget strings. +- Mail and next's chip for the following event is 24dp tall; the whole + column also opens the calendar. + +## Open + +- A0 cannot be shown on this emulator, where each registration takes + 3–10 ms; the fix is checked by the request codes being unique. +- Calendar and task widgets against Stalwart 1.0 wait for + `feat/stalwart-1.0` (see above). +- Offline message links need the offline cache; links for the other account + need the server for the switch (L4). +- Widget strings exist in English and German only. +- RSVP double tap and update race, and the tick's update race, were not run + on the device; unit tests cover them. +- The Files screen opened offline shows the raw network error instead of an + offline message (app screen, not a widget).