Pass expand=task_counts to get each project's done and undone task
counts. The v2 expand param is now repeatable so it combines with
expand=permissions.
Settings saves no longer re-read the account and PUT the merged result.
They PATCH only the edited fields, the server merges them, and the cache
gets the same edits applied. Demo mode drops the language in the form.
PUT /user/settings/general replaces every setting, and the general settings form sent the snapshot it took on open, reverting settings changed elsewhere such as the sidebar width. The sidebar drag and frontend toggles had the same problem with this tab's cached account.
Both mutations now share one save: it checks the request identity, re-reads /user, overlays only the edited fields and PUTs. The form diffs its edits against what it last saved, ignores a submit while a save is in flight and keeps edits made during one.
Refs #4014
Mutation functions now receive the request context the wrapper captured, so they can fence a multi-request mutation without capturing a second one. A request-context abort is internal: it never toasts, even while the context is still current, and skips the follow-up refetch.
checkAuth() latched the stored JWT as failed on any refresh error, so a
network error or 5xx left the user logged out until a reload, and a token
another tab stored meanwhile was never adopted.
- doRefresh reads the response itself and throws a typed
RefreshTokenError (network, server, rate-limited, rejected); the store
switches on its kind instead of guessing at the error's cause.
- Network errors and 5xx no longer latch the JWT; the next navigation
refreshes again. A 5xx gets one immediate retry so a blip recovers
inside the router guard and keeps the deep link.
- renewToken no longer logs out an expired session on a transient
failure.
- A token another tab stores while our refresh is in flight is adopted
in doRefresh, for every caller.
- A 429 with Retry-After holds all refreshes against that API server
until it passes (capped at ten minutes), then a single scheduled retry
restores the session and leaves the login page for the saved route.
Without the header the old page-load latch stays.
Fixes#4052
Every 429 from the rate limiters now carries Retry-After so clients know
when the window ends. The basic-auth limiter keys on aligned buckets, so
its value comes from the bucket end. Retry-After isn't CORS-safelisted,
so the CORS config exposes it for the desktop app and frontends on
another origin.
pageSizeFor only floored the configured size at 1, and every caller passed
the server's own max_items_per_page. Drop it, stop sending per_page, key the
pages by page number only and take per_page from the response via
toPaginated.
The v2 API caps per_page at service.maxitemsperpage (default 50) instead
of honouring 1000, and fetchAllPages already follows total_pages, so the
per_page these sweeps sent had no effect.
Nest list pages under a list key to match the sibling query modules, and
swap the inline envelope normalisation and delete cache rewrite for
toPaginated and removeFromPages.
Binds the table to the route's ?page=, clamps the page back in range once
real data has loaded (not against placeholder data), and locks the delete
buttons while a page switch is loading. Renders <Pagination> once there is
more than one page. Stops sending per_page so the server default applies.
Replaces the fetchAllPages sweep with one page of 25 per query, keyed
by page. Deleting a token rewrites total and total_pages on every
cached page before the list prefix is invalidated.
With more than one page of bots, a new bot was appended to a page the user
wasn't on, with no feedback. Jump to the last page and name the bot in a
success message.
Team.ReadAll had no ORDER BY under LIMIT/OFFSET, so pages could overlap or
skip teams, and an exact search match could fall off the first page the
pickers fetch.