Settings: profile + billing on top, everything else behind More - #113
Merged
Conversation
The web Settings page rendered nine sections in one flat column, so the two things people actually open it for -- who they are signed in as, and what their storage costs -- sat buried among Share ID, wallet keys and endpoint config. It is now three entries: PROFILE (was ACCOUNT, larger avatar), a new BILLING row, and a collapsed "More" tile holding the other seven sections verbatim, so expanding it restores exactly the previous page. Billing has no in-app screen on web, so the row opens cloud.fx.land at <issuerBaseUrl>/login?returnTo=%2Fbilling -- pinning-webui has a real billing route, and its Login honours any returnTo beginning with a slash. Resolving through issuerBaseUrl keeps a user-configured billing server working instead of hardcoding the default. That row's subtitle is deliberately quiet. getStorageAndCredits fails fast when there is no JWT, which is the normal state both when signed out and for a tokenless Mode-C vault user, so a plain FutureBuilder would have parked a BillingApiException error row at the very top of the page this change exists to simplify. Pending and failed both fall back to static copy -- no spinner, no error row -- matching how the home screen and the rank badge already treat this same fetch. ExpansionTile's own top/bottom rules are suppressed via shape and collapsedShape rather than a transparent dividerColor, which would also have erased the Dividers between the sections nested inside the tile. Expansion state rides on the tile's own State, which survives the setState the Security section fires from inside it. Two incidental fixes to the same file: it was the only one of 394 .dart files under lib/ carrying a UTF-8 BOM, and it held four mojibake ellipsis sequences in user-visible strings, so the app was rendering a garbled "Generating..." on screen. The BOM is the likely cause of both. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X6TQxyiZN6cv1NJrXkX5Ps
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes the web Settings page simpler: the profile and billing sit at the top, everything else is one click away.
Before / after
The page rendered nine sections in one flat column, so the two things people actually open Settings for -- who they are signed in as, and what their storage costs -- sat buried among Share ID, wallet keys and endpoint config.
It is now three entries:
Notes on the three judgement calls
The billing link. There is no in-app billing screen on web, so the row hands off to
<issuerBaseUrl>/login?returnTo=%2Fbilling. Checked againstpinning-service/pinning-webui:App.tsxhas a realbillingroute, andLogin.tsxnavigates to anyreturnTobeginning with a slash. Resolving throughAuthCore.issuerBaseUrl()rather than hardcoding means a user-configured billing server keeps working.The subtitle is deliberately quiet.
getStorageAndCredits()fails fast when there is no JWT -- its_ensureConfiguredthrows before any HTTP -- and that is the normal state both when signed out and for a tokenless Mode-C vault user. A plainFutureBuilderwould therefore have parkedBillingApiException: JWT Token is not configuredat the very top of the page this PR exists to simplify. Pending and failed both fall back to static copy: no spinner, no error row. Same treatment the home screen and rank badge already give this fetch.Suppressing ExpansionTile's rules. Done with
shape/collapsedShape, not a transparentdividerColor-- the latter would also have erased theDividers between the sections nested inside the tile. Verified against the SDK (expansion_tile.dart:768-783): the default expanded shape isBorder(top: dividerColor, bottom: dividerColor), whichconst Border()overrides exactly.Expansion state rides on the tile's own
State(no_moreExpandedfield), which survives thesetStatethe Security section fires from inside it -- the tile keeps a fixed position and runtime type in the parentColumn, so Flutter reuses the same Element. ThePageStorageKeycaveat in the ExpansionTile docs applies to lazily-builtListViews, not thisSingleChildScrollView+Column.Two incidental fixes to the same file
.dartfiles underlib/carrying a UTF-8 BOM.Generating...andVault 1234abcd...on screen. The BOM is the likely cause.Verification
flutter analyzeon this file returned No issues found! locally, though on a slightly earlier revision of the change; the deltas since (theshape/collapsedShapeswap, dropping a now-unused field, one comment reword) were checked by grep for dangling references and against the SDK source. The local machine then became unusable for further runs -- an unrelated process is leaking ~1M OS handles and starving process launches -- so CI is the authoritative check here.Not verified in a real browser yet: the collapsed/expanded layout and the live billing subtitle both want a look on files.fx.land/app/ after deploy.
Reviewed by an independent advisor (Google Antigravity), which found no defects.
Generated with Claude Code
https://claude.ai/code/session_01X6TQxyiZN6cv1NJrXkX5Ps