connectWithPassword() had no try/catch around firebaseFactory(), unlike
connectWithGoogle() right next to it. A thrown FirebaseAuthError (wrong
password, 400 from identitytoolkit) escaped uncaught out of the button's
async onPressed, so _busy/_status never updated and the screen sat on
"Signing in..." forever with no error shown -- reproduced live via the
browser console (confirmed "Uncaught Error" immediately after the 400,
not a hung Future).
Also reports real progress stages ("Saving account...", "Signing in to
Firebase...") instead of one static string, clears the bad password on a
confirmed rejection (previously only the null-client branch did, letting a
background sync tick keep retrying it), and surfaces the actual failure
reason in the status text instead of a generic "rejected" message.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XATAXuMzpYggWyTBPGyiSN
lyricanki's Linux desktop cannot use google_sign_in (no Linux
implementation) and its loopback replacement is dark until a Desktop OAuth
client exists. Hiding the button there makes the screen silently offer less
on one platform, which reads as a bug.
googleUnavailableReason renders a *disabled* button plus the reason. That is
not the "control that can never succeed" trap: the trap is a live-looking
button that reports a fake cancellation, and a null onPressed cannot be
tapped at all. Null stays the default, so every existing host is unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CFSGeWxHPY5rHUGrPqhcmG
sync_settings_widget.dart 68 — the public StatefulWidget and its callbacks
sync_settings_sections.dart 132 — the two sections the screen composes
sync_settings_screen.dart 192 — the State: loading, connecting, build
Uses `part`/`part of`, not imports: _FirebaseSection and _BackupSection are
library-private, so a sibling file cannot import them at all — extracting them
as ordinary files would not compile.
Two shapes tried and rejected on the way: an `extension _BackupActions` on the
State (an extension cannot call the protected setState), and moving build()
out of the class (it is an @override and has to stay). Both reverted from HEAD
rather than patched around.
flutter analyze clean, 29 tests pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CFSGeWxHPY5rHUGrPqhcmG
Extracts the near-identical "Sync settings" UI duplicated across 5 apps
(todo, diet_guard_app, workout_app, wake_alarm/phone_app, home_inventory)
into a single tested package. Firebase sync (primary) + optional local
backup slot; GitHub mirror UI was dropped per kuhy's explicit decision
mid-build in favor of Firebase-only, so existing apps keep their local
GitHub code untouched, just disconnected from this shared screen.
100% line coverage, flutter analyze clean under very_good_analysis.
Generalized check_no_crdt_sync_override.sh to guard any utils git
dependency (crdt_sync, sync_settings_ui), not just crdt_sync by name.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KdSp6Nbrs9xvsWkCMDN9a1