Notechondria
Version: 0.1.75 Build Date: 2026-04-26T20:00
What's Changed
Multi-device session manager — frontend (editor)
0.1.65 shipped the backend: creators.Session model, the
MultiSessionAuthentication DRF class, auth_payload augmented
to return {multi_device, other_sessions_count, session:{id, device_label, …}}, and two new endpoints
(GET /api/v1/auth/sessions/ and DELETE /api/v1/auth/sessions/<id>/).
Frontend was tracked in docs/TODO.md as still-to-do; this round
delivers it for editor_app and stages the shared building blocks
so planner_app / portal_app can adopt the same UI when their
Settings pages get the Apple-style sub-page treatment.
Shared AuthClient interface — two new methods
-
Future<Map<String, dynamic>> listSessions(String token)andFuture<void> revokeSession(String token, int sessionId)added tonotechondria_shared/lib/src/app_shell/auth_client.dart. Each app'sNotechondriaClientalready declaresimplements AuthClient, so the new methods auto-inherit as abstract. (Note: main 0.1.67 separately landed equivalent declarations on the shared interface; this round consolidates them with the same signatures so behaviour is unchanged.)
Concrete HTTP client implementations (all three apps)
-
editor_app/lib/core/http_client.dart,planner_app/lib/core/client.dart, andportal_app/lib/core/client.darteach gain the two@overrideimplementations:listSessions→GET /auth/sessions/then_decode.revokeSession→DELETE /auth/sessions/<id>/then_decode. The 204 No Content response decodes to{}; non-2xx surfaces as an exception just like every other client method.
applyAuthPayload captures multi-device metadata
-
editor_app/lib/app_shell.dartadds three new fields on_AppShellState:int? _currentSessionId— id of the row that owns the current bearer token. Used byActiveSessionsCardto flag "This device" without re-asking the backend.bool _multiDevice—truewhen the current user has more than one active session. Drives the warning banner above the Settings menu.int _otherSessionsCount— display string for the banner.
applyAuthPayloadreads these from the 0.1.65 payload shape; unknown fields (older backend) silently default to null/false/0 so the app keeps working against pre-0.1.65 deployments.logoutresets all three so a stale "1 other device" banner doesn't linger after sign-out.
ActiveSessionsCard widget (shared)
-
New
notechondria_shared/lib/src/components/active_sessions_card.dart. Self-contained: takesonListSessions/onRevokeSession/onCurrentRevokedcallbacks and handles its own loading / error / refresh state. Each row shows the device label (icon-mapped from User-Agent: iOS / Android / iPad / Mac / Windows / Linux / generic), last-seen + created timestamps viaformatCompactTimestamp, an IP-fingerprint hint, a "This device" pill on the current session, and a trash button that opens a confirm-with-context dialog before callingonRevokeSession. After a non-current revoke the card re-fetches; after revoking the caller's own session it firesonCurrentRevokedso the host can run its local sign-out flow.
Editor _SignInSecurityPage hosts the card
-
editor_app/lib/modules/settings_pages.dart—_SignInSecurityPagenow rendersActiveSessionsCardabove the existing_ConnectedAccountsSection. Hidden when signed out (onListSessions == null). The card'sonCurrentRevokedcallback drops the local token, resets all three new session fields, clears the persisted session, and calls_loadInitialData()so the app drops back to the anonymous view immediately.
Multi-device warning banner
-
editor_app/lib/modules/settings.dart—_buildMultiDeviceBanner(context)renders atertiaryContainer-tinted Card above the Settings menu when_multiDevice && _otherSessionsCount > 0. Tap dives into the_SignInSecurityPageso the user can audit + revoke without hunting through the menu. Mirrors iOS' "Signed in on N other devices" pattern. Hidden completely (no spacing) when the user is on a single device, signed out, or running against an older backend that doesn't return the flag.
Renumbering note
This round was originally authored on the codex branch as
0.1.71 alongside the four cross-branch round-logs (0.1.71–0.1.74).
Per the mapping documented in docs/versions/0.1.74.md, codex
0.1.71 (multi-device frontend) lands on main as 0.1.75 to
preserve TODO rule #6 (third-digit monotonic across the repo
history). Content is unchanged from the original codex 0.1.71
commit.
Files Changed
frontend/notechondria_shared/lib/src/app_shell/auth_client.dart—listSessions+revokeSessiondeclarations (deduped against main 0.1.67's equivalent additions).frontend/notechondria_shared/lib/src/components/active_sessions_card.dart(new) — the card.frontend/notechondria_shared/lib/notechondria_shared.dart— exportsActiveSessionsCard.frontend/editor_app/lib/core/http_client.dart—listSessions+revokeSessionoverrides (deduped against main 0.1.67's equivalent additions; kept the variant with the 204-No-Content comment).frontend/planner_app/lib/core/client.dart— same dedup.frontend/portal_app/lib/core/client.dart— same dedup.frontend/editor_app/lib/app_shell.dart—_currentSessionId,_multiDevice,_otherSessionsCountfields; payload reads inapplyAuthPayload; reset inlogout.frontend/editor_app/lib/core/build_helpers.dart—onListSessions,onRevokeSession,onCurrentSessionRevoked,multiDevice,otherSessionsCountprops threaded into_SettingsPage.frontend/editor_app/lib/modules/settings.dart— three new_SettingsPageprops;_buildMultiDeviceBannerhelper; banner placement inbuild().frontend/editor_app/lib/modules/settings_pages.dart—_SignInSecurityPagerendersActiveSessionsCard.docs/TODO.md— multi-device frontend item rewritten to scope only planner_app / portal_app.VERSION— 0.1.74 → 0.1.75.
Notes
- All four packages (editor / planner / portal / shared) pass their smoke tests after this round. All editor module files remain under the §1.5 1000-line cap.
- planner_app and portal_app's Settings pages still use the
pre-0.1.68 flat layout; dropping the
ActiveSessionsCardthere is blocked on porting the sub-page architecture, which is tracked separately indocs/TODO.md.