9b7f66a2c031c1fb71d359db868ad8065a895674
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
51dabd9cab |
Stop a language change undoing itself, and stop the translate prompt
Two reports, both about the language setting. **Picking a language sometimes took several clicks.** The subtree that reads the account's settings file is keyed on the language version, so choosing a language deliberately throws it away and builds it again. The remount re-read the settings file — which still held the old language, because the write is debounced by three seconds — and applied it, putting the old language back. The click that appeared to work was the one made after the previous write had landed, which is exactly the "sometimes" in the report. Worse than it looked: the queued push survived the remount, so the file was eventually written with the new language while the screen showed the old one. A reload then changed the language on its own. Fixed twice over, because either alone leaves a race. The file is read once per account per page load rather than once per mount, and hydrate now holds back any key with a change still queued — a change that has not been written up is newer than the file by definition. That rule is `mergeRemote`, pulled out as a pure function so it could be tested without a JMAP client. **Both browsers kept offering to translate an English page.** They were right to: `<html lang>` said English while the visible text was 6,289 message rows of marketing copy and brand names in whatever language the sender wrote in. The list is most of the text on the screen, so that is what the detector was reading. Sender, subject and preview in the list, and the thread subject and sender name in the reader, are now marked as what they are — content, not interface. Message bodies were already marked, so this is the same line drawn in the places the earlier pass missed rather than a new one. Whether it silences the prompt is Chrome's call and cannot be checked from inside the page; the marking is right either way. |
||
|
|
e014521fb6 |
Keep your own settings out of someone else's account
Switching to an account somebody shared pointed the whole app at it. The
rule was "use the selected account if it can do this", and a shared file
account can, by definition, do files.
ihasmail keeps its settings in the account's Files -- that is what makes
them follow you between devices -- so changing any setting while looking
at somebody's shared folder wrote `settings.json` into *their* storage,
creating the `ihasmail` folder there to do it. Signature images went the
same way, and push registration would have gone to whichever account was
on screen. Reading someone else's data by mistake is bad; writing yours
into theirs is worse, and one line was doing both.
There are two questions, and they had one answer:
- what am I looking at -- follows the switcher, because switching to a
shared account is how you read what was shared
- what is mine -- never does
So `accountFor` keeps the first meaning and `ownAccountFor` is the
second, used by settings sync, signature images and push. A `??
accountId` fallback in `loadStoredSignature` went with it: the reader's
own signature, reached through whoever happened to be selected.
A third rule was hiding in the first. A capability the selected account
does not advertise fell back to the selected account anyway, so a session
naming no primary for something aimed it at whoever was selected --
somebody else. It now answers with nothing, which is honest: the feature
is unavailable, rather than pointed at a stranger.
What this does not settle is whether the mail, calendar and contacts the
switcher appeared to offer were ever really reachable, or only asked for
and refused. That depends on what Stalwart advertises on a shared
account, which needs a look at a sharee's session; if it advertises
capabilities nobody shared, more is needed here than routing.
|
||
|
|
94bf42cfda |
Drop Stalwart 0.15 support
ihasmail spoke to two generations of Stalwart that are less alike than their version numbers suggest: 0.16 replaced the REST management API with JMAP registry objects, changed the shape of FileNode, split its rights up, and moved configuration into the store. Carrying both meant 34 branch points across nine files, a 92-line compatibility shim whose only job was telling them apart, a parallel REST implementation of every credential operation, and a mock that had to model both. The branches were not the real cost. The cost was that a wrong answer about which generation had answered always had somewhere to fall back to, so it failed quietly rather than loudly: one capability looked for in the wrong place downgraded every real 0.16 server onto the 0.15 path, which posted the current password to an endpoint 0.16 had removed, reported the wrong generation on About, and ran Files on the older code. It reached production and was recorded as verified when it was not. The mock mirrored the same wrong placement, which is why the tests agreed. Removed: the filenode compatibility shim, the dual "registry" | "legacy" backend in account.ts, the pre-0.16 generation in AccountInfo and everything that read it, the mock's LEGACY mode and dev:mock:legacy, and the three test files that existed only to pin 0.15 behaviour. Sign-in now refuses an older server by name, once, rather than letting Files, the account locale and credentials each fail in their own way with nothing connecting them. It says the credentials were fine -- someone hitting this has typed a correct password, and telling them otherwise sends them round in circles -- and names the tag to build from. Four tests cover it, including that no session cookie is minted and that bad credentials on such a server are still a plain 401. Two fallbacks went that were not strictly about 0.15, and both for the same reason the removal is happening. Files no longer answers a refused filter or sort by fetching every node in the account, which would hide a real fault behind a performance cliff nobody would notice. And the app folder lookups now filter on parentId/isTopLevel alone and match names client-side, since `name` is not a filter Stalwart is known to implement and one it does not know fails the whole query rather than being ignored. The last release that runs on 0.15 is tagged stalwart-0.15-support. Verified against the mock end to end: sign-in, the Files tree on the 0.16 path with the app folder hidden, and self-service credentials over the registry. 226 web + 75 server tests pass; typecheck and build clean. |
||
|
|
0a9218f622 |
Keep settings with the account, not the browser
Every setting lived in localStorage, so none of them travelled between devices. The sharpest edge is the default identity: with none set the address that sorts first wins, so mail goes out from an address the recipient may not recognise -- and someone who sets it at work finds it unset at home, with nothing to say so. Reported in #54. They now live in a settings.json in the account's own JMAP Files, beside the signature images already kept there. ihasmail itself stays stateless: no volume, no database, nothing to back up separately, and the settings are covered by whatever backs up the mail store. localStorage stays as a cache rather than the source of truth, so the first frame is painted from it and the file corrects it a moment later. A private window has no cache and shows defaults for that one frame, which is the trade for not gating the whole app on a network round trip. Not everything should follow the account. A list-pane width picked on a 27" monitor is wrong on a laptop, and the notification toggles track a permission the browser grants per-device, so claiming it elsewhere would be a lie. Those stay local, written as a list of exceptions so that a setting added later syncs by default -- which is what adding one almost always means. Writes are coalesced: update() fires on every frame of a splitter drag, so a change waits 3s and the newest value wins. A tab going away flushes first, as does signing out, so a setting changed seconds before either is not lost. The ihasmail folder is now hidden from the Files view, contents and all. Hiding the folder alone would have been worse than showing it: the tree attaches a node whose parent is missing to the root, so the signature images would have spilled into the top level as if the user had put them there. Those images have been visible since signatures shipped. Requires 0.16 -- FileNode/query cannot see directories before that. On 0.15 settings stay local exactly as they were. Verified against the mock end to end: folder create, blob upload, node create, read back, update, re-read. Not yet exercised against the live 0.16.19. |