Repository navigation
v0.9.6
Added
-
A site-wide ratio policy banner
(#345) — a member on
ratio watch, or with downloads disabled, now sees it on every
authenticated page, above the global notices. The profile notice (#334) and
the System PM both require the member to go looking; a watch runs 14 days and
carries a 10 GiB download trigger, either of which can pass unseen.Not dismissible. A notification is an event you acknowledge once; this is
derived state that is true until it is not, and a member who dismissed it on
Monday would get no warning on Thursday as the trigger approached.The disabled arm keys on
canDownload, not on the policy status. The api
documents that flag as "an independent download-capability flag, NOT a
projection of ratio", with ratio policy merely its first writer and future
abuse suspensions expected to gate it too. Keying on the policy enum would
leave any such member with dead download buttons and no explanation — the
exact defect #334 existed to fix. The cause only explains the flag, and
degrades to a neutral "contact staff" when the ratio domain has nothing
specific to say.The watch arm carries the deadline and the 10 GiB trigger but no numbers:
a banner is an interrupt, not a report, and/ratioand the profile carry the
arithmetic.Wording is single-sourced with the profile notice in
components/ratio/ratioPolicyCopy, so one member cannot read two
explanations of one state on two screens. #334's spec passes unchanged, which
is the evidence that extraction changed no behaviour.It reads the session (stellar-api#659), not
GET /profile/me/ratio. That
route runs an unbounded read over every one of the member's contributions —
fine behind a profile view, not for something rendered on each page and
polled. -
Eligible Contributionson/ratio— contribution coverage is eligible
bytes over downloaded, and the page showed the percentage with neither
operand.RatioStatswas the only other component that rendered the figure,
so deleting it (below) would have dropped it from the app entirely. -
Member Feed settings — view, copy and reset your feed URLs
(#349) — a new
Feeds tab in settings surfaces the four RSS feeds stellar-api serves
(stellar-api#262): new contributions, your own contributions, news and your
bookmarks. Until now the api served them and nothing in the UI could reach
them.The URLs are credentials and are treated as such. Each carries a bearer
token, so they render as read-only fields rather than links — a link would
put the token in browser history — and the token is masked until you choose
Show tokens. That control is deliberately section-wide rather than
per-row: the api derives one token per member and maps it into all four
URLs, so revealing any single row already discloses the other three, and four
separate toggles would imply a separation that does not exist.The contributions feed can be filtered by community, tag, format and
bitrate, each single-valued and ANDed, with the copied URL reflecting the
selection. The format and bitrate options are pinned to the contract: they
areRecords over the generated union, so a value added upstream fails the
typecheck here instead of silently going missing from the list. Bitrates
render through the existing shared label map, soKbpsV0reads as
V0 (VBR). Filters narrow what the api already decided you may see —
releaseVisibleToruns first — so no filter can widen access.Resetting asks for confirmation, stating that every URL already in use
stops working, then shows the new URLs straight from the rotate response
without a second request. A failed reset leaves the displayed URLs untouched
and reports the api's own message.The feed URLs are cached with
keepUnusedDataFor: 0, so the token leaves the
store as soon as the tab is closed rather than lingering for RTK Query's
default minute. -
Homepage news items are addressable, and the panel can reach back
(#348) — every item in
the announcements panel now carriesid="news-<id>", so the links the Member
Feed'snews.xmlpublishes land on the item rather than the top of the page.The panel had to grow to make that true. The feed publishes 50 items;
the homepage showed five, so 45 of those links pointed at an anchor that
could not exist. News now comes from the paginated list added in
stellar-api#670: five rows at rest, a Load more news control, and a
fragment that widens the request to the feed's whole reach in one go rather
than paging until it finds something.Arriving from a feed link scrolls the item into view, expands it — the
reader has already seen the body in their reader, so a collapsed headline
would be a non-sequitur — briefly highlights it, and moves focus to it so
assistive technology announces the arrival.scrollIntoViewmoves the
viewport but never focus, so without that a screen-reader user got no signal
at all. The scroll honoursprefers-reduced-motion, which the stylesheet's
own reduced-motion block does not cover: it disablestransition, not
scroll-behavior.An item the list cannot reach now says so in one muted line, rather than
leaving the reader at the top of the page unable to tell a stale link from a
broken one.
Changed
-
Re-vendored the API contract from stellar-api
(#328) —src/types/openapi.json
and the generatedsrc/types/api.tsnow match the spec the API serves.
Three merged API pull requests had widened it since the last sync: the Member
Feed token surface, the four feed documents, and ratio policy state on the
session.Seven new paths, all Member Feed:
/feeds/contributions.xml,
/feeds/mine.xml,/feeds/news.xml,/feeds/bookmarks.xml, the member's own
/profile/me/feedsand/profile/me/feed-token/rotate, and the staff
/users/{id}/feed-token/rotate. One new schema,MemberFeeds. Nothing in the
UI consumes them yet — #349
is the settings page that will.Three paths changed shape in place, and both changes are additive:
/install/checklist/{id}/dismissnow declares a429, and the two
/tools/user-ranksoperations carry a newusers_edit_reset_feeds
permission. The rank form renders from the API's live permission catalog
rather than a list of its own, so the new key appears there with no change
here.No consumer broke:
typecheckwas clean against the new contract. The
contract'smajor.minordid not move (0.9), so ADR-0004 forces no version
bump.
Fixed
-
The ratio policy notice is mounted again
(#334) — a member on
ratio watch, or with downloads disabled, saw nothing on their own profile.
63d64cfreplaced<RatioStats />with inline stat rows in May and the
watch and disabled banners went with it; the component and its spec stayed,
and the spec rendered the component directly, so it kept passing against code
nothing mounted. #333's
cause-aware disabled banner has been unreachable since it shipped.The notice is now a
RatioPolicyNoticeat the top ofUserProfile's main
column. Not in the Statistics panel, where the old rows were: that column is
176px wide and the notice is prose.The watch wording now carries what a member needs to act on it, mirroring
the api's own watch PM: the current and required ratio, the deadline, and —
new here — the 10 GiB trigger, which can disable downloads tomorrow
whatever the deadline says. It states no "you must upload X" figure, because
the required ratio is computed from eligible contribution bytes and so moves
as you upload; any deficit printed here would overstate it.RatioStats.tsxand its spec are deleted, along with a stale
jest.mock('…/RatioStats')inUserProfile.integration.test.tsxthat had
been mocking a componentUserProfileno longer imports. The new spec renders
UserProfile, never the notice alone — a spec that mounts the notice
directly would reproduce the bug rather than catch it. -
A download refreshes the ratio stats it changes —
grantAccessand
reverseGrantinvalidated'Contribution'and'Download'but never
'Profile', whichgetMyRatioStatsprovides. Both mutations move the
member'sconsumed, and ADR-0044 putsOK → WATCHafter a download and only
after a download, so the surface most likely to need fresh policy state was
reading the pre-download cache.One limit, deliberately left: stellar-api evaluates the policy in the
background after the grant responds, so the refetch can still beat the
transition that setsWATCH. The ratio numbers are now always correct; the
status flip may lag by one interaction and lands on the next'Profile'
invalidation or page load. Closing that is folded into the site-wide ratio
banner issue, where the same freshness question has to be answered for both
surfaces at once. -
The session refreshes after a download, and on a timer (#345) — the
download mutations now invalidate'Auth'alongside'Profile', since the
session carries the same policy state. That closes only the member's own
actions: the daily sweep and a staff override move the state with nothing to
invalidate on, and the api evaluates the policy after the grant responds,
so invalidation can lose the race.PrivateLayouttherefore polls the session
every 15 minutes — nothing callssetupListeners, sorefetchOnFocusis
unavailable without a global change and a surface needing fresh state must
poll for itself.Measured rather than assumed: each poll hands
selectCurrentUserconsumers a
new object reference, so they re-render once per interval, but the content is
identical so nothing visibly changes. At that cadence it is not worth a
deep-equality guard insetCredentials, which would alter every login path to
save four renders an hour. A test pins both halves so a shorter interval has
to confront the cost knowingly.