0.1.108 — Inbox always visible + Casdoor login always available

Two follow-ups directly from the latest user-supplied log dump:

  • pin_diagnostics warning showed total=1 pinned=0 signedIn=false. Rows: #-1777869322101000 "?" plain/local/drag. repeatedly — the user's only visible category was a stale local row, while _loadInitialData had successfully loaded 4 cloud categories (one of them their real "Inbox") at the same time.
  • The Casdoor probe was 404'ing against https://notechondria.trance-0.com/api/v1/auth/casdoor/config/, flipping _casdoorConfigured to false, which hid the SSO button on every login surface.

User directives verbatim: "I need to keep inbox always visible in categories. And the primary login option for casdoor is always available and as default."

1. _allCategories no longer gates on _token

The 0.1.105 pin_diagnostics upgrade in 0.1.107 finally surfaced the real bug: _allCategories was returning only [..._localCourses] whenever _token == null || _token!.isEmpty, so the user's cloud _courses (which had the real Inbox row in it) never reached the sidebar.

This gate predates the Casdoor cutover. In the post-0.1.106 world the legacy DRF _token field is dead weight: the backend doesn't issue DRF tokens anymore, the frontend's cloud GETs succeed via session cookies / Casdoor JWT regardless, but the editor's _token field can stay empty forever. The gate produced a guaranteed false negative — "Inbox vanished" — for every user on the new auth path.

Fix in editor_app/lib/app_shell.dart:

List<Map<String, dynamic>> get _allCategories {
  return [..._localCourses, ..._courses];
}

Whatever's loaded in _courses is shown. Post-logout the existing flow (session_mixin.logout()loadInitialData()) re-fetches courses for an unauthenticated user, so the signed-out end state is whatever the backend's public catalog returns — same as before.

The user-visible effect: the cloud "Inbox" pins to the top of the sidebar (it has is_default == true and / or matches the title- casefold check), the local "?"-titled garbage row drops to draggable, and the diagnostic re-emits with total=N pinned=1 (plus the rich row dump from 0.1.107).

2. Casdoor login button no longer gates on _casdoorConfigured

Before: every app rendered the Casdoor SSO button only when _casdoorConfigured == true. That flag is set by the boot probe of /api/v1/auth/casdoor/config/. When production hadn't been redeployed yet (or any operator hits a stale Render slot), the probe 404'd, the flag stayed false, and the user saw no Casdoor button anywhere — no way to log in.

After: all three apps (editor, portal, planner) always pass the onCasdoorLogin callback through to the signed-out auth surface:

onCasdoorLogin: () => launchOAuth('casdoor', intent: 'login'),

launchOAuth itself already does the right thing on each click: it re-fetches /auth/casdoor/config/, and if the response says configured: false (or 404s), it logs a warning to the debug log and bails without redirecting — no crash, no broken redirect. That means the worst case is "tap does nothing visible, debug log shows a single warning" — strictly better than "no button at all."

_casdoorConfigured still gates bind and unlink in the signed-in account card, since those are post-login operations that genuinely need the backend to be wired before they make sense. Probe success still updates _casdoorOrgLoginUrl so the "Login via third party" deep-link to Casdoor's hosted login page keeps working.

Files changed:

  • frontend/editor_app/lib/core/build_helpers.dart
  • frontend/portal_app/lib/app_shell.dart
  • frontend/planner_app/lib/app_shell.dart

Verification

  • flutter analyze clean for the four touched files (only pre- existing info-level lints remain — prefer_single_quotes, use_string_in_part_of_directives, deprecated_member_use from surfaceVariant / withOpacity elsewhere in portal / planner).
  • _allCategories change is purely additive on the cloud branch (the original gate already returned [..._localCourses, ..._courses] for the signed-in case — we just collapse both branches into the same shape).
  • The Casdoor button change is also purely additive at the gate; the actual click-handler (launchOAuth) is unchanged and already handles the misconfigured-backend case gracefully.

Operator follow-up

If the production backend at notechondria.trance-0.com is still returning 404 on /api/v1/auth/casdoor/config/, that's a deploy freshness issue, not a code bug — the route is wired in backend/notechondria/api_urls.py:65. Redeploy the backend with the post-0.1.96 codebase and the probe will start succeeding. The SSO button stays visible whether you redeploy or not.