Skip to content

Source status indicator: distinguish auth-expired from unreachable - #457

Open
mvanhorn wants to merge 1 commit into
Kopuz-org:masterfrom
mvanhorn:fix/427-2026-06-24-223-feature-source-status-expired-vs-unreachable
Open

Source status indicator: distinguish auth-expired from unreachable#457
mvanhorn wants to merge 1 commit into
Kopuz-org:masterfrom
mvanhorn:fix/427-2026-06-24-223-feature-source-status-expired-vs-unreachable

Conversation

@mvanhorn

@mvanhorn mvanhorn commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

The source switcher now distinguishes an expired/invalid session from an unreachable server, so the status indicator reports the two states separately instead of collapsing both into a single failure.

Why this matters

Previously a source that failed validation showed one generic failure state, so a user could not tell whether their saved session had expired (re-sign-in needed) or the server was actually unreachable (network problem). Splitting the states lets the UI guide the user to the right remedy. Reported in #427.

Testing

Added a hook test asserting the status mapping resolves to the expired/needs-sign-in state versus the unreachable state for the corresponding validation outcomes.

Closes #427

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4c04bd22-66e8-4e21-b476-9f00cb9b651d

📥 Commits

Reviewing files that changed from the base of the PR and between ac5e8e6 and 92244d6.

📒 Files selected for processing (2)
  • crates/components/src/source_switcher.rs
  • crates/hooks/src/source_switch.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/hooks/src/source_switch.rs

📝 Walkthrough

Walkthrough

The ConnStatus enum in the source-switch hook replaces its single Offline variant with Expired and Unreachable, computed via a new status_for helper with added unit tests. SourceSwitcher gains a dedicated CSS class and dot-styling match arm for the Expired state.

Changes

Distinguish Expired from Unreachable connection status

Layer / File(s) Summary
ConnStatus enum split and status_for mapping
crates/hooks/src/source_switch.rs
ConnStatus::Offline is replaced by Expired and Unreachable variants with updated documentation; use_connection_status() now calls a new status_for(AuthOutcome) helper to map outcomes explicitly, and unit tests verify the mapping for Valid, Expired, and Unreachable.
SourceSwitcher CSS and dot class mapping
crates/components/src/source_switcher.rs
SWITCHER_CSS gains an .ss-expired class with amber/orange coloring, and the status-dot match gains an explicit ConnStatus::Expired => "ss-dot ss-expired" arm instead of falling through to the generic off styling.

Sequence Diagram(s)

Not applicable — the changes are a data mapping refinement (enum split and CSS/class selection) without multi-component sequential interactions.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: splitting auth-expired from unreachable source status.
Description check ✅ Passed The description matches the PR’s UI/status-indicator change and the stated issue #427.
Linked Issues check ✅ Passed The changes split Offline into Expired and Unreachable and add distinct amber styling, meeting the linked issue’s core acceptance criteria.
Out of Scope Changes check ✅ Passed The changes stay focused on source status mapping and its UI styling, with no clear unrelated additions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/hooks/src/source_switch.rs (1)

36-44: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Guard against stale validation results after source switches.

A slower src.validate().await from the previous active source can finish after a later switch and overwrite status, showing the wrong Expired/Unreachable state for the current source.

