git.lucas.co / cce-system-interface
system settings
git clone https://git.lucas.co/cce-system-interface.git

commit5b36a4ca08b1d64813e77036757a4f865899e500
parentd6f6bab9e8
authorLucas Galante <[email protected]>
date2026-08-14 11:51
refactor: one Google sign-in entry point, in the add form

The header row's "Google Login" and the add form's "Login (Google)"
dispatched the identical GoogleLoginInit — not two flows, one flow with two
buttons. Dropped the header copy: adding an account is a single intent, and
the auth method belongs inside the form beside Save.

Removing it exposed that the form's four-button row was already clipping —
"Login (Google)" and "Login (iCloud)" rendered as "ogin (Google" and "ogin
(iCloud" at 504px, and the details row's "Re-login (Browser)" as "e-login
(Browser". Four buttons leave ~98px and three leave ~134px, against ~7px a
character. All three now ride the same `narrow` switch the header row
already used: Google / iCloud / Re-login.

The in-flight tint moves with the button, and now also marks Re-login, so a
running sign-in is visible wherever one can be started. The timeout message
no longer names a button that does not exist.

Verified live: the form's Google button binds 36137 and tints, a second
press is refused with one xdg-open call total, and every row's labels render
unclipped. The details row was checked against an OAuth account in an
isolated XDG_CONFIG_HOME — the real accounts.json was never modified.

Co-Authored-By: Claude <[email protected]>

 src/pages/accounts.rs | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/src/pages/accounts.rs b/src/pages/accounts.rs
index 2287304..a7e1f93 100644
--- a/src/pages/accounts.rs
+++ b/src/pages/accounts.rs
@@ -258,7 +258,7 @@ async fn google_login_flow(sender: &calloop::channel::Sender<AppAction>) {
         Ok(res) => res,
         Err(_) => {
             let _ = sender.send(AppAction::Accounts(AccountsMessage::StatusMessage(
-                "Google sign-in timed out — press Sign in with Google to retry.".to_string(),
+                "Google sign-in timed out — start the sign-in again to retry.".to_string(),
             )));
             return;
         }
@@ -433,14 +433,16 @@ pub fn view(state: &mut AccountsState, cx: f32, cy: f32, cw: f32, ch: f32, sec_f
         // ── Global actions: one compact row ──
         let add_bg = if state.adding_new { (ACCENT_BG, ACCENT_BG) } else { BTN_PRIMARY };
         let oauth_bg = if state.editing_oauth_creds { (ACCENT_BG, ACCENT_BG) } else { BTN_NEUTRAL };
-        // A login in flight is an active mode too — tint it like the others so
-        // the "already waiting on the browser" reply isn't the only clue.
+        // A login in flight is an active mode too — tint whichever button could
+        // have started it, so the "already waiting on the browser" reply is not
+        // the only clue.
         let login_bg = if state.oauth_listener_running { (ACCENT_BG, ACCENT_BG) } else { BTN_NEUTRAL };
         let narrow = item_w < 520.0;
-        stack.add_row(3, 8.0, btn_h, |c, i, x, w| {
+        // Google sign-in is deliberately NOT up here: adding an account is one
+        // intent, and the auth method is chosen inside the form (next to Save).
+        stack.add_row(2, 8.0, btn_h, |c, i, x, w| {
             let (label, colors, action) = match i {
                 0 => (if narrow { "Add" } else { "Add Account" }, add_bg, AccountsMessage::AddAccountStart),
-                1 => (if narrow { "Google Login" } else { "Sign in with Google" }, login_bg, AccountsMessage::GoogleLoginInit),
                 _ => (if narrow { "Google API" } else { "Google API Settings" }, oauth_bg, AccountsMessage::EditOAuthCredsStart),
             };
             c.button(label, x, c.ay(), w, btn_h, colors.0, colors.1, TEXT_BTN, AppAction::Accounts(action));
@@ -451,7 +453,7 @@ pub fn view(state: &mut AccountsState, cx: f32, cy: f32, cw: f32, ch: f32, sec_f
         // ── Context zone: add form / OAuth form / selected details ──
         if state.adding_new {
             stack.context.text("Add New Account", 12.0, 0.0, 14.0, [0.35, 0.65, 0.90, 1.0]);
-            stack.context.text("Gmail uses Google Login; iCloud requires an App Password.", 12.0, 0.0, 11.0, TEXT_DIM);
+            stack.context.text("Gmail signs in with Google below; iCloud requires an App Password.", 12.0, 0.0, 11.0, TEXT_DIM);
 
             state.email_box.set_row_rect(rx + 12.0, item_w);
             stack.add_widget(&mut state.email_box, item_w, widget_h, ctx);
@@ -467,8 +469,11 @@ pub fn view(state: &mut AccountsState, cx: f32, cy: f32, cw: f32, ch: f32, sec_f
                 let (label, colors, text_col, action) = match i {
                     0 => ("Save", BTN_PRIMARY, TEXT_BTN, AccountsMessage::AddAccountSave),
                     1 => ("Cancel", BTN_NEUTRAL, TEXT_BTN, AccountsMessage::AddAccountCancel),
-                    2 => ("Login (Google)", BTN_NEUTRAL, TEXT_BTN, AccountsMessage::GoogleLoginInit),
-                    _ => ("Login (iCloud)", BTN_NEUTRAL, TEXT_BTN, AccountsMessage::ICloudLoginHelp),
+                    // Four buttons in one row is the tightest cell on the page —
+                    // the full labels clip below ~440px of section width, so they
+                    // ride the same `narrow` switch the header row uses.
+                    2 => (if narrow { "Google" } else { "Login (Google)" }, login_bg, TEXT_BTN, AccountsMessage::GoogleLoginInit),
+                    _ => (if narrow { "iCloud" } else { "Login (iCloud)" }, BTN_NEUTRAL, TEXT_BTN, AccountsMessage::ICloudLoginHelp),
                 };
                 c.button(label, x, c.ay(), w, btn_h, colors.0, colors.1, text_col, AppAction::Accounts(action));
             });
@@ -509,7 +514,8 @@ pub fn view(state: &mut AccountsState, cx: f32, cy: f32, cw: f32, ch: f32, sec_f
                     actions.push(("Make Default", BTN_NEUTRAL, TEXT_BTN, AccountsMessage::MakeDefault(selected_idx)));
                 }
                 if acc.is_oauth {
-                    actions.push(("Re-login (Browser)", BTN_NEUTRAL, TEXT_BTN, AccountsMessage::GoogleLoginInit));
+                    let relogin = if narrow { "Re-login" } else { "Re-login (Browser)" };
+                    actions.push((relogin, login_bg, TEXT_BTN, AccountsMessage::GoogleLoginInit));
                 }
                 actions.push(("Delete", BTN_DANGER, TEXT_DANGER, AccountsMessage::DeleteAccount(selected_idx)));
                 stack.add_row(3, 8.0, btn_h, |c, i, x, w| {