feat: Resource Credits pre-publish check + RC top-up purchase client - #978
Conversation
Greptile SummaryThis PR adds two tightly scoped Resource Credits features: a non-blocking pre-check banner that warns users before they broadcast with insufficient RC, and the client side of a Points-funded RC-only top-up delegation (gated behind an off-by-default
Confidence Score: 5/5Safe to merge — the pre-check banner is purely advisory and non-blocking, and the top-up dialog is gated behind an off-by-default flag with no backend yet deployed. The two new features are well-isolated: the banner renders null when RC is sufficient or the user is logged out, and the top-up dialog is unreachable in production. The broadcast path follows the established active-key custom_json pattern used by Boost+. The only fresh concerns are the unconditional getRcStatsQueryOptions call for logged-out visitors and the missing error display in the top-up dialog when the broadcast throws — neither affects correctness of the on-chain action or the non-blocking guarantee. use-rc-precheck.ts for the logged-out stats query; rc-topup-dialog.tsx for broadcast error feedback. Important Files Changed
Reviews (3): Last reviewed commit: "chore: apply changeset versioning for PR..." | Re-trigger Greptile |
| return queryOptions({ | ||
| queryKey: ["promotions", "rc-delegation-prices"], | ||
| queryFn: async () => { | ||
| if (!accessToken) { |
There was a problem hiding this comment.
Stale price data visible to a different user on account switch
The query key ["promotions", "rc-delegation-prices"] contains no user identifier, yet the fetch is authenticated with a per-user accessToken. With staleTime: Infinity the cached response is never evicted. When user A is logged in and the prices are fetched, then user A logs out and user B logs in, useQuery will immediately return user A's cached data without refetching — because the key is identical. If pricing is ever account-specific (e.g., tiers, discounts), user B sees the wrong prices and could submit a purchase with an incorrect expectation of the cost.
| return queryOptions({ | ||
| queryKey: ["promotions", "rc-delegation-prices"], | ||
| queryFn: async () => { | ||
| if (!accessToken) { | ||
| return []; | ||
| } | ||
|
|
||
| const response = await fetch(CONFIG.privateApiHost + "/private-api/rc-delegation-price", { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| body: JSON.stringify({ code: accessToken }), | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error(`Failed to fetch RC delegation prices: ${response.status}`); | ||
| } | ||
|
|
||
| return (await response.json()) as PromotePrice[]; | ||
| }, | ||
| staleTime: Infinity, |
There was a problem hiding this comment.
New query keys missing from centralized
QueryKeys
Per CLAUDE.md, all new queries must add entries to QueryKeys in query-keys.ts as the single source of truth for cache references — "Use hardcoded query key arrays" is listed under ❌ DO NOT. Both get-rc-delegation-prices-query-options.ts (["promotions", "rc-delegation-prices"]) and get-rc-delegation-active-query-options.ts (["promotions", "rc-delegation-active", username]) define their keys inline without corresponding QueryKeys.promotions.rcDelegationPrices / rcDelegationActive entries. The absence of a QueryKeys entry for rc-delegation-active is also the direct cause of the invalidation gap above — there's no canonical key to pass to auth.adapter.invalidateQueries in use-rc-delegation.ts.
Context Used: CLAUDE.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2549824f38
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <EcencyConfigManager.Conditional | ||
| condition={({ visionFeatures }) => visionFeatures.drafts.enabled} | ||
| > | ||
| <RcPrecheckBanner operation="comment_operation" className="mx-2 mt-2" /> |
There was a problem hiding this comment.
Account for comment_options in publish RC check
When a post uses non-default rewards or beneficiaries, usePublishApi passes options to useCommentMutation, which broadcasts both comment and comment_options; this banner only checks comment_operation. For accounts with enough RC for the comment but not the extra options operation, the new pre-publish warning stays hidden and publishing still fails with the RC error, so the submit flow should estimate the full operation set or include comment_options_operation when options are present.
Useful? React with 👍 / 👎.
| [activeTopup] | ||
| ); | ||
| const canSubmit = useMemo( | ||
| () => !balanceError && !isAlreadyActive && duration > 0, |
There was a problem hiding this comment.
Disable top-up while active status is unresolved
If a user already has an active RC top-up, activeTopup is undefined until /rc-delegation-active returns, and canSubmit treats that as not active. When prices/points are cached or load first, the Next button is enabled during that window, so a duplicate purchase can be broadcast before the guard displays; include the active-status loading/fetching state in canSubmit or treat the unresolved state as blocked.
Useful? React with 👍 / 👎.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (6)
📒 Files selected for processing (13)
📝 WalkthroughWalkthroughAdds a Resource Credits (RC) pre-check and top-up feature. A new SDK utility estimates RC sufficiency for given operations, a ChangesRC Precheck and Top-up Feature
Sequence Diagram(s)sequenceDiagram
participant User
participant RcPrecheckBanner
participant useRcPrecheck
participant RcTopupDialog
participant useRcDelegationMutation
participant PrivateAPI as Private API
User->>RcPrecheckBanner: views editor/comment/vote
RcPrecheckBanner->>useRcPrecheck: useRcPrecheck(username, operation)
useRcPrecheck->>PrivateAPI: fetch RC account + RC stats
PrivateAPI-->>useRcPrecheck: rcAccount, rcStats
useRcPrecheck-->>RcPrecheckBanner: RcPrecheckResult { willLikelyFail, deficit }
RcPrecheckBanner-->>User: renders low-RC warning banner
User->>RcPrecheckBanner: clicks "Top up RC"
RcPrecheckBanner->>RcTopupDialog: showTopup=true
RcTopupDialog->>PrivateAPI: fetch delegation prices + active delegation
PrivateAPI-->>RcTopupDialog: prices[], activeRcDelegation
User->>RcTopupDialog: selects duration, clicks Next
RcTopupDialog->>useRcDelegationMutation: mutate({ duration })
useRcDelegationMutation->>PrivateAPI: broadcast ecency_rc_delegation custom_json
PrivateAPI-->>useRcDelegationMutation: success
useRcDelegationMutation-->>RcTopupDialog: step 2 (success)
RcTopupDialog-->>User: success confirmation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
packages/sdk/src/modules/operations/builders/ecency.tsOops! Something went wrong! :( ESLint: 8.57.1 YAMLException: Cannot read config file: /packages/sdk/eslint.config.mjs 2 | packages/sdk/src/modules/promotions/mutations/use-rc-delegation.tsOops! Something went wrong! :( ESLint: 8.57.1 YAMLException: Cannot read config file: /packages/sdk/eslint.config.mjs 2 | packages/sdk/src/modules/promotions/queries/get-rc-delegation-active-query-options.tsOops! Something went wrong! :( ESLint: 8.57.1 YAMLException: Cannot read config file: /packages/sdk/eslint.config.mjs 2 |
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (1)
packages/sdk/src/modules/resource-credits/utils/estimate-rc-precheck.spec.ts (1)
7-17: ⚡ Quick winReplace
anyin test doubles to keep strict typing effective.Lines 7, 15, and 17 use
any, which bypasses type checks and can hide contract changes inestimateRcPrecheckinputs.As per coding guidelines,
**/*.{ts,tsx}requires strict-mode-safe typing for all new TypeScript code.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/sdk/src/modules/resource-credits/utils/estimate-rc-precheck.spec.ts` around lines 7 - 17, Replace the `any` type assertions in the test doubles to enforce strict typing and catch contract changes in estimateRcPrecheck inputs. For the calculateRCMana mock function, define a proper return type instead of relying on type inference. For the stats function, replace the `as any` assertion with the actual type that represents the resource credits stats structure. Similarly, for the account function, replace the `as any` assertion with the proper type that represents the account object expected by estimateRcPrecheck. This ensures TypeScript's type checker can validate that the test doubles match the actual function contracts.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/config/config.template.ts`:
- Around line 92-94: The rcTopup configuration in the template config file has
enabled set to true, but it should be set to false to align with the actual
config.ts file and the PR's stated intent of having this feature disabled by
default. Change the enabled property within the rcTopup object in visionFeatures
from true to false to prevent the incomplete feature from being accidentally
enabled in environments initialized from this template.
In `@apps/web/src/features/shared/rc-precheck/rc-precheck-banner.tsx`:
- Around line 46-51: In the onTopUp function, the window.open call that opens
the purchase page with _blank target is missing the noopener security attribute,
which exposes window.opener to the destination page and creates a security
vulnerability. Add noopener to the window.open call by including it in the
second parameter string or as part of window features parameter to prevent the
opened page from accessing or manipulating the opener window.
In `@apps/web/src/features/shared/rc-topup/rc-topup-dialog.tsx`:
- Around line 38-41: The select function in the useQuery call with
getRcDelegationPricesQueryOptions is mutating the source array in place by
calling sort directly on the data parameter, which modifies the cached data and
can cause side effects for other consumers. Fix this by creating a shallow copy
of the data array using the spread operator before calling sort, so the original
cached array remains unchanged.
- Line 149: The onChange handler for the duration input field uses
ChangeEvent<any> which violates strict typing requirements. Replace
ChangeEvent<any> with ChangeEvent<HTMLSelectElement> in the onChange callback to
provide proper type safety and maintain consistency with TypeScript strict mode
guidelines. This change should be made in the setDuration onChange handler to
ensure the event type matches the actual HTML element being used.
In `@packages/sdk/src/modules/operations/builders/ecency.ts`:
- Around line 51-53: The validation in the buildRcDelegationOp function for the
duration parameter is incomplete. It currently only checks if the value is
finite using Number.isFinite(duration), but this allows zero, negative numbers,
and decimal values to pass through. Update the validation condition to
additionally verify that duration is a positive integer (greater than zero and a
whole number without decimal places). This will ensure invalid delegation
durations fail at the SDK boundary before serialization.
In
`@packages/sdk/src/modules/promotions/queries/get-rc-delegation-active-query-options.ts`:
- Around line 34-38: In the return statement of the query helper, the condition
only checks that responseData and responseData.expires exist before creating the
return object, but it does not verify that responseData.user is defined. Since
the return type contract specifies user as a string (not optional), add
responseData.user to the conditional check so that the object is only returned
when both user and expires are present in the response, preventing undefined
values from being cast as strings.
In
`@packages/sdk/src/modules/promotions/queries/get-rc-delegation-prices-query-options.ts`:
- Around line 12-33: The queryKey in getRcDelegationPricesQueryOptions is
globally static and doesn't include user identity, causing stale prices to be
cached across user switches since TanStack Query v5 only refetches when the
queryKey changes, not when closure variables like accessToken change. Add the
username or activeUser identifier to the queryKey array alongside the existing
string literals, following the same pattern used in other queries like
getPointsQueryOptions and getRcDelegationActiveQueryOptions in the same
component. Ensure the queryKey changes whenever the user changes to properly
isolate the cache per user.
In `@packages/sdk/src/modules/resource-credits/utils/estimate-rc-precheck.ts`:
- Around line 69-83: The `buffer` parameter in the `estimateRcPrecheck` function
is not validated before being used in the `estimatedCost` calculation, allowing
invalid values like 0, negative numbers, NaN, or Infinity to produce unreliable
estimates. Add validation to ensure `buffer` is a finite positive number before
the calculation on line 82. Insert this validation early in the function, after
the initial checks for `rcAccount` and `rcStats`, and ensure that `buffer` is
greater than 0 and not NaN or Infinity. Either normalize invalid values to the
default value of 1.2 or throw an error to prevent silent failures.
---
Nitpick comments:
In
`@packages/sdk/src/modules/resource-credits/utils/estimate-rc-precheck.spec.ts`:
- Around line 7-17: Replace the `any` type assertions in the test doubles to
enforce strict typing and catch contract changes in estimateRcPrecheck inputs.
For the calculateRCMana mock function, define a proper return type instead of
relying on type inference. For the stats function, replace the `as any`
assertion with the actual type that represents the resource credits stats
structure. Similarly, for the account function, replace the `as any` assertion
with the proper type that represents the account object expected by
estimateRcPrecheck. This ensures TypeScript's type checker can validate that the
test doubles match the actual function contracts.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a5a80827-6165-4f11-9c7a-2a9bbed5c975
⛔ Files ignored due to path filters (7)
packages/sdk/dist/browser/index.d.tsis excluded by!**/dist/**packages/sdk/dist/browser/index.jsis excluded by!**/dist/**packages/sdk/dist/browser/index.js.mapis excluded by!**/dist/**,!**/*.mappackages/sdk/dist/node/index.cjsis excluded by!**/dist/**packages/sdk/dist/node/index.cjs.mapis excluded by!**/dist/**,!**/*.mappackages/sdk/dist/node/index.mjsis excluded by!**/dist/**packages/sdk/dist/node/index.mjs.mapis excluded by!**/dist/**,!**/*.map
📒 Files selected for processing (25)
apps/web/src/api/sdk-mutations/index.tsapps/web/src/api/sdk-mutations/use-rc-delegation-mutation.tsapps/web/src/app/submit/_components/editor-actions.tsxapps/web/src/config/config.template.tsapps/web/src/config/config.tsapps/web/src/features/i18n/locales/en-US.jsonapps/web/src/features/shared/comment/index.tsxapps/web/src/features/shared/entry-vote-btn/entry-vote-dialog.tsxapps/web/src/features/shared/rc-precheck/index.tsapps/web/src/features/shared/rc-precheck/rc-precheck-banner.tsxapps/web/src/features/shared/rc-precheck/use-rc-precheck.tsapps/web/src/features/shared/rc-topup/index.tsapps/web/src/features/shared/rc-topup/rc-topup-dialog.tsxpackages/sdk/src/modules/operations/builders/build-rc-delegation-op.spec.tspackages/sdk/src/modules/operations/builders/ecency.tspackages/sdk/src/modules/operations/builders/index.tspackages/sdk/src/modules/promotions/mutations/index.tspackages/sdk/src/modules/promotions/mutations/use-rc-delegation.tspackages/sdk/src/modules/promotions/queries/get-rc-delegation-active-query-options.tspackages/sdk/src/modules/promotions/queries/get-rc-delegation-prices-query-options.tspackages/sdk/src/modules/promotions/queries/index.tspackages/sdk/src/modules/resource-credits/index.tspackages/sdk/src/modules/resource-credits/utils/estimate-rc-precheck.spec.tspackages/sdk/src/modules/resource-credits/utils/estimate-rc-precheck.tspackages/sdk/src/modules/resource-credits/utils/index.ts
| queryKey: ["promotions", "rc-delegation-prices"], | ||
| queryFn: async () => { | ||
| if (!accessToken) { | ||
| return []; | ||
| } | ||
|
|
||
| const response = await fetch(CONFIG.privateApiHost + "/private-api/rc-delegation-price", { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| body: JSON.stringify({ code: accessToken }), | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error(`Failed to fetch RC delegation prices: ${response.status}`); | ||
| } | ||
|
|
||
| return (await response.json()) as PromotePrice[]; | ||
| }, | ||
| staleTime: Infinity, | ||
| enabled: !!accessToken, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In TanStack Query v5, if queryKey stays the same and staleTime is Infinity, does changing only a queryFn closure variable (like accessToken) trigger an automatic refetch?
💡 Result:
In TanStack Query v5, if the queryKey remains the same and staleTime is set to Infinity, changing a variable captured in your queryFn closure will NOT trigger an automatic refetch [1][2][3]. TanStack Query manages caching and refetching primarily based on the queryKey [1][3]. Because the queryKey is used to uniquely identify and cache the data, TanStack Query only considers the data "outdated" or in need of a re-run when the queryKey changes or when the query is manually invalidated [4][1][2]. If the queryKey is unchanged, TanStack Query treats the cache as fresh (due to staleTime: Infinity) and will not automatically re-execute the query function [4]. To ensure that your query function uses the latest variable (such as an updated accessToken), you must follow the idiomatic pattern of including that variable in the queryKey [1][5][3]: 1. Include the variable in the queryKey array (e.g., ['todos', accessToken]) [1][3]. 2. This ensures the query cache is invalidated and the queryFn is re-run whenever that variable changes [1][6]. If you omit the variable from the queryKey, you are creating a stale closure issue where the query function continues to use the initial value it was created with [5][7]. While you could manually call refetch to execute the function with the latest variable, relying on this is generally discouraged for data-dependent fetching because it bypasses the library's automatic caching mechanisms [5][7][2]. Using the queryKey as a dependency array is the recommended approach to keep your data in sync with your application state [1][3][6].
Citations:
- 1: https://tanstack.com/query/latest/docs/framework/react/guides/query-keys
- 2: How to refetch data from new API? Returns previous value TanStack/query#6502
- 3: https://tanstack.com/query/v5/docs/framework/react/guides/query-keys
- 4: https://tanstack.com/query/v5/docs/framework/react/guides/important-defaults
- 5: refetch vs invalidateQuery: inconsistent behavior of queryFn TanStack/query#5894
- 6: https://tanstack.com/query/v5/docs/eslint/exhaustive-deps
- 7: useQuery not reflecting updated queryFn while refetching after queryKey Invalidation TanStack/query#6734
🏁 Script executed:
cat -n packages/sdk/src/modules/promotions/queries/get-rc-delegation-prices-query-options.tsRepository: ecency/vision-next
Length of output: 1361
🏁 Script executed:
rg "getRcDelegationPricesQueryOptions" --type ts --type tsx -B 2 -A 2Repository: ecency/vision-next
Length of output: 90
🏁 Script executed:
rg "getRcDelegationPricesQueryOptions" -B 2 -A 2Repository: ecency/vision-next
Length of output: 50376
🏁 Script executed:
rg "getRcDelegationPricesQueryOptions" --type ts -B 2 -A 2Repository: ecency/vision-next
Length of output: 20957
🏁 Script executed:
cat -n apps/web/src/features/shared/rc-topup/rc-topup-dialog.tsx | head -100Repository: ecency/vision-next
Length of output: 3871
🏁 Script executed:
grep -n "accessToken\|username\|useAccount\|useUser" apps/web/src/features/shared/rc-topup/rc-topup-dialog.tsx | head -20Repository: ecency/vision-next
Length of output: 339
Auth-scoped fetch is cached under a global key, causing stale prices across user switches.
The queryKey at line 12 (["promotions", "rc-delegation-prices"]) doesn't include user identity, but the queryFn uses accessToken to fetch user-specific prices. With staleTime: Infinity (line 32), switching accounts will reuse the cached result from the previous user instead of refetching.
TanStack Query v5 only refetches when the queryKey changes or the query is manually invalidated—a closure variable change (accessToken) alone does NOT trigger automatic refetch.
Notice that other queries in the same component already follow the correct pattern:
getPointsQueryOptions(activeUser?.username)(line 51)getRcDelegationActiveQueryOptions(activeUser?.username ?? "", accessToken)(line 56)
Include username in the queryKey array to fix the cache isolation:
Suggested fix
-export function getRcDelegationPricesQueryOptions(accessToken: string) {
+export function getRcDelegationPricesQueryOptions(
+ username: string | undefined,
+ accessToken: string
+) {
return queryOptions({
- queryKey: ["promotions", "rc-delegation-prices"],
+ queryKey: ["promotions", "rc-delegation-prices", username],
queryFn: async () => {Update the call site to pass activeUser?.username.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@packages/sdk/src/modules/promotions/queries/get-rc-delegation-prices-query-options.ts`
around lines 12 - 33, The queryKey in getRcDelegationPricesQueryOptions is
globally static and doesn't include user identity, causing stale prices to be
cached across user switches since TanStack Query v5 only refetches when the
queryKey changes, not when closure variables like accessToken change. Add the
username or activeUser identifier to the queryKey array alongside the existing
string literals, following the same pattern used in other queries like
getPointsQueryOptions and getRcDelegationActiveQueryOptions in the same
component. Ensure the queryKey changes whenever the user changes to properly
isolate the cache per user.
- lazy-load the RC top-up dialog so its SDK/mutation chain is not pulled into every comment/editor/vote render (fixes comment.spec build failure) - invalidate the rc-delegation-active query after a successful top-up - disable the buy while the active-top-up check is still loading (avoid duplicate) - copy prices before sorting in the query select (no cache mutation) - harden new-tab open with noopener; type the duration select event - validate buildRcDelegationOp duration (positive integer) and estimateRcPrecheck buffer - guard the rc-delegation-active response user shape - default visionFeatures.rcTopup to disabled in the config template - add the new RC SDK exports to the test mock
Summary
Two related Resource Credits (RC) features for the editor surfaces.
Non-blocking RC pre-check. Before a user publishes, comments, or votes, we now estimate whether their RC is likely too low to broadcast and show an inline warning with a top-up CTA. This replaces the experience of submitting and only then hitting the chain's cryptic "Please wait to transact" failure. The check is a hint, never a gate: the publish/comment/vote action stays fully enabled and the banner renders nothing when the user is logged out, the estimate is not ready, or RC is sufficient.
RC top-up purchase client. Adds the client side of a Points-funded, short-term, RC-only delegation to the user's own account (distinct from Boost+, which delegates Hive Power). When the new
rcTopupflag is enabled the pre-check CTA opens an in-app top-up dialog; until the backend ships, the flag is off and the CTA falls back to the existing Boost+ purchase page. The dialog and pricing are wired up but inert in production until the backend endpoints and the relay account exist.The on-chain delegation itself is broadcast by the Ecency relay account, not by the client. The client only signs a Points-spend custom_json (active authority) that the backend acts on.
Changes
SDK (
@ecency/sdk)estimateRcPrecheck(...)- new pure helper inresource-credits/utils. Given an RC account and the network-widerc_api.get_rc_statsaverages, returns{ ready, willLikelyFail, currentMana, maxMana, avgCost, estimatedCost, deficit, remaining }. Uses a configurable safety buffer (default 1.2x) since on-chain cost varies with load. Returns a non-ready result when inputs are missing, and does not flag failure when the operation's average cost is unknown. ExposesRcPrecheckOperation/RcPrecheckInput/RcPrecheckResulttypes.buildRcDelegationOp(user, duration)- new custom_json op builder (idecency_rc_delegation, active authority, payload{ user, duration }). Throws on missing user or non-finite duration.useRcDelegation(...)- new mutation (inpromotions/mutations) built onuseBroadcastMutation, RC-only counterpart touseBoostPlus. Invalidates the account-full and RC account caches on success so the new RC appears once the relay delegation lands.getRcDelegationPricesQueryOptions(accessToken)- new pricing query (inpromotions/queries) hitting the private API/private-api/rc-delegation-price, reusing the existingPromotePriceshape. Returns an empty list and is disabled without a token.getRcDelegationActiveQueryOptions(username, accessToken)- new query returning the user's current active RC top-up (if any), so the dialog can block a duplicate purchase up front. Hits/private-api/rc-delegation-active.build-rc-delegation-op.spec.ts,estimate-rc-precheck.spec.ts.Web (
@ecency/web)RcPrecheckBanner+useRcPrecheck- newfeatures/shared/rc-precheckmodule. The hook reuses the same RC queries the credits widget already loads (cache hit, no extra fetches) and feedsestimateRcPrecheck. The banner has a full layout and acompactsingle-line variant for tight surfaces; CTA opens the top-up dialog whenrcTopupis enabled, otherwise opens the Boost+ purchase page in a new tab.EditorActions(above the action row on new posts), the comment box, and the vote dialog (compact, hidden on paid-out posts).RcTopupDialog- newfeatures/shared/rc-topupmodule. Two-step Points-spend flow (choose duration, confirm) mirroring the Boost dialog but self-only and RC-scoped, with insufficient-Points validation, a guard that disables the buy when the user already has an active top-up (only one is allowed at a time), and a submit guard against double-broadcast.useRcDelegationMutation- web wrapper inapi/sdk-mutationsbinding the SDK mutation to the active user and web broadcast adapter.visionFeatures.rcTopupflag (default off inconfig.ts).rc-precheck.*andrc-topup.*strings inen-US.json.Flags / dependencies / staged status
visionFeatures.rcTopupis off by default (config.tsshipsenabled: false; the template istrueonly as documentation of the key). With the flag off, the only user-visible change is the pre-check banner, whose CTA points at the existing Boost+ purchase page. The pre-check itself ships on by default and is purely client-side and non-blocking./private-api/rc-delegation-priceand/private-api/rc-delegation-active) and a relay account that watches for theecency_rc_delegationcustom_json and performs the actualdelegate_rc. Keep the flag off until both exist.packages/sdk/dist/**changes are rebuilt package output only.Testing
Summary by CodeRabbit
Release Notes
rcTopupfeature flag (currently disabled).