Suggested fix
-        let src = active_source.read().clone();
-        if matches!(config.peek().active_source, Source::Local) {
+        let src = active_source.read().clone();
+        let expected_source = config.peek().active_source.clone();
+        if matches!(expected_source, Source::Local) {
             status.set(ConnStatus::Online);
             return;
         }
         status.set(ConnStatus::Connecting);
         spawn(async move {
-            status.set(status_for(src.validate().await));
+            let next_status = status_for(src.validate().await);
+            if config.peek().active_source == expected_source {
+                status.set(next_status);
+            }
         });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/hooks/src/source_switch.rs` around lines 36 - 44, The validation in
source_switch::spawn can write a stale ConnStatus after the active source
changes. Update the logic around active_source.read(), src.validate().await, and
status.set so the async result is only applied if it still matches the current
source when it completes. Use a source identity/version check inside the spawned
task before calling status_for(...) and status.set(...), and ignore outdated
validation results from previous switches.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/components/src/source_switcher.rs`:
- Around line 160-168: The connection status dot is currently rendered only
inside the !collapsed branch in source_switcher.rs, so collapsed mode hides the
status indicator. Update the SourceSwitcher rendering logic around the
ss-stk/ss-stat block to always render the dot for both collapsed and expanded
states, while keeping any text/extra details gated by !collapsed.

---

Outside diff comments:
In `@crates/hooks/src/source_switch.rs`:
- Around line 36-44: The validation in source_switch::spawn can write a stale
ConnStatus after the active source changes. Update the logic around
active_source.read(), src.validate().await, and status.set so the async result
is only applied if it still matches the current source when it completes. Use a
source identity/version check inside the spawned task before calling
status_for(...) and status.set(...), and ignore outdated validation results from
previous switches.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a9f5baad-9f63-4324-9636-3e683bc12f7e

📥 Commits

Reviewing files that changed from the base of the PR and between c6758b4 and ac5e8e6.

📒 Files selected for processing (3)
  • crates/components/src/source_switcher.rs
  • crates/hooks/src/source_switch.rs
  • crates/server/src/source.rs

Comment on lines +160 to +168
span { class: "ss-tile", i { class: "{active_icon}" } }
if !collapsed {
span { class: "ss-stk",
span { class: "ss-stat",
span { class: "ss-stat", title: "{dot_title}",
span {
class: if conn() == ConnStatus::Online { "ss-dot ss-on" } else { "ss-dot ss-off" },
class: "{dot_class}",
title: "{dot_title}",
}
if conn() == ConnStatus::Connecting {
if connection_status == ConnStatus::Connecting {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Render the status dot in collapsed mode too.

The dot is only inside if !collapsed, so the icon-only switcher still hides connection state. This misses the linked issue’s collapsed-switcher acceptance criterion.

Suggested fix
-.ss-mini{width:40px;height:40px;padding:0;justify-content:center;border-radius:11px}
+.ss-mini{width:40px;height:40px;padding:0;justify-content:center;border-radius:11px;position:relative}
+.ss-mini .ss-dot{position:absolute;right:7px;bottom:7px;border:1px solid var(--ss-surface)}
                 onclick: move |_| open.set(!open()),
                 span { class: "ss-tile", i { class: "{active_icon}" } }
+                if collapsed {
+                    span {
+                        class: "{dot_class}",
+                        title: "{dot_title}",
+                    }
+                }
                 if !collapsed {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
span { class: "ss-tile", i { class: "{active_icon}" } }
if !collapsed {
span { class: "ss-stk",
span { class: "ss-stat",
span { class: "ss-stat", title: "{dot_title}",
span {
class: if conn() == ConnStatus::Online { "ss-dot ss-on" } else { "ss-dot ss-off" },
class: "{dot_class}",
title: "{dot_title}",
}
if conn() == ConnStatus::Connecting {
if connection_status == ConnStatus::Connecting {
span { class: "ss-tile", i { class: "{active_icon}" } }
if collapsed {
span {
class: "{dot_class}",
title: "{dot_title}",
}
}
if !collapsed {
span { class: "ss-stk",
span { class: "ss-stat", title: "{dot_title}",
span {
class: "{dot_class}",
title: "{dot_title}",
}
if connection_status == ConnStatus::Connecting {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/components/src/source_switcher.rs` around lines 160 - 168, The
connection status dot is currently rendered only inside the !collapsed branch in
source_switcher.rs, so collapsed mode hides the status indicator. Update the
SourceSwitcher rendering logic around the ss-stk/ss-stat block to always render
the dot for both collapsed and expanded states, while keeping any text/extra
details gated by !collapsed.

@temidaradev

Copy link
Copy Markdown
Member

can you fix the linting and conflicts?

@mvanhorn
mvanhorn force-pushed the fix/427-2026-06-24-223-feature-source-status-expired-vs-unreachable branch from ac5e8e6 to 92244d6 Compare July 7, 2026 04:55
@mvanhorn

mvanhorn commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto master. In the meantime upstream landed the server side of this independently — AuthOutcome::Expired/Unreachable are already returned by the source backends, and source.rs was split into the source/ module — so I dropped my server-side changes. This PR now carries just the UI half: ConnStatus splits Offline into Expired vs Unreachable, and the switcher shows an amber dot for expired (sign-in-needed) vs the off/red dot for unreachable, layered onto your current switcher without touching the redesign. fmt + clippy (--workspace --all-targets -D warnings) + tests are green. Happy to add labels/i18n for the two states if you'd prefer them surfaced as text too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Source status indicator: distinguish auth-expired from unreachable

2 participants