Commit Graph

10 Commits

Author SHA1 Message Date
Arkadiusz Fal
e661635079 Let every menu mark the current context and a disabled source admit it
MAC1: switcher rows are Toggles on macOS so the checkmark really renders,
and the settings row names its context via LabeledContent with a tooltip.
MAC4: accessible names are words, never raw SF Symbol identifiers.
MAC14: new displayStatus(for:) surfaces instanceDisabled on account rows,
badges, and the switcher tooltip without disturbing status(for:) consumers.
MAC16: disambiguated labels guard the username branch and fall back to the
source name only when rows would still collide.
Also carries the new Localizable strings for the whole macOS fix batch.
2026-09-11 08:02:26 +02:00
Arkadiusz Fal
b69fb7fb1f Let the switcher icon say how the account is doing
Only the non-tvOS toolbar menu computed a health-aware glyph; the
settings row and the tvOS button hardcoded the plain outline, so on
tvOS the icon never changed no matter what broke (TV7). One shared
icon now serves all three: outline for Local, filled for a signed-in
account, and an orange exclamation badge when the context needs a
person — credentials rejected, instance disabled, or unreachable.
Transient states like checking stay unbadged so launch doesn't flash
a false alarm.
2026-09-10 22:40:44 +02:00
Arkadiusz Fal
0c6c7763db Let an untouched Save keep the name it was already showing 2026-09-09 23:59:57 +02:00
Arkadiusz Fal
2e1f411cc8 Let the label the account syncs be one the user can set 2026-09-09 23:52:54 +02:00
Arkadiusz Fal
f1e034111c Prune a record only once nothing of it is left behind 2026-09-08 22:23:30 +02:00
Arkadiusz Fal
87ab014467 Let the account that signs in claim what was made signed out 2026-09-07 23:46:12 +02:00
Arkadiusz Fal
02efc71e7c Tell the accounts apart, and let a disabled source come back 2026-09-07 18:53:21 +02:00
Arkadiusz Fal
dca8e2fbe4 Let a row show the metadata it was handed 2026-09-07 07:30:00 +02:00
Arkadiusz Fal
ccf5c51460 Keep an automatic repair from ever reaching the server
A repair carries the timestamp of the selection it replaced, which is what
stops it outranking a deliberate choice made elsewhere — but it also makes
every ordering comparison read it as that choice. A fetch holding the
selected account's tombstone and an older server context applied them in
that order: the tombstone repaired the selection, and the older context
then lost to it, so the "the server holds the stale copy" path marked the
repair dirty and uploaded an automatic fallback under the deleted choice's
timestamp, over an explicit choice. The stored context now remembers
whether it is a repair, and a repair that wins an ordering pushes nothing
back: the server's context stands remotely, the fallback stays local, and
the next explicit choice on any device re-converges everyone. The bit is
persisted with the context, or a relaunch would forget and publish it on
the first stale record it read.

Three more from the same trace:

- The repair prefers a signed-in login on the same server, decided off
  statuses, and it commits — once the selection no longer names the deleted
  account there is nothing left for a later status to move. Foreground
  activation refreshes statuses ahead of it, but a deletion delivered by a
  silent push arrives outside that sequence, and a credential that reached
  this device over iCloud Keychain while it was in the background announces
  nothing. The refresh is now asked for by the repair itself, which closes
  every entry point rather than each fetch trigger having to remember one.

- A device that meets a tombstone before the account it kills cannot record
  which instance that account lived on, and the account record arriving
  afterwards was discarded — with the only copy of that fact still in it.
  Every later repair of a context naming it then fell to local past a
  usable sibling. The association is kept as the record is dropped.

- Removing an orphaned account deleted its credential before recording the
  deletion, so a crash in between brought the account back on the next
  launch as one nobody can sign into, owing no deletion to any device. The
  outbox goes first, here and in the sign-out and instance cascades; the
  scoped credential key needs only the account's ID, and the store is
  resolved through the instance before anything is removed.
2026-09-05 23:06:10 +02:00
Arkadiusz Fal
69179c9fe9 Give every login a screen, including the ones with nowhere to go
An account whose source is gone was listed nowhere: `orphanedAccounts` had
no readers, and the credential behind it could not be reached at all —
`AppEnvironment.accountCredentialsManager(for:)` resolves the store from
the account's instance, and an orphan has none. The new accounts screen
lists them and removes them by account ID, asking every account-keyed
store because the backend is no longer knowable. The pre-revamp
instance-keyed item is deliberately left behind: identifying it needs the
instance too.

Deleting a source has always signed out and removed the accounts on it.
Both confirmations now say so, and name them.

The sign-in that files a credential against an account is now one
implementation instead of a private method on the source editor, so the
accounts screen and the new optional sign-in step after adding a source
attach a login exactly as the editor does. Extracted verbatim — the
identity-scope guard, the account re-resolve, the write-then-read-back
and the rollback are what keep a half-written login from looking real.

Signing out from the per-instance section now asks first, and both
sign-out paths run one cascade rather than two copies of it.
2026-09-04 23:35:51 +02:00